@tracespace/parser
Version:
Gerber and NC drill file parser
1,585 lines (1,413 loc) • 40.6 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var Moo = _interopDefault(require('moo'));
// common constants
// filetype constants
var GERBER = 'gerber';
var DRILL = 'drill'; // units constants
var MM = 'mm';
var IN = 'in'; // format constants
var LEADING = 'leading';
var TRAILING = 'trailing';
var ABSOLUTE = 'absolute';
var INCREMENTAL = 'incremental'; // tool constants
var CIRCLE = 'circle';
var RECTANGLE = 'rectangle';
var OBROUND = 'obround';
var POLYGON = 'polygon';
var MACRO_SHAPE = 'macroShape'; // macro primitive codes
var MACRO_CIRCLE = '1';
var MACRO_VECTOR_LINE = '20';
var MACRO_CENTER_LINE = '21';
var MACRO_OUTLINE = '4';
var MACRO_POLYGON = '5';
var MACRO_MOIRE = '6';
var MACRO_THERMAL = '7'; // drawing constants
var SHAPE = 'shape';
var MOVE = 'move';
var SEGMENT = 'segment';
var SLOT = 'slot'; // interpolation / routing constants
var LINE = 'line';
var CW_ARC = 'cwArc';
var CCW_ARC = 'ccwArc'; // quadrant mode
var SINGLE = 'single';
var MULTI = 'multi'; // load polarity
var DARK = 'dark';
var CLEAR = 'clear';
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _toArray(arr) {
return _arrayWithHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableRest();
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArray(iter) {
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
}
function _iterableToArrayLimit(arr, i) {
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
/**
* T-code token type
*
* @category Lexer
*/
var T_CODE = 'T_CODE';
/**
* G-code token type
*
* @category Lexer
*/
var G_CODE = 'G_CODE';
/**
* M-code token type
*
* @category Lexer
*/
var M_CODE = 'M_CODE';
/**
* D-code token type
*
* @category Lexer
*/
var D_CODE = 'D_CODE';
/**
* Asterisk token type
*
* @category Lexer
*/
var ASTERISK = 'ASTERISK';
/**
* Percent sign token type
*
* @category Lexer
*/
var PERCENT = 'PERCENT';
/**
* Equals sign token type
*
* @category Lexer
*/
var EQUALS = 'EQUALS';
/**
* Comma token type
*
* @category Lexer
*/
var COMMA = 'COMMA';
/**
* Arithmatic operator token type
*
* @category Lexer
*/
var OPERATOR = 'OPERATOR';
/**
* Gerber format specification token type
*
* @category Lexer
*/
var GERBER_FORMAT = 'GERBER_FORMAT';
/**
* Gerber units specification token type
*
* @category Lexer
*/
var GERBER_UNITS = 'GERBER_UNITS';
/**
* Gerber tool macro token type
*
* @category Lexer
*/
var GERBER_TOOL_MACRO = 'GERBER_TOOL_MACRO';
/**
* Gerber tool definition token type
*
* @category Lexer
*/
var GERBER_TOOL_DEF = 'GERBER_TOOL_DEF';
/**
* Gerber load polarity token type
*
* @category Lexer
*/
var GERBER_LOAD_POLARITY = 'GERBER_LOAD_POLARITY';
/**
* Gerber step repear token type
*
* @category Lexer
*/
var GERBER_STEP_REPEAT = 'GERBER_STEP_REPEAT';
/**
* Gerber macro variable token type
*
* @category Lexer
*/
var GERBER_MACRO_VARIABLE = 'GERBER_MACRO_VARIABLE';
/**
* Semicolor token type
*
* @category Lexer
*/
var SEMICOLON = 'SEMICOLON';
/**
* Drill file units token type
*
* @category Lexer
*/
var DRILL_UNITS = 'DRILL_UNITS';
/**
* Drill zero-inclusion token type
*
* @category Lexer
*/
var DRILL_ZERO_INCLUSION = 'DRILL_ZERO_INCLUSION';
/**
* Coordinate axis character token type
*
* @category Lexer
*/
var COORD_CHAR = 'COORD_CHAR';
/**
* Number token type
*
* @category Lexer
*/
var NUMBER = 'NUMBER';
/**
* Word token type
*
* @category Lexer
*/
var WORD = 'WORD';
/**
* Whitespace token type
*
* @category Lexer
*/
var WHITESPACE = 'WHITESPACE';
/**
* Newline token type
*
* @category Lexer
*/
var NEWLINE = 'NEWLINE';
/**
* Catchall token type
*
* @category Lexer
*/
var CATCHALL = 'CATCHALL';
/**
* Error token type
*
* @category Lexer
*/
var ERROR = 'ERROR';
/**
* Union of all available token types
*
* @category Lexer
*/
var _rules;
var RE_STRIP_LEADING_ZEROS = /^0*/;
var stripLeadingZeros = function stripLeadingZeros(text) {
return text.replace(RE_STRIP_LEADING_ZEROS, '');
};
var getCodeValue = function getCodeValue(text) {
return stripLeadingZeros(text.slice(1)) || '0';
};
var rules = (_rules = {}, _defineProperty(_rules, T_CODE, {
match: /T\d+/,
value: getCodeValue
}), _defineProperty(_rules, G_CODE, {
match: /G\d+/,
value: getCodeValue
}), _defineProperty(_rules, M_CODE, {
match: /M\d+/,
value: getCodeValue
}), _defineProperty(_rules, D_CODE, {
match: /D\d+/,
value: getCodeValue
}), _defineProperty(_rules, ASTERISK, '*'), _defineProperty(_rules, PERCENT, '%'), _defineProperty(_rules, EQUALS, '='), _defineProperty(_rules, GERBER_FORMAT, {
match: /FS[LTDAI]+/,
value: function value(text) {
return text.slice(2);
}
}), _defineProperty(_rules, GERBER_UNITS, {
match: /MO(?:IN|MM)/,
value: function value(text) {
return text.slice(2);
}
}), _defineProperty(_rules, GERBER_TOOL_MACRO, {
// "-" in a tool name is illegal, but some gerber writers misbehave
// https://github.com/mcous/gerber-parser/pull/13
match: /AM[a-zA-Z_.$][\w.-]*/,
value: function value(text) {
return text.slice(2);
}
}), _defineProperty(_rules, GERBER_TOOL_DEF, {
match: /ADD\d+[a-zA-Z_.$][\w.-]*/,
value: function value(text) {
return stripLeadingZeros(text.slice(3));
}
}), _defineProperty(_rules, GERBER_LOAD_POLARITY, {
match: /LP[DC]/,
value: function value(text) {
return text.slice(2);
}
}), _defineProperty(_rules, GERBER_STEP_REPEAT, 'SR'), _defineProperty(_rules, GERBER_MACRO_VARIABLE, /\$\d+/), _defineProperty(_rules, SEMICOLON, ';'), _defineProperty(_rules, DRILL_UNITS, /^(?:METRIC|INCH)/), _defineProperty(_rules, DRILL_ZERO_INCLUSION, {
match: /,(?:TZ|LZ)/,
value: function value(text) {
return text.slice(1);
}
}), _defineProperty(_rules, COORD_CHAR, /[XYIJACFSBHZN]/), _defineProperty(_rules, NUMBER, /(?:[+-])?[\d.]+/), _defineProperty(_rules, OPERATOR, ['x', '/', '+', '-', '(', ')']), _defineProperty(_rules, COMMA, ','), _defineProperty(_rules, WORD, /[a-zA-Z]+/), _defineProperty(_rules, WHITESPACE, /[ \t]+/), _defineProperty(_rules, NEWLINE, {
match: /\r?\n/,
lineBreaks: true
}), _defineProperty(_rules, CATCHALL, /[\S]/), _defineProperty(_rules, ERROR, Moo.error), _rules);
// gerber and drill file lexer + tokenizer
/**
* {@linkcode Lexer} factory
*
* @example
* ```ts
* import {createLexer} from '@tracespace/parser'
*
* const lexer = createLexer()
*
* lexer.reset('G04 gerber string*\nM02*\n')
*
* Array.from(lexer).forEach(token => {
* console.log(`${token.type}: ${token.value}`)
* })
* ```
*
* @category Lexer
*/
function createLexer() {
return Moo.compile(rules);
}
/**
* The lexing module of the parser. The Lexer is generated by
* {@link https://github.com/no-context/moo | Moo}, which determines its API.
*
* @category Lexer
*/
/**
* {@linkcode Root} node type
*
* @category Node
*/
var ROOT = 'root';
/**
* {@linkcode Comment} node type
*
* @category Node
*/
var COMMENT = 'comment';
/**
* {@linkcode Done} node type
*
* @category Node
*/
var DONE = 'done';
/**
* {@linkcode Units} node type
*
* @category Node
*/
var UNITS = 'units';
/**
* {@linkcode CoordinateFormat} node type
*
* @category Node
*/
var COORDINATE_FORMAT = 'coordinateFormat';
/**
* {@linkcode ToolDefinition} node type
*
* @category Node
*/
var TOOL_DEFINITION = 'toolDefinition';
/**
* {@linkcode ToolMacro} node type
*
* @category Node
*/
var TOOL_MACRO = 'toolMacro';
/**
* {@linkcode ToolChange} node type
*
* @category Node
*/
var TOOL_CHANGE = 'toolChange';
/**
* {@linkcode LoadPolarity} node type
*
* @category Node
*/
var LOAD_POLARITY = 'loadPolarity';
/**
* {@linkcode StepRepeat} node type
*
* @category Node
*/
var STEP_REPEAT = 'stepRepeat';
/**
* {@linkcode Graphic} node type
*
* @category Node
*/
var GRAPHIC = 'graphic';
/**
* {@linkcode InterpolateMode} node type
*
* @category Node
*/
var INTERPOLATE_MODE = 'interpolateMode';
/**
* {@linkcode RegionMode} node type
*
* @category Node
*/
var REGION_MODE = 'regionMode';
/**
* {@linkcode QuadrantMode} node type
*
* @category Node
*/
var QUADRANT_MODE = 'quadrantMode';
/**
* {@linkcode Unimplemented} node type
*
* @category Node
*/
var UNIMPLEMENTED = 'unimplemented';
/**
* {@linkcode MacroComment} node type
*
* @category Macro
*/
var MACRO_COMMENT = 'macroComment';
/**
* {@linkcode MacroVariable} node type
*
* @category Macro
*/
var MACRO_VARIABLE = 'macroVariable';
/**
* {@linkcode MacroPrimitive} node type
*
* @category Macro
*/
var MACRO_PRIMITIVE = 'macroPrimitive';
var SINGLE_TOKEN = 'TOKEN';
var MIN_TO_MAX = 'MIN_TO_MAX';
function token(type, value) {
return {
rule: SINGLE_TOKEN,
type: type,
value: value
};
}
function notToken(type, value) {
return {
rule: SINGLE_TOKEN,
type: type,
value: value,
negate: true
};
}
function one(match) {
return {
rule: MIN_TO_MAX,
min: 1,
max: 1,
match: match
};
}
function zeroOrOne(match) {
return {
rule: MIN_TO_MAX,
min: 0,
max: 1,
match: match
};
}
function zeroOrMore(match) {
return {
rule: MIN_TO_MAX,
min: 0,
max: Infinity,
match: match
};
}
function oneOrMore(match) {
return {
rule: MIN_TO_MAX,
min: 1,
max: Infinity,
match: match
};
}
function minToMax(min, max, match) {
return {
rule: MIN_TO_MAX,
min: min,
max: max,
match: match
};
}
function tokensToCoordinates(tokens) {
return tokens.reduce(function (coords, token, i) {
var prev = tokens[i - 1];
if (token.type === NUMBER && (prev === null || prev === void 0 ? void 0 : prev.type) === COORD_CHAR) {
coords[prev.value.toLowerCase()] = token.value;
}
return coords;
}, {});
}
function tokensToMode(tokens) {
return tokens.filter(function (t) {
return t.type === G_CODE;
}).reduce(function (m, t) {
if (t.value === '0') return MOVE;
if (t.value === '1') return LINE;
if (t.value === '2') return CW_ARC;
if (t.value === '3') return CCW_ARC;
if (t.value === '5') return DRILL;
return m;
}, null);
}
function tokensToGraphic(tokens) {
return tokens.filter(function (t) {
return t.type === D_CODE;
}).reduce(function (g, t) {
if (t.value === '1') return SEGMENT;
if (t.value === '2') return MOVE;
if (t.value === '3') return SHAPE;
return g;
}, null);
}
function tokensToString(tokens) {
return tokens.map(function (t) {
return t.value;
}).join('').trim();
}
function tokensToPosition(tokens) {
var _options$head;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var head = (_options$head = options.head) !== null && _options$head !== void 0 ? _options$head : tokens[0];
var tail = options.length ? tokens[tokens.indexOf(head) + options.length - 1] : tokens[tokens.length - 1];
return {
start: {
line: head.line,
column: head.col,
offset: head.offset
},
end: {
line: tail.line,
column: tail.col,
offset: tail.offset
}
};
}
var units = {
rules: [one([token(DRILL_UNITS), token(M_CODE, '71'), token(M_CODE, '72')]), zeroOrMore([token(COMMA), token(DRILL_ZERO_INCLUSION), token(NUMBER, /^0{1,8}\.0{1,8}$/)]), token(NEWLINE)],
createNodes: function createNodes(tokens) {
var units = tokens[0].value === 'INCH' || tokens[0].value === '72' ? IN : MM;
var zeroSuppression = tokens.filter(function (t) {
return t.type === DRILL_ZERO_INCLUSION;
}).reduce(function (z, t) {
if (t.value === 'LZ') return TRAILING;
if (t.value === 'TZ') return LEADING;
return z;
}, null);
var format = tokens.filter(function (t) {
return t.type === NUMBER;
}).reduce(function (_, t) {
var _t$value$split = t.value.split('.'),
_t$value$split2 = _slicedToArray(_t$value$split, 2),
_t$value$split2$ = _t$value$split2[0],
integer = _t$value$split2$ === void 0 ? '' : _t$value$split2$,
_t$value$split2$2 = _t$value$split2[1],
decimal = _t$value$split2$2 === void 0 ? '' : _t$value$split2$2;
return [integer.length, decimal.length];
}, null);
var nodes = [{
type: UNITS,
position: tokensToPosition(tokens.slice(0, 2)),
units: units
}];
if (zeroSuppression || format) {
nodes.push({
type: COORDINATE_FORMAT,
position: tokensToPosition(tokens.slice(1)),
mode: null,
format: format,
zeroSuppression: zeroSuppression
});
}
return nodes;
}
};
var tool = {
rules: [token(T_CODE), minToMax(0, 12, [token(COORD_CHAR, 'C'), token(COORD_CHAR, 'F'), token(COORD_CHAR, 'S'), token(COORD_CHAR, 'B'), token(COORD_CHAR, 'H'), token(COORD_CHAR, 'Z'), token(NUMBER)]), token(NEWLINE)],
createNodes: function createNodes(tokens) {
var code = tokens[0].value;
var position = tokensToPosition(tokens);
var _tokensToCoordinates = tokensToCoordinates(tokens.slice(1, -1)),
_tokensToCoordinates$ = _tokensToCoordinates.c,
c = _tokensToCoordinates$ === void 0 ? null : _tokensToCoordinates$;
var shape = c !== null ? {
type: CIRCLE,
diameter: Number(c)
} : null;
return shape ? [{
type: TOOL_DEFINITION,
hole: null,
position: position,
shape: shape,
code: code
}] : [{
type: TOOL_CHANGE,
position: position,
code: code
}];
}
};
var mode = {
rules: [one([token(G_CODE, '0'), token(G_CODE, '1'), token(G_CODE, '2'), token(G_CODE, '3'), token(G_CODE, '5')]), token(NEWLINE)],
createNodes: function createNodes(tokens) {
return [{
type: INTERPOLATE_MODE,
position: tokensToPosition(tokens),
mode: tokensToMode(tokens)
}];
}
};
var operation = {
rules: [minToMax(0, 2, [token(T_CODE), token(G_CODE, '0'), token(G_CODE, '1'), token(G_CODE, '2'), token(G_CODE, '3'), token(G_CODE, '5')]), minToMax(2, 8, [token(COORD_CHAR), token(NUMBER)]), zeroOrOne([token(T_CODE)]), token(NEWLINE)],
createNodes: function createNodes(tokens) {
var graphicTokens = tokens.filter(function (t) {
return t.type === COORD_CHAR || t.type === NUMBER;
});
var modeToken = tokens.find(function (t) {
return t.type === G_CODE;
});
var toolToken = tokens.find(function (t) {
return t.type === T_CODE;
});
var coordinates = tokensToCoordinates(graphicTokens);
var code = toolToken ? toolToken.value : null;
var mode = tokensToMode(tokens);
var graphicPosition = tokensToPosition(tokens, {
head: graphicTokens[0],
length: graphicTokens.length + 1
});
var modePosition = tokensToPosition(tokens, {
head: modeToken,
length: 2
});
var toolPosition = tokensToPosition(tokens, {
head: toolToken,
length: 2
});
var nodes = [{
type: GRAPHIC,
position: graphicPosition,
graphic: null,
coordinates: coordinates
}];
if (mode) {
nodes.unshift({
type: INTERPOLATE_MODE,
position: modePosition,
mode: mode
});
}
if (code) {
nodes.unshift({
type: TOOL_CHANGE,
position: toolPosition,
code: code
});
}
return nodes;
}
};
var slot = {
rules: [minToMax(2, 4, [token(COORD_CHAR), token(NUMBER)]), token(G_CODE, '85'), minToMax(2, 4, [token(COORD_CHAR), token(NUMBER)]), token(NEWLINE)],
createNodes: function createNodes(tokens) {
var gCode = tokens.find(function (t) {
return t.type === G_CODE;
});
var splitIdx = gCode ? tokens.indexOf(gCode) : -1;
var start = tokensToCoordinates(tokens.slice(0, splitIdx));
var end = tokensToCoordinates(tokens.slice(splitIdx));
var coordinates = {};
Object.keys(start).forEach(function (k) {
return coordinates["".concat(k, "1")] = start[k];
});
Object.keys(end).forEach(function (k) {
return coordinates["".concat(k, "2")] = end[k];
});
return [{
type: GRAPHIC,
position: tokensToPosition(tokens),
graphic: SLOT,
coordinates: coordinates
}];
}
};
var done = {
rules: [one([token(M_CODE, '30'), token(M_CODE, '0')]), token(NEWLINE)],
createNodes: function createNodes(tokens) {
return [{
type: DONE,
position: tokensToPosition(tokens)
}];
}
};
var comment = {
rules: [token(SEMICOLON), zeroOrMore([notToken(NEWLINE)]), token(NEWLINE)],
createNodes: function createNodes(tokens) {
return [{
type: COMMENT,
comment: tokensToString(tokens.slice(1, -1)),
position: tokensToPosition(tokens)
}];
}
};
var drillSyntax = [tool, mode, operation, slot, comment, units, done].map(function (r) {
return _extends({}, r, {
filetype: DRILL
});
});
var FULL_MATCH = 'FULL_MATCH';
var PARTIAL_MATCH = 'PARTIAL_MATCH';
var NO_MATCH = 'NO_MATCH';
function createMatchSyntax() {
for (var _len = arguments.length, grammar = new Array(_len), _key = 0; _key < _len; _key++) {
grammar[_key] = arguments[_key];
}
return function (state, token) {
return matchSyntax(state, token, grammar);
};
}
function matchSyntax(state, token, grammar) {
if (state === null) state = {
candidates: grammar,
tokens: []
};
var _state = state,
prevCandidates = _state.candidates;
var candidates = [];
var tokens = [].concat(_toConsumableArray(state.tokens), [token]);
var i;
for (i = 0; i < prevCandidates.length; i++) {
var rule = prevCandidates[i];
var result = tokenListMatches(rule.rules, tokens);
if (result === FULL_MATCH) {
var nodes = rule.createNodes(tokens);
return {
candidates: [],
tokens: tokens,
nodes: nodes,
filetype: rule.filetype
};
}
if (result === PARTIAL_MATCH) {
candidates.push(rule);
}
}
return {
candidates: candidates,
tokens: tokens
};
}
function tokenListMatches(rules, tokens) {
var i = 0;
var j = 0;
var multiMatchCount = 0;
while (i < rules.length && j < tokens.length) {
var rule = rules[i];
var _token = tokens[j];
var match = tokenMatches(rule, _token);
if (match) {
if (rule.rule === SINGLE_TOKEN || rule.rule === MIN_TO_MAX && multiMatchCount >= rule.max - 1) {
i++;
j++;
multiMatchCount = 0;
} else if (rule.rule === MIN_TO_MAX) {
j++;
multiMatchCount++;
}
} else if (rule.rule === MIN_TO_MAX && multiMatchCount >= rule.min) {
multiMatchCount = 0;
i++;
} else {
return NO_MATCH;
}
}
if (i < rules.length) return PARTIAL_MATCH;
return FULL_MATCH;
}
function tokenMatches(rule, token) {
if (rule.rule === SINGLE_TOKEN) {
var typeResult = rule.type === token.type;
var valueResult = rule.value == null || typeof rule.value === 'string' && rule.value === token.value || rule.value instanceof RegExp && rule.value.test(token.value);
var result = typeResult && valueResult;
return rule.negate ? !result : result;
}
if (Array.isArray(rule.match)) {
return rule.match.some(function (match) {
return tokenMatches(match, token);
});
}
return false;
}
var macroComment = {
rules: [token(NUMBER, '0'), zeroOrMore([notToken(ASTERISK)]), token(ASTERISK)],
createNodes: createMacroComment
};
var macroVariable = {
rules: [token(GERBER_MACRO_VARIABLE), token(EQUALS), oneOrMore([token(NUMBER), token(OPERATOR), token(GERBER_MACRO_VARIABLE), token(COORD_CHAR, 'X')]), token(ASTERISK)],
createNodes: createMacroVariable
};
var macroPrimitive = {
rules: [token(NUMBER), token(COMMA), oneOrMore([token(COMMA), token(NUMBER), token(OPERATOR), token(GERBER_MACRO_VARIABLE), token(COORD_CHAR, 'X')]), token(ASTERISK)],
createNodes: createMacroPrimitive
};
function createMacroComment(tokens) {
var comment = tokens.slice(1, -1).map(function (t) {
return t.text;
}).join('').trim();
return [{
type: MACRO_COMMENT,
position: tokensToPosition(tokens),
comment: comment
}];
}
function createMacroPrimitive(tokens) {
var code = tokens[0].value;
var modifiers = tokens.slice(2, -1).reduce(function (groups, token) {
var current = groups[groups.length - 1];
if (token.type !== COMMA) {
current.push(token);
} else {
groups.push([]);
}
return groups;
}, [[]]).map(parseMacroExpression);
return [{
type: MACRO_PRIMITIVE,
position: tokensToPosition(tokens),
code: code,
modifiers: modifiers
}];
}
function createMacroVariable(tokens) {
var name = tokens[0].value;
var value = parseMacroExpression(tokens.slice(2, -1));
return [{
type: MACRO_VARIABLE,
position: tokensToPosition(tokens),
name: name,
value: value
}];
}
function parseMacroExpression(tokens) {
var toParse = tokens.map(function (token) {
return token.type === COORD_CHAR ? _extends({}, token, {
type: OPERATOR,
value: 'x'
}) : token;
});
return parseAddition();
function peekNextToken() {
var _toParse$;
return (_toParse$ = toParse[0]) !== null && _toParse$ !== void 0 ? _toParse$ : null;
} // parse numbers, variables, and parenthesis
function parsePrimary() {
var token = toParse.shift();
if (token.type === NUMBER) return Number(token.value);
if (token.type === GERBER_MACRO_VARIABLE) return token.value; // else, we've got a parentheses group, so parse it and consume the ")"
var expression = parseAddition();
toParse.shift();
return expression;
} // parse multiplication and division operations
function parseMultiplication() {
var expression = parsePrimary();
var nextToken = peekNextToken();
while (((_nextToken = nextToken) === null || _nextToken === void 0 ? void 0 : _nextToken.type) === OPERATOR && (nextToken.value === 'x' || nextToken.value === '/')) {
var _nextToken;
toParse.shift();
expression = {
left: expression,
right: parsePrimary(),
operator: nextToken.value
};
nextToken = peekNextToken();
}
return expression;
}
function parseAddition() {
var expression = parseMultiplication();
var nextToken = peekNextToken();
while (((_nextToken2 = nextToken) === null || _nextToken2 === void 0 ? void 0 : _nextToken2.type) === OPERATOR && (nextToken.value === '+' || nextToken.value === '-') || ((_nextToken3 = nextToken) === null || _nextToken3 === void 0 ? void 0 : _nextToken3.type) === NUMBER) {
var _nextToken2, _nextToken3;
var operator = '+';
if (nextToken.type === OPERATOR) {
toParse.shift();
operator = nextToken.value;
}
var right = parseMultiplication();
expression = {
left: expression,
right: right,
operator: operator
};
nextToken = peekNextToken();
}
return expression;
}
}
var MACRO_GRAMMAR = [macroPrimitive, macroVariable, macroComment];
function parseMacroBlocks(tokens) {
var matchState = null;
var blocks = [];
tokens.forEach(function (token) {
matchState = matchSyntax(matchState, token, MACRO_GRAMMAR);
if (matchState.nodes) blocks.push.apply(blocks, _toConsumableArray(matchState.nodes));
if (matchState.candidates.length === 0) matchState = null;
});
return blocks;
}
var holeParamsToShape = function holeParamsToShape(params) {
if (params.length === 1) {
var _params = _slicedToArray(params, 1),
diameter = _params[0];
return {
type: CIRCLE,
diameter: diameter
};
}
if (params.length === 2) {
var _params2 = _slicedToArray(params, 2),
xSize = _params2[0],
ySize = _params2[1];
return {
type: RECTANGLE,
xSize: xSize,
ySize: ySize
};
}
return null;
};
var done$1 = {
rules: [one([token(M_CODE, '0'), token(M_CODE, '2')]), token(ASTERISK)],
createNodes: function createNodes(tokens) {
return [{
type: DONE,
position: tokensToPosition(tokens)
}];
}
};
var comment$1 = {
rules: [token(G_CODE, '4'), zeroOrMore([notToken(ASTERISK)]), token(ASTERISK)],
createNodes: function createNodes(tokens) {
return [{
type: COMMENT,
position: tokensToPosition(tokens),
comment: tokensToString(tokens.slice(1, -1))
}];
}
};
var format = {
rules: [token(PERCENT), token(GERBER_FORMAT), zeroOrMore([notToken(COORD_CHAR, 'X')]), token(COORD_CHAR, 'X'), token(NUMBER), token(COORD_CHAR, 'Y'), token(NUMBER), zeroOrMore([notToken(ASTERISK)]), token(ASTERISK), // including units here is invalid syntax, but Cadence Allegro does it
// https://github.com/tracespace/tracespace/issues/234
minToMax(0, 2, [token(GERBER_UNITS), token(ASTERISK)]), token(PERCENT)],
createNodes: function createNodes(tokens) {
var _coords$x;
var format = null;
var zeroSuppression = null;
var mode = null;
var coords = tokensToCoordinates(tokens);
var formatEndIdx = tokens.findIndex(function (t) {
return t.type === ASTERISK;
});
var unitsToken = tokens.find(function (t) {
return t.type === GERBER_UNITS;
});
tokens.filter(function (t) {
return t.type === GERBER_FORMAT;
}).forEach(function (t) {
if (t.value.indexOf('T') >= 0) zeroSuppression = TRAILING;
if (t.value.indexOf('L') >= 0) zeroSuppression = LEADING;
if (t.value.indexOf('I') >= 0) mode = INCREMENTAL;
if (t.value.indexOf('A') >= 0) mode = ABSOLUTE;
});
if (coords.x === coords.y && ((_coords$x = coords.x) === null || _coords$x === void 0 ? void 0 : _coords$x.length) === 2) {
var integers = Number(coords.x[0]);
var decimals = Number(coords.x[1]);
if (integers && decimals) format = [integers, decimals];
}
var nodes = [{
type: COORDINATE_FORMAT,
position: tokensToPosition(tokens.slice(1, formatEndIdx + 1)),
zeroSuppression: zeroSuppression,
format: format,
mode: mode
}];
if (unitsToken) {
nodes.push({
type: UNITS,
position: tokensToPosition(tokens.slice(1, -1), {
head: unitsToken
}),
units: unitsToken.value === 'MM' ? MM : IN
});
}
return nodes;
}
};
var units$1 = {
rules: [token(PERCENT), token(GERBER_UNITS), token(ASTERISK), token(PERCENT)],
createNodes: function createNodes(tokens) {
return [{
type: UNITS,
position: tokensToPosition(tokens.slice(1, -1)),
units: tokens[1].value === 'MM' ? MM : IN
}];
}
};
var toolMacro = {
rules: [token(PERCENT), token(GERBER_TOOL_MACRO), token(ASTERISK), zeroOrMore([notToken(PERCENT)]), token(PERCENT)],
createNodes: function createNodes(tokens) {
var name = tokens[1].value;
var position = tokensToPosition(tokens.slice(1, -1));
var blockTokens = tokens.slice(3, -1);
return [{
type: TOOL_MACRO,
position: position,
children: parseMacroBlocks(blockTokens),
name: name
}];
}
};
var toolDefinition = {
rules: [token(PERCENT), token(GERBER_TOOL_DEF), zeroOrMore([token(COMMA), token(NUMBER), token(COORD_CHAR, 'X')]), token(ASTERISK), token(PERCENT)],
createNodes: function createNodes(tokens) {
var shape;
var hole = null;
var toolProps = tokens[1].value.match(/(\d+)(.+)/);
var _ref = toolProps !== null && toolProps !== void 0 ? toolProps : [],
_ref2 = _slicedToArray(_ref, 3),
_ref2$ = _ref2[1],
code = _ref2$ === void 0 ? '' : _ref2$,
_ref2$2 = _ref2[2],
name = _ref2$2 === void 0 ? '' : _ref2$2;
var params = tokens.slice(3, -2).filter(function (t) {
return t.type === NUMBER;
}).map(function (t) {
return Number(t.value);
});
if (name === 'C') {
var _params3 = _toArray(params),
diameter = _params3[0],
holeParams = _params3.slice(1);
shape = {
type: CIRCLE,
diameter: diameter
};
hole = holeParamsToShape(holeParams);
} else if (name === 'R' || name === 'O') {
var _params4 = _toArray(params),
xSize = _params4[0],
ySize = _params4[1],
_holeParams = _params4.slice(2);
var type = name === 'R' ? RECTANGLE : OBROUND;
shape = {
type: type,
xSize: xSize,
ySize: ySize
};
hole = holeParamsToShape(_holeParams);
} else if (name === 'P') {
var _params5 = _toArray(params),
_diameter = _params5[0],
vertices = _params5[1],
_params5$ = _params5[2],
rotation = _params5$ === void 0 ? null : _params5$,
_holeParams2 = _params5.slice(3);
shape = {
type: POLYGON,
diameter: _diameter,
vertices: vertices,
rotation: rotation
};
hole = holeParamsToShape(_holeParams2);
} else {
shape = {
type: MACRO_SHAPE,
name: name,
params: params
};
}
return [{
type: TOOL_DEFINITION,
position: tokensToPosition(tokens.slice(1, -1)),
code: code,
shape: shape,
hole: hole
}];
}
};
var toolChange = {
rules: [zeroOrOne([token(G_CODE, '54')]), token(D_CODE), token(ASTERISK)],
createNodes: function createNodes(tokens) {
var _tokens$find;
return [{
type: TOOL_CHANGE,
position: tokensToPosition(tokens),
code: (_tokens$find = tokens.find(function (t) {
return t.type === D_CODE;
})) === null || _tokens$find === void 0 ? void 0 : _tokens$find.value
}];
}
};
var createOperationNodes = function createOperationNodes(tokens) {
var graphic = tokensToGraphic(tokens);
var coordinates = tokensToCoordinates(tokens);
var mode = tokensToMode(tokens);
var position = tokensToPosition(tokens, {
head: mode ? tokens[1] : tokens[0]
});
var nodes = [{
type: GRAPHIC,
position: position,
graphic: graphic,
coordinates: coordinates
}];
if (mode) {
var modePosition = tokensToPosition(tokens, {
head: tokens[0],
length: 2
});
nodes.unshift({
type: INTERPOLATE_MODE,
position: modePosition,
mode: mode
});
}
return nodes;
};
var operation$1 = {
rules: [zeroOrOne([token(G_CODE, '1'), token(G_CODE, '2'), token(G_CODE, '3')]), minToMax(2, 8, [token(COORD_CHAR), token(NUMBER)]), zeroOrOne([token(D_CODE, '1'), token(D_CODE, '2'), token(D_CODE, '3')]), token(ASTERISK)],
createNodes: createOperationNodes
};
var operationWithoutCoords = {
rules: [zeroOrOne([token(G_CODE, '1'), token(G_CODE, '2'), token(G_CODE, '3')]), one([token(D_CODE, '1'), token(D_CODE, '2'), token(D_CODE, '3')]), token(ASTERISK)],
createNodes: createOperationNodes
};
var interpolationMode = {
rules: [one([token(G_CODE, '1'), token(G_CODE, '2'), token(G_CODE, '3')]), token(ASTERISK)],
createNodes: function createNodes(tokens) {
return [{
type: INTERPOLATE_MODE,
position: tokensToPosition(tokens),
mode: tokensToMode(tokens)
}];
}
};
var regionMode = {
rules: [one([token(G_CODE, '36'), token(G_CODE, '37')]), token(ASTERISK)],
createNodes: function createNodes(tokens) {
return [{
type: REGION_MODE,
position: tokensToPosition(tokens),
region: tokens[0].value === '36'
}];
}
};
var quadrantMode = {
rules: [one([token(G_CODE, '74'), token(G_CODE, '75')]), token(ASTERISK)],
createNodes: function createNodes(tokens) {
return [{
type: QUADRANT_MODE,
position: tokensToPosition(tokens),
quadrant: tokens[0].value === '74' ? SINGLE : MULTI
}];
}
};
var loadPolarity = {
rules: [token(PERCENT), token(GERBER_LOAD_POLARITY), token(ASTERISK), token(PERCENT)],
createNodes: function createNodes(tokens) {
return [{
type: LOAD_POLARITY,
position: tokensToPosition(tokens.slice(1, -1)),
polarity: tokens[1].value === 'D' ? DARK : CLEAR
}];
}
};
var stepRepeat = {
rules: [token(PERCENT), token(GERBER_STEP_REPEAT), zeroOrMore([token(COORD_CHAR), token(NUMBER)]), token(ASTERISK), token(PERCENT)],
createNodes: function createNodes(tokens) {
var coordinates = tokensToCoordinates(tokens);
var params = Object.keys(coordinates).reduce(function (res, axis) {
res[axis] = Number(coordinates[axis]);
return res;
}, {});
return [{
type: STEP_REPEAT,
position: tokensToPosition(tokens.slice(1, -1)),
stepRepeat: params
}];
}
};
var unimplementedExtendedCommand = {
rules: [token(PERCENT), zeroOrMore([notToken(ASTERISK)]), token(ASTERISK), token(PERCENT)],
createNodes: function createNodes(tokens) {
return [{
type: UNIMPLEMENTED,
position: tokensToPosition(tokens.slice(1, -1)),
value: tokensToString(tokens)
}];
}
};
var gerberSyntax = [operation$1, operationWithoutCoords, interpolationMode, toolChange, toolDefinition, toolMacro, comment$1, regionMode, quadrantMode, loadPolarity, stepRepeat, format, units$1, done$1, unimplementedExtendedCommand].map(function (r) {
return _extends({}, r, {
filetype: GERBER
});
});
var matchSyntax$1 = createMatchSyntax.apply(void 0, _toConsumableArray(gerberSyntax).concat(_toConsumableArray(drillSyntax)));
/**
* Gerber and NC drill file parser.
*
* @category Parser
*/
/**
* {@linkcode Parser} factory and the primary export of the library.
*
* @example
* ```ts
* import {createParser} from '@tracespace/parser'
*
* // create a parser to parse a single file
* const parser = createParser()
*
* // feed the parser the source file contents
* parser.feed('G04 gerber file contents*\nM02*\n')
*
* // get the resulting AST
* const tree = parser.results()
* ```
*
* @category Parser
*/
function createParser() {
var lexer = createLexer();
var root = {
type: ROOT,
filetype: null,
done: false,
children: []
};
var stash = '';
var lexerOffset = 0;
var lexerState = lexer.save();
return {
lexer: lexer,
feed: feed,
results: results
};
function feed(chunk) {
var currentStash = stash;
var currentOffset = lexerOffset;
var matchState = null;
var nextToken;
stash = '';
lexer.reset("".concat(currentStash).concat(chunk), lexerState);
while (nextToken = lexer.next()) {
var token = _extends({}, nextToken, {
offset: nextToken.offset + currentOffset
});
stash += nextToken.text;
matchState = matchSyntax$1(matchState, token);
if (matchState.nodes) {
var _lexer$index, _root$children, _root$filetype;
var _matchState = matchState,
nodes = _matchState.nodes,
_matchState$filetype = _matchState.filetype,
filetype = _matchState$filetype === void 0 ? null : _matchState$filetype;
stash = '';
lexerOffset += (_lexer$index = lexer.index) !== null && _lexer$index !== void 0 ? _lexer$index : 0;
lexerState = lexer.save();
(_root$children = root.children).push.apply(_root$children, _toConsumableArray(nodes));
root.filetype = (_root$filetype = root.filetype) !== null && _root$filetype !== void 0 ? _root$filetype : filetype;
root.done = root.done || nodes.some(function (n) {
return n.type === DONE;
});
}
if (matchState.candidates.length === 0) {
matchState = null;
}
}
}
function results() {
return root;
}
}
exports.ABSOLUTE = ABSOLUTE;
exports.ASTERISK = ASTERISK;
exports.CATCHALL = CATCHALL;
exports.CCW_ARC = CCW_ARC;
exports.CIRCLE = CIRCLE;
exports.CLEAR = CLEAR;
exports.COMMA = COMMA;
exports.COMMENT = COMMENT;
exports.COORDINATE_FORMAT = COORDINATE_FORMAT;
exports.COORD_CHAR = COORD_CHAR;
exports.CW_ARC = CW_ARC;
exports.DARK = DARK;
exports.DONE = DONE;
exports.DRILL = DRILL;
exports.DRILL_UNITS = DRILL_UNITS;
exports.DRILL_ZERO_INCLUSION = DRILL_ZERO_INCLUSION;
exports.D_CODE = D_CODE;
exports.EQUALS = EQUALS;
exports.ERROR = ERROR;
exports.GERBER = GERBER;
exports.GERBER_FORMAT = GERBER_FORMAT;
exports.GERBER_LOAD_POLARITY = GERBER_LOAD_POLARITY;
exports.GERBER_MACRO_VARIABLE = GERBER_MACRO_VARIABLE;
exports.GERBER_STEP_REPEAT = GERBER_STEP_REPEAT;
exports.GERBER_TOOL_DEF = GERBER_TOOL_DEF;
exports.GERBER_TOOL_MACRO = GERBER_TOOL_MACRO;
exports.GERBER_UNITS = GERBER_UNITS;
exports.GRAPHIC = GRAPHIC;
exports.G_CODE = G_CODE;
exports.IN = IN;
exports.INCREMENTAL = INCREMENTAL;
exports.INTERPOLATE_MODE = INTERPOLATE_MODE;
exports.LEADING = LEADING;
exports.LINE = LINE;
exports.LOAD_POLARITY = LOAD_POLARITY;
exports.MACRO_CENTER_LINE = MACRO_CENTER_LINE;
exports.MACRO_CIRCLE = MACRO_CIRCLE;
exports.MACRO_COMMENT = MACRO_COMMENT;
exports.MACRO_MOIRE = MACRO_MOIRE;
exports.MACRO_OUTLINE = MACRO_OUTLINE;
exports.MACRO_POLYGON = MACRO_POLYGON;
exports.MACRO_PRIMITIVE = MACRO_PRIMITIVE;
exports.MACRO_SHAPE = MACRO_SHAPE;
exports.MACRO_THERMAL = MACRO_THERMAL;
exports.MACRO_VARIABLE = MACRO_VARIABLE;
exports.MACRO_VECTOR_LINE = MACRO_VECTOR_LINE;
exports.MM = MM;
exports.MOVE = MOVE;
exports.MULTI = MULTI;
exports.M_CODE = M_CODE;
exports.NEWLINE = NEWLINE;
exports.NUMBER = NUMBER;
exports.OBROUND = OBROUND;
exports.OPERATOR = OPERATOR;
exports.PERCENT = PERCENT;
exports.POLYGON = POLYGON;
exports.QUADRANT_MODE = QUADRANT_MODE;
exports.RECTANGLE = RECTANGLE;
exports.REGION_MODE = REGION_MODE;
exports.ROOT = ROOT;
exports.SEGMENT = SEGMENT;
exports.SEMICOLON = SEMICOLON;
exports.SHAPE = SHAPE;
exports.SINGLE = SINGLE;
exports.SLOT = SLOT;
exports.STEP_REPEAT = STEP_REPEAT;
exports.TOOL_CHANGE = TOOL_CHANGE;
exports.TOOL_DEFINITION = TOOL_DEFINITION;
exports.TOOL_MACRO = TOOL_MACRO;
exports.TRAILING = TRAILING;
exports.T_CODE = T_CODE;
exports.UNIMPLEMENTED = UNIMPLEMENTED;
exports.UNITS = UNITS;
exports.WHITESPACE = WHITESPACE;
exports.WORD = WORD;
exports.createLexer = createLexer;
exports.createParser = createParser;
//# sourceMappingURL=index.js.map