Initial commit of JS template modifications
authorNick Downing <downing.nick@gmail.com>
Sun, 14 Oct 2018 03:47:16 +0000 (14:47 +1100)
committerNick Downing <nick@ndcode.org>
Tue, 30 Oct 2018 07:54:35 +0000 (18:54 +1100)
env.sh [new file with mode: 0644]
package.json
page.jst [new file with mode: 0644]
src/expression.js
src/lval.js
template.js [new file with mode: 0644]
test.js [new file with mode: 0644]
transform.js [new file with mode: 0644]

diff --git a/env.sh b/env.sh
new file mode 100644 (file)
index 0000000..f3162a7
--- /dev/null
+++ b/env.sh
@@ -0,0 +1 @@
+PATH="`pwd`/node_modules/.bin:$PATH"
index 33bc1fa..4fdf7bf 100644 (file)
@@ -5,7 +5,9 @@
   "main": "dist/acorn.js",
   "module": "dist/acorn.mjs",
   "version": "6.0.2",
-  "engines": {"node": ">=0.4.0"},
+  "engines": {
+    "node": ">=0.4.0"
+  },
   "maintainers": [
     {
       "name": "Marijn Haverbeke",
@@ -30,5 +32,7 @@
   "scripts": {
     "prepare": "cd ..; npm run build:main && npm run build:bin"
   },
-  "bin": {"acorn": "./bin/acorn"}
+  "bin": {
+    "acorn": "./bin/acorn"
+  }
 }
diff --git a/page.jst b/page.jst
new file mode 100644 (file)
index 0000000..c402fa9
--- /dev/null
+++ b/page.jst
@@ -0,0 +1,9 @@
+let val='something'
+return html(lang="en" my-val=val) {
+  head {}
+  body {
+    `hello
+go away
+`
+  }
+}
index 310b393..60d9168 100644 (file)
@@ -292,6 +292,11 @@ pp.parseSubscripts = function(base, startPos, startLoc, noCalls) {
       node.tag = base
       node.quasi = this.parseTemplate({isTagged: true})
       base = this.finishNode(node, "TaggedTemplateExpression")
+    } else if (this.type === tt.braceL) { // Nick
+      let node = this.startNodeAt(startPos, startLoc)
+      node.tag = base
+      node.body = this.parseBlock(false)
+      base = this.finishNode(node, "HTMLExpression")
     } else {
       return base
     }
@@ -807,7 +812,7 @@ pp.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestructur
   let elts = [], first = true
   while (!this.eat(close)) {
     if (!first) {
-      this.expect(tt.comma)
+      this.eat(tt.comma) // Nick this.expect(tt.comma)
       if (allowTrailingComma && this.afterTrailingComma(close)) break
     } else first = false
 
index 17e0faf..847ba36 100644 (file)
@@ -78,6 +78,18 @@ pp.toAssignable = function(node, isBinding, refDestructuringErrors) {
 
     default:
       this.raise(node.start, "Assigning to rvalue")
+
+    case "BinaryExpression": // Nick
+      let temp = node
+      while (
+        temp.type === "BinaryExpression" &&
+        temp.operator === "-" &&
+        temp.right.type === "Identifier"
+      )
+        temp = temp.left
+      if (temp.type !== "Identifier")
+        this.raise(node.start, "Assigning to rvalue")
+      break
     }
   } else if (refDestructuringErrors) this.checkPatternErrors(refDestructuringErrors, true)
   return node
@@ -233,5 +245,17 @@ pp.checkLVal = function(expr, bindingType = BIND_NONE, checkClashes) {
 
   default:
     this.raise(expr.start, (bindingType ? "Binding" : "Assigning to") + " rvalue")
+
+  case "BinaryExpression": // Nick
+    let temp = expr
+    while (
+      temp.type === "BinaryExpression" &&
+      temp.operator === "-" &&
+      temp.right.type === "Identifier"
+    )
+      temp = temp.left
+    if (temp.type !== "Identifier")
+      this.raise(expr.start, (bindingType ? "Binding" : "Assigning to") + " rvalue")
+    break
   }
 }
diff --git a/template.js b/template.js
new file mode 100644 (file)
index 0000000..384ce0e
--- /dev/null
@@ -0,0 +1,237 @@
+let assert = require('assert')
+let transform = require('./transform')
+
+let html_body = (node, st, c) => {
+  let tag, arguments
+  if (node.tag.type === 'CallExpression') {
+    tag = node.tag.callee
+    arguments = node.tag.arguments
+  }
+  else {
+    tag = node.tag
+    arguments = []
+  }
+  assert(tag.type === 'Identifier')
+
+  let prefix = '<' + tag.name, expr = undefined, expr1;
+  for (var i = 0; i < arguments.length; ++i) {
+    let name_expr, value_expr
+    if (arguments[i].type === 'AssignmentExpression') {
+      name_expr = arguments[i].left
+      value_expr = arguments[i].right
+    }
+    else {
+      name_expr = arguments[i]
+      value_expr = undefined
+    }
+
+    let name = ''
+    while (
+      name_expr.type === 'BinaryExpression' &&
+      name_expr.operator === '-' &&
+      name_expr.right.type === 'Identifier'
+    ) {
+      name = '-' + name_expr.right.name + name
+      name_expr = name_expr.left
+    }
+    assert(name_expr.type === 'Identifier')
+    name = name_expr.name + name
+    prefix += ' ' + name
+    if (value_expr !== undefined) {
+      prefix += '="'
+      if (value_expr.type === 'Literal')
+        prefix += value_expr.value
+      else {
+        expr1 = {
+          type: 'Literal',
+          value: prefix
+        }
+        expr = {
+          type: 'BinaryExpression',
+          left: expr === undefined ? expr1 : {
+            type: 'BinaryExpression',
+            left: expr,
+            operator: '+',
+            right: expr1,
+            start: node.start,
+            end: node.end
+          },
+          operator: '+',
+          right: value_expr
+        }
+        prefix = ''
+      }
+      prefix += '"'
+    }
+  }
+  expr1 = {
+    type: 'Literal',
+    value: prefix + '>',
+  }
+  expr = expr === undefined ? expr1 : {
+    type: 'BinaryExpression',
+    left: expr,
+    operator: '+',
+    right: expr1,
+  }
+  let result = [
+    {
+      type: 'ExpressionStatement',
+      expression: {
+        type: 'CallExpression',
+        callee: {
+          type: 'MemberExpression',
+          object: {
+            type: 'Identifier',
+            name: '_html'
+          },
+          property: {
+            type: 'Identifier',
+            name: 'push'
+          },
+          computed: false
+        },
+        arguments: [
+          expr
+        ]
+      }
+    }
+  ].concat(c(node.body, st, 'Statement').body)
+  if (
+    tag.name !== 'br' &&
+    tag.name !== 'img' &&
+    tag.name !== 'input' &&
+    tag.name !== 'link' &&
+    tag.name !== 'meta'
+  )
+    result.push(
+      {
+        type: 'ExpressionStatement',
+        expression: {
+          type: 'CallExpression',
+          callee: {
+            type: 'MemberExpression',
+            object: {
+              type: 'Identifier',
+              name: '_html'
+            },
+            property: {
+              type: 'Identifier',
+              name: 'push'
+            },
+            computed: false
+          },
+          arguments: [
+            {
+              type: 'Literal',
+              value: '</' + tag.name + '>'
+            }
+          ]
+        }
+      }
+    )
+  return result
+}
+
+let visitors = Object.assign({}, transform.visitors)
+let visitors_ExpressionStatement = visitors.ExpressionStatement
+visitors.ExpressionStatement = (node, st, c) =>
+  node.expression.type === 'Literal' ||
+  node.expression.type === 'TemplateLiteral' ||
+  node.expression.type === 'TaggedTemplateLiteral' ?
+  {
+    type: 'ExpressionStatement',
+    expression: {
+      type: 'CallExpression',
+      callee: {
+        type: 'MemberExpression',
+        object: {
+          type: 'Identifier',
+          name: '_html'
+        },
+        property: {
+          type: 'Identifier',
+          name: 'push'
+        },
+        computed: false
+      },
+      arguments: [
+        c(node.expression, st, 'Expression')
+      ]
+    }
+  } :
+  node.expression.type === 'HTMLExpression' ?
+  {
+      type: 'BlockStatement',
+      body: html_body(node.expression, st, c)
+  } :
+  visitors_ExpressionStatement(node, st, c)
+visitors.HTMLExpression = (node, st, c) => {
+  return {
+    type: 'CallExpression',
+    callee: {
+      type: 'ArrowFunctionExpression',
+      id: null,
+      expression: false,
+      generator: false,
+      async: false,
+      params: [],
+      body: {
+        type: 'BlockStatement',
+        body: [
+          {
+            type: 'VariableDeclaration',
+            declarations: [
+              {
+                type: 'VariableDeclarator',
+                id: {
+                  type: 'Identifier',
+                  name: '_html'
+                },
+                init: {
+                  type: 'ArrayExpression',
+                  elements: []
+                }
+              }
+            ],
+            kind: 'let'
+          }
+        ]
+        .concat(html_body(node, st, c))
+        .concat(
+          [
+            {
+              type: 'ReturnStatement',
+              argument: {
+                type: 'CallExpression',
+                callee: {
+                  type: 'MemberExpression',
+                  object: {
+                    type: 'Identifier',
+                    name: '_html'
+                  },
+                  property: {
+                    type: 'Identifier',
+                    name: 'join'
+                  },
+                  computed: false
+                },
+                arguments: [
+                  {
+                    type: 'Literal',
+                    value: '',
+                    raw: '\'\''
+                  }
+                ]
+              }
+            }
+          ]
+        )
+      }
+    },
+    arguments: []
+  }
+}
+
+module.exports = visitors
diff --git a/test.js b/test.js
new file mode 100644 (file)
index 0000000..39c54c2
--- /dev/null
+++ b/test.js
@@ -0,0 +1,21 @@
+let acorn = require('./dist/acorn')
+let assert = require('assert')
+let astring = require('astring')
+let fs = require('fs')
+let template = require('./template')
+let transform = require('./transform')
+
+let page = fs.readFileSync('page.jst', {encoding: 'utf-8'})
+console.log(
+  eval(
+    astring.generate(
+      transform.transform(
+        template,
+        acorn.parse(
+          '(() => {' + page + '})()'
+        )
+      ),
+      {indent: ''}
+    )
+  )
+)
diff --git a/transform.js b/transform.js
new file mode 100644 (file)
index 0000000..761d74d
--- /dev/null
@@ -0,0 +1,281 @@
+let transform = (visitors, node, state) => {
+  let c = (node, st, override) =>
+    visitors[override || node.type](node, st, c)
+  return c(node, state, node.type)
+}
+
+let skipThrough = (node, st, c) => c(node, st)
+let ignore = (node, st, c) => node
+
+// Node walkers.
+let visitors = {}
+
+visitors.Program =
+visitors.BlockStatement = (node, st, c) => {
+  for (let i = 0; i < node.body.length; ++i)
+    node.body[i] = c(node.body[i], st, 'Statement')
+  return node
+}
+visitors.Statement = skipThrough
+visitors.EmptyStatement = ignore
+visitors.ExpressionStatement =
+visitors.ParenthesizedExpression = (node, st, c) => {
+  node.expression = c(node.expression, st, 'Expression')
+  return node
+}
+visitors.IfStatement = (node, st, c) => {
+  node.test = c(node.test, st, 'Expression')
+  node.consequent = c(node.consequent, st, 'Statement')
+  if (node.alternate) node.alternate = c(node.alternate, st, 'Statement')
+  return node
+}
+visitors.LabeledStatement = (node, st, c) => {
+  node.body = c(node.body, st, 'Statement')
+  return node
+}
+visitors.BreakStatement =
+visitors.ContinueStatement = ignore
+visitors.WithStatement = (node, st, c) => {
+  node.object = c(node.object, st, 'Expression')
+  node.body = c(node.body, st, 'Statement')
+  return node
+}
+visitors.SwitchStatement = (node, st, c) => {
+  node.discriminant = c(node.discriminant, st, 'Expression')
+  for (let cs of node.cases) {
+    if (cs.test) cs.test = c(cs.test, st, 'Expression')
+    for (let i = 0; i < cs.consequent.length; ++i)
+      cs.consequent[i] = c(cs.consequent[i], st, 'Statement')
+  }
+  return node
+}
+visitors.SwitchCase = (node, st, c) => {
+  if (node.test) node.test = c(node.test, st, 'Expression')
+  for (let i = 0; i < node.consequent.length; ++i)
+    node.consequent[i] = c(node.consequent[i], st, 'Statement')
+  return node
+}
+visitors.ReturnStatement =
+visitors.YieldExpression =
+visitors.AwaitExpression = (node, st, c) => {
+  if (node.argument) node.argument = c(node.argument, st, 'Expression')
+  return node
+}
+visitors.ThrowStatement =
+visitors.SpreadElement = (node, st, c) =>
+  node.argument = c(node.argument, st, 'Expression')
+visitors.TryStatement = (node, st, c) => {
+  node.block = c(node.block, st, 'Statement')
+  if (node.handler) node.handler = c(node.handler, st)
+  if (node.finalizer) node.finalizer = c(node.finalizer, st, 'Statement')
+  return node
+}
+visitors.CatchClause = (node, st, c) => {
+  if (node.param) node.param = c(node.param, st, 'Pattern')
+  node.body = c(node.body, st, 'Statement')
+  return node
+}
+visitors.WhileStatement =
+visitors.DoWhileStatement = (node, st, c) => {
+  node.test = c(node.test, st, 'Expression')
+  node.body = c(node.body, st, 'Statement')
+  return node
+}
+visitors.ForStatement = (node, st, c) => {
+  if (node.init) node.init = c(node.init, st, 'ForInit')
+  if (node.test) node.test = c(node.test, st, 'Expression')
+  if (node.update) node.update = c(node.update, st, 'Expression')
+  node.body = c(node.body, st, 'Statement')
+  return node
+}
+visitors.ForInStatement =
+visitors.ForOfStatement = (node, st, c) => {
+  node.left = c(node.left, st, 'ForInit')
+  node.right = c(node.right, st, 'Expression')
+  node.body = c(node.body, st, 'Statement')
+  return node
+}
+visitors.ForInit = (node, st, c) => {
+  if (node.type === 'VariableDeclaration') return c(node, st)
+  else return c(node, st, 'Expression')
+  return node
+}
+visitors.DebuggerStatement = ignore
+
+visitors.FunctionDeclaration = (node, st, c) => c(node, st, 'Function')
+visitors.VariableDeclaration = (node, st, c) => {
+  for (let i = 0; i < node.declarations.length; ++i)
+    node.declarations[i] = c(node.declarations[i], st)
+  return node
+}
+visitors.VariableDeclarator = (node, st, c) => {
+  node.id = c(node.id, st, 'Pattern')
+  if (node.init) node.init = c(node.init, st, 'Expression')
+  return node
+}
+
+visitors.Function = (node, st, c) => {
+  if (node.id) node.id = c(node.id, st, 'Pattern')
+  for (let i = 0; i < node.params.length; ++i)
+    node.params[i] = c(node.params[i], st, 'Pattern')
+  node.body = c(node.body, st, node.expression ? 'Expression' : 'Statement')
+  return node
+}
+
+visitors.Pattern = (node, st, c) => {
+  if (node.type === 'Identifier')
+    return c(node, st, 'VariablePattern')
+  else if (node.type === 'MemberExpression')
+    return c(node, st, 'MemberPattern')
+  else
+    return c(node, st)
+  return node
+}
+visitors.VariablePattern = ignore
+visitors.MemberPattern = skipThrough
+visitors.RestElement = (node, st, c) =>
+  node.argument = c(node.argument, st, 'Pattern')
+visitors.ArrayPattern = (node, st, c) => {
+  for (let i = 0; i < node.elements.length; ++i) {
+    let elt = node.elements[i]
+    if (elt) node.elements[i] = c(elt, st, 'Pattern')
+  }
+  return node
+}
+visitors.ObjectPattern = (node, st, c) => {
+  for (let prop of node.properties) {
+    if (prop.type === 'Property') {
+      if (prop.computed) prop.key = c(prop.key, st, 'Expression')
+      prop.value = c(prop.value, st, 'Pattern')
+    } else if (prop.type === 'RestElement') {
+      prop.argument = c(prop.argument, st, 'Pattern')
+    }
+  }
+  return node
+}
+
+visitors.Expression = skipThrough
+visitors.ThisExpression =
+visitors.Super =
+visitors.MetaProperty = ignore
+visitors.ArrayExpression = (node, st, c) => {
+  for (let i = 0; i < node.elements.length; ++i) {
+    let elt = node.elements[i]
+    if (elt) node.elements[i] = c(elt, st, 'Expression')
+  }
+  return node
+}
+visitors.ObjectExpression = (node, st, c) => {
+  for (let i = 0; i < node.properties.length; ++i)
+    node.properties[i] = c(node.properties[i], st)
+  return node
+}
+visitors.FunctionExpression =
+visitors.ArrowFunctionExpression = visitors.FunctionDeclaration
+visitors.SequenceExpression = (node, st, c) => {
+  for (let i = 0; i < node.expressions.length; ++i)
+    node.expressions[i] = c(node.expressions[i], st, 'Expression')
+  return node
+}
+visitors.TemplateLiteral = (node, st, c) => {
+  for (let i = 0; i < node.quasis.length; ++i)
+    node.quasis[i] = c(node.quasis[i], st)
+
+  for (let i = 0; i < node.expressions.length; ++i)
+    node.expressions[i] = c(node.expressions[i], st, 'Expression')
+  return node
+}
+visitors.TemplateElement = ignore
+visitors.UnaryExpression =
+visitors.UpdateExpression = (node, st, c) => {
+  node.argument = c(node.argument, st, 'Expression')
+  return node
+}
+visitors.BinaryExpression =
+visitors.LogicalExpression = (node, st, c) => {
+  node.left = c(node.left, st, 'Expression')
+  node.right = c(node.right, st, 'Expression')
+  return node
+}
+visitors.AssignmentExpression =
+visitors.AssignmentPattern = (node, st, c) => {
+  node.left = c(node.left, st, 'Pattern')
+  node.right = c(node.right, st, 'Expression')
+  return node
+}
+visitors.ConditionalExpression = (node, st, c) => {
+  node.test = c(node.test, st, 'Expression')
+  node.consequent = c(node.consequent, st, 'Expression')
+  node.alternate = c(node.alternate, st, 'Expression')
+  return node
+}
+visitors.NewExpression =
+visitors.CallExpression = (node, st, c) => {
+  node.callee = c(node.callee, st, 'Expression')
+  if (node.arguments)
+    for (let i = 0; i < node.arguments.length; ++i)
+      node.arguments[i] = c(node.arguments[i], st, 'Expression')
+  return node
+}
+visitors.MemberExpression = (node, st, c) => {
+  node.object = c(node.object, st, 'Expression')
+  if (node.computed) node.property = c(node.property, st, 'Expression')
+  return node
+}
+visitors.ExportNamedDeclaration =
+visitors.ExportDefaultDeclaration = (node, st, c) => {
+  if (node.declaration)
+    node.declaration = c(node.declaration, st, node.type === 'ExportNamedDeclaration' || node.declaration.id ? 'Statement' : 'Expression')
+  if (node.source) node.source = c(node.source, st, 'Expression')
+  return node
+}
+visitors.ExportAllDeclaration = (node, st, c) => {
+  node.source = c(node.source, st, 'Expression')
+  return node
+}
+visitors.ImportDeclaration = (node, st, c) => {
+  for (let i = 0; i < node.specifiers.length; ++i)
+    node.specifiers[i] = c(node.specifiers[i], st)
+  node.source = c(node.source, st, 'Expression')
+  return node
+}
+visitors.ImportSpecifier =
+visitors.ImportDefaultSpecifier =
+visitors.ImportNamespaceSpecifier =
+visitors.Identifier =
+visitors.Literal = ignore
+
+visitors.TaggedTemplateExpression = (node, st, c) => {
+  node.tag = c(node.tag, st, 'Expression')
+  node.quasi = c(node.quasi, st, 'Expression')
+  return node
+}
+visitors.ClassDeclaration =
+visitors.ClassExpression = (node, st, c) => c(node, st, 'Class')
+visitors.Class = (node, st, c) => {
+  if (node.id) node.id = c(node.id, st, 'Pattern')
+  if (node.superClass) node.superClass = c(node.superClass, st, 'Expression')
+  node.body = c(node.body, st)
+  return node
+}
+visitors.ClassBody = (node, st, c) => {
+  for (let i = 0; i < node.body.length; ++i)
+    node.body[i] = c(node.body[i], st)
+  return node
+}
+visitors.MethodDefinition =
+visitors.Property = (node, st, c) => {
+  if (node.computed) node.key = c(node.key, st, 'Expression')
+  node.value = c(node.value, st, 'Expression')
+  return node
+}
+
+// Nick
+visitors.HTMLExpression = (node, st, c) => {
+  node.tag = c(node.tag, st, 'Expression')
+  node.body = c(node.body, st, 'Statement')
+  return node
+}
+
+exports.transform = transform
+exports.visitors = visitors