diff --git a/src/Stone/Parser.js b/src/Stone/Parser.js index 28c4a17..014eb66 100644 --- a/src/Stone/Parser.js +++ b/src/Stone/Parser.js @@ -3,6 +3,8 @@ import './Parsers' import './Contexts/DirectiveArgs' import './Contexts/PreserveSpace' +import './Support/MakeNode' + import './Tokens/StoneOutput' import './Tokens/StoneDirective' @@ -20,6 +22,7 @@ export class Parser { template.type = 'StoneTemplate' template.pathname = this._stoneTemplatePathname this._stoneTemplate = template + this.make = new MakeNode(this) const node = this.startNode() this.nextToken() @@ -28,7 +31,7 @@ export class Parser { template.output = new acorn.Node(this) template.output.type = 'StoneOutputBlock' - template.output.body = this._createBlockStatement(result.body) + template.output.body = this.make.block(result.body) result.body = [ template ] @@ -50,10 +53,6 @@ export class Parser { return next.call(this, type) } - skipSpace(next, ...args) { - return next.call(this, ...args) - } - readToken(next, code) { if(code === 64 && !this._isCharCode(123, 1)) { this.pos++ @@ -92,7 +91,7 @@ export class Parser { // we can leverage the built in acorn functionality // for parsing things like for loops without it // trying to parse the block - return this._createBlockStatement([ ]) + return this.make.block([ ]) } switch(this.type) { @@ -261,72 +260,6 @@ export class Parser { return [ args ] } - _createIdentifier(identifier) { - const node = new acorn.Node(this) - node.type = 'Identifier' - node.name = identifier - return node - } - - _maybeCreateIdentifier(name) { - if(typeof name !== 'string') { - return name - } - - return this._createIdentifier(name) - } - - _createBlockStatement(statements) { - const node = new acorn.Node(this) - node.type = 'BlockStatement' - node.body = statements - return node - } - - _createEmptyNode() { - const node = new acorn.Node(this) - node.type = 'BlankExpression' - return node - } - - _createAssignment(left, right, operator = '=') { - const node = this.startNodeAt(this.start, this.startLoc) - node.operator = operator - node.left = this._maybeCreateIdentifier(left) - node.right = right - - return this.finishNode(node, 'AssignmentExpression') - } - - _createDeclaration(lhs, rhs, kind = 'const') { - const declarator = this.startNode() - declarator.id = this._maybeCreateIdentifier(lhs) - declarator.init = rhs - this.finishNode(declarator, 'VariableDeclarator') - - const declaration = this.startNode() - declaration.declarations = [ declarator ] - declaration.kind = kind - return this.finishNode(declaration, 'VariableDeclaration') - } - - _createLiteral(value) { - const node = this.startNode() - node.value = value - node.raw = value - return this.finishNode(node, 'Literal') - } - - _createReturn(value) { - const declarator = this.startNode() - - if(value) { - declarator.argument = this._maybeCreateIdentifier(value) - } - - return this.finishNode(declarator, 'ReturnStatement') - } - _debug(message = 'DEBUG', peek = false) { let debug = { start: this.start, diff --git a/src/Stone/Support/MakeNode.js b/src/Stone/Support/MakeNode.js new file mode 100644 index 0000000..c133a3a --- /dev/null +++ b/src/Stone/Support/MakeNode.js @@ -0,0 +1,114 @@ +import './MockParser' + +export class MakeNode { + + constructor(parser = null) { + this.parser = parser || new MockParser + } + + identifier(identifier) { + const node = this.parser.startNode() + node.name = identifier + return this.parser.finishNode(node, 'Identifier') + } + + auto(type) { + if(typeof type !== 'string') { + return type + } + + return this.identifier(type, this.parser) + } + + new(callee, args) { + const node = this.parser.startNode() + node.type = 'NewExpression' + node.callee = this.auto(callee) + node.arguments = Array.isArray(args) ? args.map(arg => this.auto(arg)) : [ this.auto(args) ] + return this.parser.finishNode(node, 'NewExpression') + } + + object(properties) { + const node = this.parser.startNode() + node.type = 'ObjectExpression' + node.properties = properties || [ ] + return this.parser.finishNode(node, 'ObjectExpression') + } + + property(key, value) { + const node = this.parser.startNode() + node.key = this.auto(key) + + if(!value.isNil) { + node.value = this.auto(value) + node.kind = 'init' + } + + return this.parser.finishNode(node, 'Property') + } + + spread(type) { + const node = this.parser.startNode() + node.argument = this.auto(type, this.parser) + return this.parser.finishNode(node, 'SpreadElement') + } + + assignment(left, right, operator = '=') { + const node = this.parser.startNode() + node.operator = operator + node.left = this.auto(left) + node.right = this.auto(right) + return this.parser.finishNode(node, 'AssignmentExpression') + } + + declaration(left, right, kind = 'const') { + const declarator = this.parser.startNode() + declarator.id = this.auto(left) + declarator.init = this.auto(right) + this.parser.finishNode(declarator, 'VariableDeclarator') + + const declaration = this.parser.startNode() + declaration.declarations = [ declarator ] + declaration.kind = kind + return this.parser.finishNode(declaration, 'VariableDeclaration') + } + + literal(value) { + const node = this.parser.startNode() + node.value = value + node.raw = value + return this.parser.finishNode(node, 'Literal') + } + + return(value) { + const node = this.parser.startNode() + + if(!value.isNil) { + node.argument = this.auto(value) + } + + return this.parser.finishNode(node, 'ReturnStatement') + } + + block(statements) { + const node = this.parser.startNode() + node.body = statements + return this.parser.finishNode(node, 'BlockStatement') + } + + + null() { + const node = this.parser.startNode() + node.type = 'Literal' + node.value = null + node.raw = 'null' + return this.parser.finishNode(node, 'Literal') + } + + empty() { + return this.parser.finishNode(this.parser.startNode(), 'StoneEmptyExpression') + } + +} + +export const make = new MakeNode diff --git a/src/Stone/Support/MockParser.js b/src/Stone/Support/MockParser.js new file mode 100644 index 0000000..2a9c453 --- /dev/null +++ b/src/Stone/Support/MockParser.js @@ -0,0 +1,29 @@ +const { Node } = require('acorn') + +export class MockParser { + + options = { } + + startNode() { + return new Node(this) + } + + startNodeAt(pos, loc) { + return new Node(this, pos, loc) + } + + finishNode(node, type) { + return this.finishNodeAt(node, type) + } + + finishNodeAt(node, type, pos) { + node.type = type + + if(!pos.isNil) { + node.end = pos + } + + return node + } + +}