Skip to content

Commit

Permalink
Drop v8 vm for native implementation, update tests accordingly 🚀.
Browse files Browse the repository at this point in the history
- Implements and closes #1 by @edmulraney.
- Better performance & browser support.
  • Loading branch information
yamafaktory committed Feb 2, 2017
1 parent 681aa09 commit a91e08c
Showing 1 changed file with 21 additions and 54 deletions.
75 changes: 21 additions & 54 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,75 +1,42 @@
const vm = require('vm')

module.exports = class Guards {
class Guards {
constructor () {
this.guardRegex = /[^=<>!']=[^=]/

this.templateRegex = /[^|]\|[^|]/

this.optionsVM = {
displayErrors: true,
filename: 'guards'
}
}

getStack () {
const origin = Error.prepareStackTrace
const error = new Error()

Error.prepareStackTrace = (_, stack) => stack

Error.captureStackTrace(error, this.getStack)

const stack = error.stack

Error.prepareStackTrace = origin

// V8 stack traces.
return stack
buildPredicate (constants, guard) {
return new Function(
'',
[
`const { ${this.destructureProps(constants)} } = ${JSON.stringify(constants)}`,
`return ${guard}`
].join(';')
)
}

equal (constants) {
return template => {
const guards = this.parse(template.raw[0])
const lineOffset = this.getStack()[1].getLineNumber()
const firstTruthyGuard = (
guards.map(
g => this.runInVM(g.eval, constants, lineOffset)
)
).findIndex(a => a === true)

if (firstTruthyGuard === -1) this.error(`Non-exhaustive patterns in guards at line ${lineOffset}!`)

// First truthy guard is returned, like in Haskell.
return guards[firstTruthyGuard].result
}
destructureProps (constants) {
return Object.keys(constants).join(',')
}

error (e) {
console.error(e)

process.exit(1)
}
equal (constants) {
const self = this

parse (template) {
// Inline guards need filtering.
return template
return template => template.raw[0]
.split(this.templateRegex)
.filter(g => g.trim() !== '')
.map((g, i) => {
// Remove break line and extract the guard.
const parts = g.trim().split(this.guardRegex)

return {
eval: parts[0],
result: JSON.parse(`${parts[1].trim().replace(/'/g, '"')}`)
}
return [
parts[0],
JSON.parse(`${parts[1].trim().replace(/'/g, '"')}`)
]
})
}

runInVM (code, sandbox, lineOffset) {
const options = Object.assign({}, this.optionsVM, { lineOffset: lineOffset })

return vm.runInNewContext(code, sandbox, options)
.find(g => self.buildPredicate(constants, g[0])())[1]
}
}

module.exports = (c, t) => (new Guards()).equal(c, t)

0 comments on commit a91e08c

Please sign in to comment.