@tracespace/parser
Version:
Gerber and NC drill file parser
228 lines (207 loc) • 6.05 kB
text/typescript
// drill file grammar
import * as Lexer from '../lexer'
import * as Tree from '../tree'
import * as Constants from '../constants'
import * as Types from '../types'
import {token, notToken, one, zeroOrOne, zeroOrMore, minToMax} from './rules'
import {SyntaxRule} from './types'
import {
tokensToCoordinates,
tokensToMode,
tokensToString,
tokensToPosition,
} from './map-tokens'
const units: SyntaxRule = {
rules: [
one([
token(Lexer.DRILL_UNITS),
token(Lexer.M_CODE, '71'),
token(Lexer.M_CODE, '72'),
]),
zeroOrMore([
token(Lexer.COMMA),
token(Lexer.DRILL_ZERO_INCLUSION),
token(Lexer.NUMBER, /^0{1,8}\.0{1,8}$/),
]),
token(Lexer.NEWLINE),
],
createNodes: tokens => {
const units =
tokens[0].value === 'INCH' || tokens[0].value === '72'
? Constants.IN
: Constants.MM
const zeroSuppression = tokens
.filter(t => t.type === Lexer.DRILL_ZERO_INCLUSION)
.reduce<Types.ZeroSuppression | null>((z, t) => {
if (t.value === 'LZ') return Constants.TRAILING
if (t.value === 'TZ') return Constants.LEADING
return z
}, null)
const format = tokens
.filter(t => t.type === Lexer.NUMBER)
.reduce<Types.Format | null>((_, t) => {
const [integer = '', decimal = ''] = t.value.split('.')
return [integer.length, decimal.length]
}, null)
const nodes: Tree.ChildNode[] = [
{type: Tree.UNITS, position: tokensToPosition(tokens.slice(0, 2)), units},
]
if (zeroSuppression || format) {
nodes.push({
type: Tree.COORDINATE_FORMAT,
position: tokensToPosition(tokens.slice(1)),
mode: null,
format,
zeroSuppression,
})
}
return nodes
},
}
const tool: SyntaxRule = {
rules: [
token(Lexer.T_CODE),
minToMax(0, 12, [
token(Lexer.COORD_CHAR, 'C'),
token(Lexer.COORD_CHAR, 'F'),
token(Lexer.COORD_CHAR, 'S'),
token(Lexer.COORD_CHAR, 'B'),
token(Lexer.COORD_CHAR, 'H'),
token(Lexer.COORD_CHAR, 'Z'),
token(Lexer.NUMBER),
]),
token(Lexer.NEWLINE),
],
createNodes: tokens => {
const code = tokens[0].value
const position = tokensToPosition(tokens)
const {c = null} = tokensToCoordinates(tokens.slice(1, -1))
const shape: Types.ToolShape | null =
c !== null ? {type: Constants.CIRCLE, diameter: Number(c)} : null
return shape
? [{type: Tree.TOOL_DEFINITION, hole: null, position, shape, code}]
: [{type: Tree.TOOL_CHANGE, position, code}]
},
}
const mode: SyntaxRule = {
rules: [
one([
token(Lexer.G_CODE, '0'),
token(Lexer.G_CODE, '1'),
token(Lexer.G_CODE, '2'),
token(Lexer.G_CODE, '3'),
token(Lexer.G_CODE, '5'),
]),
token(Lexer.NEWLINE),
],
createNodes: tokens => [
{
type: Tree.INTERPOLATE_MODE,
position: tokensToPosition(tokens),
mode: tokensToMode(tokens),
},
],
}
const operation: SyntaxRule = {
rules: [
minToMax(0, 2, [
token(Lexer.T_CODE),
token(Lexer.G_CODE, '0'),
token(Lexer.G_CODE, '1'),
token(Lexer.G_CODE, '2'),
token(Lexer.G_CODE, '3'),
token(Lexer.G_CODE, '5'),
]),
minToMax(2, 8, [token(Lexer.COORD_CHAR), token(Lexer.NUMBER)]),
zeroOrOne([token(Lexer.T_CODE)]),
token(Lexer.NEWLINE),
],
createNodes: tokens => {
const graphicTokens = tokens.filter(
t => t.type === Lexer.COORD_CHAR || t.type === Lexer.NUMBER
)
const modeToken = tokens.find(t => t.type === Lexer.G_CODE)
const toolToken = tokens.find(t => t.type === Lexer.T_CODE)
const coordinates = tokensToCoordinates(graphicTokens)
const code = toolToken ? toolToken.value : null
const mode = tokensToMode(tokens)
const graphicPosition = tokensToPosition(tokens, {
head: graphicTokens[0],
length: graphicTokens.length + 1,
})
const modePosition = tokensToPosition(tokens, {head: modeToken, length: 2})
const toolPosition = tokensToPosition(tokens, {head: toolToken, length: 2})
const nodes: Tree.ChildNode[] = [
{
type: Tree.GRAPHIC,
position: graphicPosition,
graphic: null,
coordinates,
},
]
if (mode) {
nodes.unshift({type: Tree.INTERPOLATE_MODE, position: modePosition, mode})
}
if (code) {
nodes.unshift({type: Tree.TOOL_CHANGE, position: toolPosition, code})
}
return nodes
},
}
const slot: SyntaxRule = {
rules: [
minToMax(2, 4, [token(Lexer.COORD_CHAR), token(Lexer.NUMBER)]),
token(Lexer.G_CODE, '85'),
minToMax(2, 4, [token(Lexer.COORD_CHAR), token(Lexer.NUMBER)]),
token(Lexer.NEWLINE),
],
createNodes: tokens => {
const gCode = tokens.find(t => t.type === Lexer.G_CODE)
const splitIdx = gCode ? tokens.indexOf(gCode) : -1
const start = tokensToCoordinates(tokens.slice(0, splitIdx))
const end = tokensToCoordinates(tokens.slice(splitIdx))
const coordinates: Types.Coordinates = {}
Object.keys(start).forEach(k => (coordinates[`${k}1`] = start[k]))
Object.keys(end).forEach(k => (coordinates[`${k}2`] = end[k]))
return [
{
type: Tree.GRAPHIC,
position: tokensToPosition(tokens),
graphic: Constants.SLOT,
coordinates,
},
]
},
}
const done: SyntaxRule = {
rules: [
one([token(Lexer.M_CODE, '30'), token(Lexer.M_CODE, '0')]),
token(Lexer.NEWLINE),
],
createNodes: tokens => [
{type: Tree.DONE, position: tokensToPosition(tokens)},
],
}
const comment: SyntaxRule = {
rules: [
token(Lexer.SEMICOLON),
zeroOrMore([notToken(Lexer.NEWLINE)]),
token(Lexer.NEWLINE),
],
createNodes: tokens => [
{
type: Tree.COMMENT,
comment: tokensToString(tokens.slice(1, -1)),
position: tokensToPosition(tokens),
},
],
}
export const drillSyntax: Array<SyntaxRule> = [
tool,
mode,
operation,
slot,
comment,
units,
done,
].map(r => ({...r, filetype: Constants.DRILL}))