@litexa/core
Version:
Litexa, a programming language for writing Alexa skills
143 lines (115 loc) • 4.54 kB
text/coffeescript
###
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
###
fs = require 'fs'
path = require 'path'
mkdirp = require 'mkdirp'
peg = require 'pegjs'
crypto = require 'crypto'
debug = require('debug')('litexa')
sourceFilename = "#{__dirname}/litexa.pegjs"
cacheFilename = "#{__dirname}/litexa.pegjs.cached"
parserSourceCode = null
try
hash = crypto.createHash('md5')
sourcePEG = fs.readFileSync sourceFilename, 'utf8'
hash.update(sourcePEG)
sourceHash = hash.digest('hex')
cached = JSON.parse(fs.readFileSync(cacheFilename, 'utf8'))
if cached.hash == sourceHash
parserSourceCode = cached.source
debug "cached parser loaded"
else
debug "cached parser was state #{sourceHash} != #{cached.hash}"
catch e
debug "no cached parser found"
debug e
unless parserSourceCode
source = fs.readFileSync(sourceFilename, 'utf8')
parserSourceCode = peg.generate source, { cache: true, output: 'source', format: 'bare', allowedStartRules:['start', 'TestStatements'] }
cached =
hash: sourceHash
source: parserSourceCode
fs.writeFileSync cacheFilename, JSON.stringify(cached, null, 2), 'utf8'
try
parser = eval(parserSourceCode)
catch e
debug "failed to eval parser"
throw e
module.exports.parser = parser
module.exports.sourceCode = parserSourceCode
module.exports.buildExtendedParser = (projectInfo) ->
enableParserCache = false
if projectInfo.isMock
enableParserCache = false
# try to load the cache
if enableParserCache
tempdir = path.join projectInfo.root, '.deploy'
mkdirp.sync tempdir
projectSourceCacheFilename = path.join tempdir, 'extended-litexa.pegjs'
projectCodeCacheFilename = path.join tempdir, 'extended-litexa.pegjs.cached'
try
extParserSource = fs.readFileSync projectCodeCacheFilename, 'utf8'
extParser = eval extParserSource
return extParser
# interpolate in any extensions
extSourcePEG = "" + sourcePEG
statementNames = []
testStatementNames = []
intentStatementNames = []
for extensionName, extension of projectInfo.extensions
continue unless extension.language?
for statementName, statement of extension.language.statements
statementNames.push statementName
unless statement.parser?
throw new Error "Statement #{statementName} in extension
#{extensionName} is missing parser code"
extSourcePEG += "\n#{statement.parser}\n"
for statementName, statement of extension.language.testStatements
testStatementNames.push statementName
unless statement.parser?
throw new Error "Test statement #{statementName} in extension #{extensionName} is missing parser code"
extSourcePEG += "\n#{statement.parser}\n"
replacePlaceholder = (placeholder, nameList) ->
block = for s in nameList
" / " + s
extSourcePEG = extSourcePEG.replace placeholder, block.join '\n'
replacePlaceholder " /* ADDITIONAL STATEMENTS */", statementNames
replacePlaceholder " /* ADDITIONAL TEST STATEMENTS */", testStatementNames
replacePlaceholder " /* ADDITIONAL INTENT STATEMENTS */", intentStatementNames
# save the combined parser input for later
if enableParserCache
fs.writeFileSync projectSourceCacheFilename, extSourcePEG, 'utf8'
extParserSource = peg.generate extSourcePEG, { cache: true, output: 'source', format: 'bare', allowedStartRules:['start', 'TestStatements', 'AllFileExclusions'] }
try
extParser = eval extParserSource
catch err
debug "failed to eval extended parser"
throw e
# success! Recycle the generated parser for later
if enableParserCache
fs.writeFileSync projectCodeCacheFilename, extParserSource, 'utf8'
return extParser
fragmentParser = null
module.exports.parseFragment = (fragment, language) ->
unless fragmentParser?
source = fs.readFileSync sourceFilename, 'utf8'
fragmentParserSource = peg.generate source,
cache: true
output: 'source'
format: 'bare'
allowedStartRules:['Fragment']
fragmentParser = eval(fragmentParserSource)
result = null
skill =
pushCode: (thing) -> result = thing
getExtensions: -> {}
returnValue = fragmentParser.parse fragment,
skill: skill
lib: require './parserlib.coffee'
language: language ? 'default'
result = returnValue unless result?
return result