Compare commits
2 Commits
b9fc50c0d2
...
f85ab90be1
Author | SHA1 | Date |
---|---|---|
Sven Slootweg | f85ab90be1 | 4 years ago |
Sven Slootweg | 98a4bcb6d8 | 4 years ago |
@ -1,249 +0,0 @@
|
|||||||
{
|
|
||||||
const matchValue = require("match-value");
|
|
||||||
const syncpipe = require("syncpipe");
|
|
||||||
|
|
||||||
const {B} = require("../unit-bytes-iec");
|
|
||||||
const mapAttributeFlags = require("./map-attribute-flags");
|
|
||||||
}
|
|
||||||
|
|
||||||
RootInfo
|
|
||||||
= header:Header infoSection:InfoSection Newline* {
|
|
||||||
return { ...header, fields: infoSection }
|
|
||||||
};
|
|
||||||
|
|
||||||
RootScan
|
|
||||||
= devices:ScanDevice* {
|
|
||||||
return { devices: devices };
|
|
||||||
}
|
|
||||||
|
|
||||||
RootAttributes
|
|
||||||
= header:Header attributesSection:AttributesSection Newline* {
|
|
||||||
return { ...header, attributes: attributesSection }
|
|
||||||
};
|
|
||||||
|
|
||||||
_
|
|
||||||
= (" " / "\t")*
|
|
||||||
|
|
||||||
RestOfLine
|
|
||||||
= content:$[^\n]+ Newline {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
Newline
|
|
||||||
= "\n"
|
|
||||||
/ "\r\n"
|
|
||||||
|
|
||||||
Header 'header'
|
|
||||||
= "smartctl " versionString:RestOfLine "Copyright" copyrightStatement:RestOfLine Newline {
|
|
||||||
return { versionString, copyrightStatement };
|
|
||||||
}
|
|
||||||
|
|
||||||
BytesValue
|
|
||||||
= value:SeparatedNumberValue {
|
|
||||||
return B(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberValue
|
|
||||||
= value:$[0-9]+ {
|
|
||||||
return parseInt(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
SeparatedNumberValue
|
|
||||||
= value:$[0-9,]+ {
|
|
||||||
return syncpipe(value, [
|
|
||||||
(_) => _.replace(/,/g, ""),
|
|
||||||
(_) => parseInt(_)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
HexNumberValue
|
|
||||||
= value:$[0-9A-Fa-f]+ {
|
|
||||||
return parseInt(value, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentifierValue
|
|
||||||
= value:$[a-zA-Z_-]+ {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// smartctl --scan
|
|
||||||
|
|
||||||
ScanDevice 'scanned device'
|
|
||||||
= path:$[^ ]+ _ "-d" _ interface_:$[^ ]+ _ RestOfLine {
|
|
||||||
return { path: path, interface: interface_ };
|
|
||||||
}
|
|
||||||
|
|
||||||
// smartctl --info
|
|
||||||
|
|
||||||
InfoSection 'information section'
|
|
||||||
= "=== START OF INFORMATION SECTION ===" Newline fields:(InfoField+) {
|
|
||||||
return fields.filter((field) => field != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoField 'information field'
|
|
||||||
= InfoFieldSimple
|
|
||||||
/ InfoFieldIgnored
|
|
||||||
/ InfoFieldSize
|
|
||||||
/ InfoFieldRPM
|
|
||||||
/ InfoFieldSectorSizes
|
|
||||||
/ InfoFieldBoolean
|
|
||||||
/ InfoFieldUnknown
|
|
||||||
|
|
||||||
InfoFieldSimpleKey
|
|
||||||
= "Device Model" { return "model"; }
|
|
||||||
/ "Model Number" { return "model"; }
|
|
||||||
/ "Model Family" { return "modelFamily"; }
|
|
||||||
/ "Serial Number" { return "serialNumber"; }
|
|
||||||
/ "LU WWN Device Id" { return "wwn"; }
|
|
||||||
/ "Firmware Version" { return "firmwareVersion"; }
|
|
||||||
/ "Form Factor" { return "formFactor"; }
|
|
||||||
/ "ATA Version is" { return "ataVersion"; }
|
|
||||||
/ "SATA Version is" { return "sataVersion"; }
|
|
||||||
|
|
||||||
InfoFieldSimple
|
|
||||||
= key:InfoFieldSimpleKey ":" _ value:RestOfLine {
|
|
||||||
return { key: key, value: value };
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoFieldUnknown
|
|
||||||
= key:$[^:]+ ":" _ RestOfLine {
|
|
||||||
console.warn(`Encountered unrecognized SMART info key: ${key}`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoFieldIgnoredKey
|
|
||||||
= "Device is"
|
|
||||||
/ "Local Time is"
|
|
||||||
|
|
||||||
InfoFieldIgnored
|
|
||||||
= key:InfoFieldIgnoredKey ":" _ RestOfLine {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/ "SMART support is:" _ ("Available" / "Unavailable") RestOfLine {
|
|
||||||
// We don't actually care about this entry, but have to specify its possible values explicitly, to distinguish it from the entry we *do* care about that (annoyingly) uses the same key; see InfoFieldBoolean
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoFieldSize
|
|
||||||
// NOTE: We don't actually care about the human-friendly display size after the 'bytes' specifier, hence the RestOfLine
|
|
||||||
= InfoFieldSizeKey _ value:SeparatedNumberValue _ "bytes"? _ RestOfLine {
|
|
||||||
return {
|
|
||||||
key: "size",
|
|
||||||
value: B(value)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoFieldSizeKey
|
|
||||||
= "User Capacity:"
|
|
||||||
/ "Total NVM Capacity:"
|
|
||||||
|
|
||||||
InfoFieldRPM
|
|
||||||
= "Rotation Rate:" _ value:NumberValue _ "rpm" Newline {
|
|
||||||
return {
|
|
||||||
key: "rpm",
|
|
||||||
value: value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoFieldSectorSizes
|
|
||||||
= "Sector Sizes:" _ logicalSize:BytesValue _ "bytes logical," _ physicalSize:BytesValue _ "bytes physical" Newline {
|
|
||||||
return {
|
|
||||||
key: "sectorSizes",
|
|
||||||
value: {
|
|
||||||
logical: logicalSize,
|
|
||||||
physical: physicalSize
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoFieldBooleanKey
|
|
||||||
= "SMART support is" { return "smartEnabled"; }
|
|
||||||
|
|
||||||
InfoFieldBoolean
|
|
||||||
= key:InfoFieldBooleanKey ":" _ value:RestOfLine {
|
|
||||||
return {
|
|
||||||
key: key,
|
|
||||||
value: matchValue(value, {
|
|
||||||
Enabled: true,
|
|
||||||
Disabled: false
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// smartctl --attributes
|
|
||||||
|
|
||||||
AttributesSection
|
|
||||||
= AttributesSectionSATA
|
|
||||||
/ AttributesSectionNVMe
|
|
||||||
|
|
||||||
AttributesSectionSATA
|
|
||||||
= "=== START OF READ SMART DATA SECTION ===" Newline
|
|
||||||
"SMART Attributes Data Structure revision number:" _ NumberValue Newline
|
|
||||||
"Vendor Specific SMART Attributes with Thresholds:" Newline
|
|
||||||
"ID#" _ "ATTRIBUTE_NAME" _ "FLAG" _ "VALUE" _ "WORST" _ "THRESH" _ "TYPE" _ "UPDATED" _ "WHEN_FAILED" _ "RAW_VALUE" Newline
|
|
||||||
attributes:AttributeFieldSATA+ {
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
AttributesSectionNVMe
|
|
||||||
= "=== START OF SMART DATA SECTION ===" Newline
|
|
||||||
"SMART/Health Information (NVMe Log 0x02)" Newline
|
|
||||||
attributes:AttributeFieldNVMe+ {
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
AttributeFlags
|
|
||||||
= "0x" number:HexNumberValue {
|
|
||||||
return mapAttributeFlags(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
AttributeUpdatedWhen
|
|
||||||
= "Always"
|
|
||||||
/ "Offline"
|
|
||||||
|
|
||||||
AttributeFailedWhen
|
|
||||||
= "FAILING_NOW"
|
|
||||||
/ "In_the_past"
|
|
||||||
/ "-"
|
|
||||||
|
|
||||||
AttributeFieldType
|
|
||||||
= "Pre-fail"
|
|
||||||
/ "Old_age"
|
|
||||||
|
|
||||||
AttributeFieldSATA
|
|
||||||
= _ id:NumberValue
|
|
||||||
_ attributeName:IdentifierValue
|
|
||||||
_ flags:AttributeFlags
|
|
||||||
_ value:NumberValue
|
|
||||||
_ worstValue:NumberValue
|
|
||||||
_ threshold:NumberValue
|
|
||||||
_ type:AttributeFieldType
|
|
||||||
_ updatedWhen:AttributeUpdatedWhen
|
|
||||||
_ failedWhen:AttributeFailedWhen
|
|
||||||
_ rawValue:RestOfLine {
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
attributeName,
|
|
||||||
flags,
|
|
||||||
value,
|
|
||||||
worstValue,
|
|
||||||
threshold,
|
|
||||||
rawValue,
|
|
||||||
updatedWhen: matchValue(updatedWhen, {
|
|
||||||
"Always": "always",
|
|
||||||
"Offline": "offline"
|
|
||||||
}),
|
|
||||||
type: matchValue(type, {
|
|
||||||
"Pre-fail": "preFail",
|
|
||||||
"Old_age": "oldAge"
|
|
||||||
}),
|
|
||||||
failingNow: (failedWhen === "FAILING_NOW"),
|
|
||||||
/* TODO: Should the below include the FAILING_NOW state? */
|
|
||||||
failedBefore: (failedWhen === "In_the_past")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
AttributeFieldNVMe
|
|
||||||
= label:$[^:]+ ":" _ value:RestOfLine {
|
|
||||||
return { label: label, value };
|
|
||||||
}
|
|
@ -0,0 +1,88 @@
|
|||||||
|
import { _, RestOfLine, Newline, NumberValue, HexNumberValue, IdentifierValue } from "../primitives"
|
||||||
|
import { Header } from "../shared"
|
||||||
|
|
||||||
|
{
|
||||||
|
const matchValue = require("match-value");
|
||||||
|
const mapAttributeFlags = require("../../map-attribute-flags");
|
||||||
|
}
|
||||||
|
|
||||||
|
RootAttributes
|
||||||
|
= header:Header attributesSection:AttributesSection Newline* {
|
||||||
|
return { ...header, attributes: attributesSection }
|
||||||
|
};
|
||||||
|
|
||||||
|
AttributesSection
|
||||||
|
= AttributesSectionSATA
|
||||||
|
/ AttributesSectionNVMe
|
||||||
|
|
||||||
|
AttributesSectionSATA
|
||||||
|
= "=== START OF READ SMART DATA SECTION ===" Newline
|
||||||
|
"SMART Attributes Data Structure revision number:" _ NumberValue Newline
|
||||||
|
"Vendor Specific SMART Attributes with Thresholds:" Newline
|
||||||
|
"ID#" _ "ATTRIBUTE_NAME" _ "FLAG" _ "VALUE" _ "WORST" _ "THRESH" _ "TYPE" _ "UPDATED" _ "WHEN_FAILED" _ "RAW_VALUE" Newline
|
||||||
|
attributes:AttributeFieldSATA+ {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributesSectionNVMe
|
||||||
|
= "=== START OF SMART DATA SECTION ===" Newline
|
||||||
|
"SMART/Health Information (NVMe Log 0x02)" Newline
|
||||||
|
attributes:AttributeFieldNVMe+ {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeFlags
|
||||||
|
= "0x" number:HexNumberValue {
|
||||||
|
return mapAttributeFlags(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeUpdatedWhen
|
||||||
|
= "Always"
|
||||||
|
/ "Offline"
|
||||||
|
|
||||||
|
AttributeFailedWhen
|
||||||
|
= "FAILING_NOW"
|
||||||
|
/ "In_the_past"
|
||||||
|
/ "-"
|
||||||
|
|
||||||
|
AttributeFieldType
|
||||||
|
= "Pre-fail"
|
||||||
|
/ "Old_age"
|
||||||
|
|
||||||
|
AttributeFieldSATA
|
||||||
|
= _ id:NumberValue
|
||||||
|
_ attributeName:IdentifierValue
|
||||||
|
_ flags:AttributeFlags
|
||||||
|
_ value:NumberValue
|
||||||
|
_ worstValue:NumberValue
|
||||||
|
_ threshold:NumberValue
|
||||||
|
_ type:AttributeFieldType
|
||||||
|
_ updatedWhen:AttributeUpdatedWhen
|
||||||
|
_ failedWhen:AttributeFailedWhen
|
||||||
|
_ rawValue:RestOfLine {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
attributeName,
|
||||||
|
flags,
|
||||||
|
value,
|
||||||
|
worstValue,
|
||||||
|
threshold,
|
||||||
|
rawValue,
|
||||||
|
updatedWhen: matchValue(updatedWhen, {
|
||||||
|
"Always": "always",
|
||||||
|
"Offline": "offline"
|
||||||
|
}),
|
||||||
|
type: matchValue(type, {
|
||||||
|
"Pre-fail": "preFail",
|
||||||
|
"Old_age": "oldAge"
|
||||||
|
}),
|
||||||
|
failingNow: (failedWhen === "FAILING_NOW"),
|
||||||
|
/* TODO: Should the below include the FAILING_NOW state? */
|
||||||
|
failedBefore: (failedWhen === "In_the_past")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeFieldNVMe
|
||||||
|
= label:$[^:]+ ":" _ value:RestOfLine {
|
||||||
|
return { label: label, value };
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
import { _, RestOfLine, Newline, NumberValue, SeparatedNumberValue, BytesValue } from "../primitives"
|
||||||
|
import { Header } from "../shared"
|
||||||
|
|
||||||
|
{
|
||||||
|
const matchValue = require("match-value");
|
||||||
|
}
|
||||||
|
|
||||||
|
RootInfo
|
||||||
|
= header:Header infoSection:InfoSection Newline* {
|
||||||
|
return { ...header, fields: infoSection }
|
||||||
|
};
|
||||||
|
|
||||||
|
InfoSection 'information section'
|
||||||
|
= "=== START OF INFORMATION SECTION ===" Newline fields:(InfoField+) {
|
||||||
|
return fields.filter((field) => field != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoField 'information field'
|
||||||
|
= InfoFieldSimple
|
||||||
|
/ InfoFieldIgnored
|
||||||
|
/ InfoFieldSize
|
||||||
|
/ InfoFieldRPM
|
||||||
|
/ InfoFieldSectorSizes
|
||||||
|
/ InfoFieldBoolean
|
||||||
|
/ InfoFieldUnknown
|
||||||
|
|
||||||
|
InfoFieldSimpleKey
|
||||||
|
= "Device Model" { return "model"; }
|
||||||
|
/ "Model Number" { return "model"; }
|
||||||
|
/ "Model Family" { return "modelFamily"; }
|
||||||
|
/ "Serial Number" { return "serialNumber"; }
|
||||||
|
/ "LU WWN Device Id" { return "wwn"; }
|
||||||
|
/ "Firmware Version" { return "firmwareVersion"; }
|
||||||
|
/ "Form Factor" { return "formFactor"; }
|
||||||
|
/ "ATA Version is" { return "ataVersion"; }
|
||||||
|
/ "SATA Version is" { return "sataVersion"; }
|
||||||
|
|
||||||
|
InfoFieldSimple
|
||||||
|
= key:InfoFieldSimpleKey ":" _ value:RestOfLine {
|
||||||
|
return { key: key, value: value };
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoFieldUnknown
|
||||||
|
= key:$[^:]+ ":" _ RestOfLine {
|
||||||
|
console.warn(`Encountered unrecognized SMART info key: ${key}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoFieldIgnoredKey
|
||||||
|
= "Device is"
|
||||||
|
/ "Local Time is"
|
||||||
|
|
||||||
|
InfoFieldIgnored
|
||||||
|
= key:InfoFieldIgnoredKey ":" _ RestOfLine {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/ "SMART support is:" _ ("Available" / "Unavailable") RestOfLine {
|
||||||
|
// We don't actually care about this entry, but have to specify its possible values explicitly, to distinguish it from the entry we *do* care about that (annoyingly) uses the same key; see InfoFieldBoolean
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoFieldSize
|
||||||
|
// NOTE: We don't actually care about the human-friendly display size after the 'bytes' specifier, hence the RestOfLine
|
||||||
|
= InfoFieldSizeKey _ value:SeparatedNumberValue _ "bytes"? _ RestOfLine {
|
||||||
|
return {
|
||||||
|
key: "size",
|
||||||
|
value: B(value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoFieldSizeKey
|
||||||
|
= "User Capacity:"
|
||||||
|
/ "Total NVM Capacity:"
|
||||||
|
|
||||||
|
InfoFieldRPM
|
||||||
|
= "Rotation Rate:" _ value:NumberValue _ "rpm" Newline {
|
||||||
|
return {
|
||||||
|
key: "rpm",
|
||||||
|
value: value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoFieldSectorSizes
|
||||||
|
= "Sector Sizes:" _ logicalSize:BytesValue _ "bytes logical," _ physicalSize:BytesValue _ "bytes physical" Newline {
|
||||||
|
return {
|
||||||
|
key: "sectorSizes",
|
||||||
|
value: {
|
||||||
|
logical: logicalSize,
|
||||||
|
physical: physicalSize
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoFieldBooleanKey
|
||||||
|
= "SMART support is" { return "smartEnabled"; }
|
||||||
|
|
||||||
|
InfoFieldBoolean
|
||||||
|
= key:InfoFieldBooleanKey ":" _ value:RestOfLine {
|
||||||
|
return {
|
||||||
|
key: key,
|
||||||
|
value: matchValue(value, {
|
||||||
|
Enabled: true,
|
||||||
|
Disabled: false
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import { _, RestOfLine } from "../primitives"
|
||||||
|
|
||||||
|
RootScan
|
||||||
|
= devices:ScanDevice* {
|
||||||
|
return { devices: devices };
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanDevice 'scanned device'
|
||||||
|
= path:$[^ ]+ _ "-d" _ interface_:$[^ ]+ _ RestOfLine {
|
||||||
|
return { path: path, interface: interface_ };
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
const syncpipe = require("syncpipe");
|
||||||
|
const {B} = require("../../unit-bytes-iec");
|
||||||
|
}
|
||||||
|
|
||||||
|
_
|
||||||
|
= (" " / "\t")*
|
||||||
|
|
||||||
|
RestOfLine
|
||||||
|
= content:$[^\n]+ Newline {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
Newline
|
||||||
|
= "\n"
|
||||||
|
/ "\r\n"
|
||||||
|
|
||||||
|
BytesValue
|
||||||
|
= value:SeparatedNumberValue {
|
||||||
|
return B(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberValue
|
||||||
|
= value:$[0-9]+ {
|
||||||
|
return parseInt(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
SeparatedNumberValue
|
||||||
|
= value:$[0-9,]+ {
|
||||||
|
return syncpipe(value, [
|
||||||
|
(_) => _.replace(/,/g, ""),
|
||||||
|
(_) => parseInt(_)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
HexNumberValue
|
||||||
|
= value:$[0-9A-Fa-f]+ {
|
||||||
|
return parseInt(value, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentifierValue
|
||||||
|
= value:$[a-zA-Z_-]+ {
|
||||||
|
return value;
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
import { RestOfLine, Newline } from "./primitives"
|
||||||
|
|
||||||
|
Header 'header'
|
||||||
|
= "smartctl " versionString:RestOfLine "Copyright" copyrightStatement:RestOfLine Newline {
|
||||||
|
return { versionString, copyrightStatement };
|
||||||
|
}
|
@ -1,73 +1,74 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const pegjs = require("pegjs");
|
const Promise = require("bluebird");
|
||||||
const { validateOptions, either, required, isString, isPlainObject, allowExtraProperties } = require("validatem");
|
const pegRedux = require("peg-redux");
|
||||||
const fs = require("fs");
|
|
||||||
const moduleEval = require("eval");
|
const moduleEval = require("eval");
|
||||||
const vm = require("vm");
|
const vm = require("vm");
|
||||||
const asExpression = require("as-expression");
|
const asExpression = require("as-expression");
|
||||||
|
const { chain } = require("error-chain");
|
||||||
const textParser = require("../text-parser");
|
const textParser = require("../text-parser");
|
||||||
|
|
||||||
module.exports = function createPegParser({ grammar, grammarFile, options }) {
|
const { validateOptions } = require("@validatem/core");
|
||||||
validateOptions(arguments, [
|
const isString = require("@validatem/is-string");
|
||||||
|
const isPlainObject = require("@validatem/is-plain-object");
|
||||||
|
const requireEither = require("@validatem/require-either");
|
||||||
|
|
||||||
|
module.exports = function createPegParser(_options) {
|
||||||
|
let { grammar, grammarFile, options } = validateOptions(arguments, [
|
||||||
{
|
{
|
||||||
grammar: [ isString ],
|
grammar: [ isString ],
|
||||||
grammarFile: [ isString ],
|
grammarFile: [ isString ],
|
||||||
options: [ isPlainObject ]
|
options: [ isPlainObject ]
|
||||||
},
|
}, requireEither([ "grammar", "grammarFile" ])
|
||||||
// FIXME: require-either
|
|
||||||
either(
|
|
||||||
allowExtraProperties({ grammar: [ required ] }),
|
|
||||||
allowExtraProperties({ grammarFile: [ required ] })
|
|
||||||
)
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (grammarFile != null) {
|
let parserOptions = {
|
||||||
// FIXME: cache
|
|
||||||
grammar = fs.readFileSync(grammarFile, "utf8");
|
|
||||||
}
|
|
||||||
|
|
||||||
let parserCode = pegjs.generate(grammar, {
|
|
||||||
... options,
|
... options,
|
||||||
output: "source",
|
output: "source",
|
||||||
format: "commonjs"
|
format: "commonjs"
|
||||||
});
|
};
|
||||||
|
|
||||||
let parser = asExpression(() => {
|
|
||||||
if (grammarFile != null) {
|
|
||||||
return moduleEval(parserCode, grammarFile, {}, true);
|
|
||||||
} else {
|
|
||||||
let exports_ = {};
|
|
||||||
|
|
||||||
let sandbox = {
|
return Promise.try(() => {
|
||||||
exports: exports_,
|
return (grammarFile != null)
|
||||||
module: {
|
? pegRedux.generateFromFile(grammarFile, parserOptions)
|
||||||
|
: pegRedux.generate(grammar, parserOptions);
|
||||||
|
}).then((parserCode) => {
|
||||||
|
let parser = asExpression(() => {
|
||||||
|
if (grammarFile != null) {
|
||||||
|
return moduleEval(parserCode, grammarFile, {}, true);
|
||||||
|
} else {
|
||||||
|
let exports_ = {};
|
||||||
|
|
||||||
|
let sandbox = {
|
||||||
exports: exports_,
|
exports: exports_,
|
||||||
},
|
module: {
|
||||||
require: function () {
|
exports: exports_,
|
||||||
throw new Error("You cannot use require() when loading a grammar as a string; use the `grammarFile` option instead");
|
},
|
||||||
}
|
require: function () {
|
||||||
};
|
throw new Error("You cannot use require() when loading a grammar as a string; use the `grammarFile` option instead");
|
||||||
|
}
|
||||||
let script = new vm.Script(parserCode.replace(/^\#\!.*/, ''));
|
};
|
||||||
script.runInNewContext(sandbox);
|
|
||||||
|
let script = new vm.Script(parserCode.replace(/^\#\!.*/, ''));
|
||||||
return sandbox.module.exports;
|
script.runInNewContext(sandbox);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return sandbox.module.exports;
|
||||||
supportsStreams: false,
|
}
|
||||||
parse: function (text) {
|
});
|
||||||
try {
|
|
||||||
return parser.parse(text);
|
return {
|
||||||
} catch (error) {
|
supportsStreams: false,
|
||||||
if (error.name === "SyntaxError") {
|
parse: function (text) {
|
||||||
throw textParser.NoResult.chain(error, "Parsing output failed");
|
try {
|
||||||
} else {
|
return parser.parse(text);
|
||||||
throw error;
|
} catch (error) {
|
||||||
|
if (error.name === "SyntaxError") {
|
||||||
|
throw chain(error, textParser.NoResult, "Parsing output failed");
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
module.exports = function unreachable(reason) {
|
|
||||||
// TODO: Parse the package name out of the stacktrace and include it in the error message?
|
|
||||||
throw new Error(`${reason} -- this is a bug, please report it!`);
|
|
||||||
};
|
|
Loading…
Reference in New Issue