@tracespace/parser
Version:
Gerber and NC drill file parser
417 lines (410 loc) • 11.1 kB
text/typescript
// gerber grammar tests
import {expect} from 'chai'
import {toArray} from 'lodash'
import * as Lexer from '../../lexer'
import * as Tree from '../../tree'
import {
token as t,
position as pos,
simplifyToken,
} from '../../__tests__/helpers'
import {matchSyntax, MatchState} from '..'
import {
GERBER,
CIRCLE,
RECTANGLE,
OBROUND,
POLYGON,
MACRO_SHAPE,
} from '../../constants'
const SPECS: Array<{
source: string
expectedTokens: Lexer.Token[]
expectedNodes: Tree.ChildNode[]
}> = [
{
// simple circle tool
source: '%ADD10C,.025*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '10C'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '.025'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 13, 12]),
code: '10',
shape: {type: CIRCLE, diameter: 0.025},
hole: null,
},
],
},
{
// circle tool with circular hole
source: '%ADD11C,0.5X0.25*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '11C'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '0.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.25'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 17, 16]),
code: '11',
shape: {type: CIRCLE, diameter: 0.5},
hole: {type: CIRCLE, diameter: 0.25},
},
],
},
{
// circle tool with rectangular hole
source: '%ADD12C,10X5X5*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '12C'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '10'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '5'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 15, 14]),
code: '12',
shape: {type: CIRCLE, diameter: 10},
hole: {type: RECTANGLE, xSize: 5, ySize: 5},
},
],
},
{
// rectangle tool definition with no hole
source: '%ADD13R,0.5X0.6*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '13R'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '0.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.6'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 16, 15]),
code: '13',
shape: {type: RECTANGLE, xSize: 0.5, ySize: 0.6},
hole: null,
},
],
},
{
// rectangle tool definition with circle hole
source: '%ADD14R,0.5X0.6X0.2*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '14R'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '0.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.6'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.2'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 20, 19]),
code: '14',
shape: {type: RECTANGLE, xSize: 0.5, ySize: 0.6},
hole: {type: CIRCLE, diameter: 0.2},
},
],
},
{
// rectangle tool definition with rectangle hole
source: '%ADD15R,0.5X0.6X0.2X0.1*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '15R'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '0.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.6'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.2'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.1'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 24, 23]),
code: '15',
shape: {type: RECTANGLE, xSize: 0.5, ySize: 0.6},
hole: {type: RECTANGLE, xSize: 0.2, ySize: 0.1},
},
],
},
{
// obround tool definition with no hole
source: '%ADD16O,0.5X0.6*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '16O'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '0.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.6'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 16, 15]),
code: '16',
shape: {type: OBROUND, xSize: 0.5, ySize: 0.6},
hole: null,
},
],
},
{
// obround tool definition with circle hole
source: '%ADD17O,0.5X0.6X0.2*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '17O'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '0.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.6'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.2'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 20, 19]),
code: '17',
shape: {type: OBROUND, xSize: 0.5, ySize: 0.6},
hole: {type: CIRCLE, diameter: 0.2},
},
],
},
{
// obround tool definition with rectangle hole
source: '%ADD18O,0.5X0.6X0.2X0.1*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '18O'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '0.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.6'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.2'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.1'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 24, 23]),
code: '18',
shape: {type: OBROUND, xSize: 0.5, ySize: 0.6},
hole: {type: RECTANGLE, xSize: 0.2, ySize: 0.1},
},
],
},
{
// polygon tool definition with no rotation and no hole
source: '%ADD19P,1.5X3*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '19P'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '1.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '3'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 14, 13]),
code: '19',
shape: {type: POLYGON, diameter: 1.5, vertices: 3, rotation: null},
hole: null,
},
],
},
{
// polygon tool defintion with rotation and no hole
source: '%ADD20P,2.5X4X12.5*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '20P'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '2.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '4'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '12.5'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 19, 18]),
code: '20',
shape: {type: POLYGON, diameter: 2.5, vertices: 4, rotation: 12.5},
hole: null,
},
],
},
{
// polygon tool definition with rotation and circle hole
source: '%ADD21P,2.5X4X12.5X1*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '21P'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '2.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '4'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '12.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '1'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 21, 20]),
code: '21',
shape: {type: POLYGON, diameter: 2.5, vertices: 4, rotation: 12.5},
hole: {type: CIRCLE, diameter: 1},
},
],
},
{
// polygon tool definition with rotation and circle hole
source: '%ADD22P,2.5X4X12.5X1X1.5*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '22P'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '2.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '4'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '12.5'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '1'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '1.5'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 25, 24]),
code: '22',
shape: {type: POLYGON, diameter: 2.5, vertices: 4, rotation: 12.5},
hole: {type: RECTANGLE, xSize: 1, ySize: 1.5},
},
],
},
{
// macro tool definition with no parameters
source: '%ADD23MyMacro*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '23MyMacro'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 14, 13]),
code: '23',
shape: {type: MACRO_SHAPE, name: 'MyMacro', params: []},
hole: null,
},
],
},
{
// macro tool definition with parameters
source: '%ADD24MyMacro,0.1X0.2X0.3*%',
expectedTokens: [
t(Lexer.PERCENT, '%'),
t(Lexer.GERBER_TOOL_DEF, '24MyMacro'),
t(Lexer.COMMA, ','),
t(Lexer.NUMBER, '0.1'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.2'),
t(Lexer.COORD_CHAR, 'X'),
t(Lexer.NUMBER, '0.3'),
t(Lexer.ASTERISK, '*'),
t(Lexer.PERCENT, '%'),
],
expectedNodes: [
{
type: Tree.TOOL_DEFINITION,
position: pos([1, 2, 1], [1, 26, 25]),
code: '24',
shape: {type: MACRO_SHAPE, name: 'MyMacro', params: [0.1, 0.2, 0.3]},
hole: null,
},
],
},
]
describe('gerber tool syntax matches', () => {
SPECS.forEach(({source, expectedTokens, expectedNodes}) => {
it(`should match on ${source.trim()}`, () => {
const lexer = Lexer.createLexer()
lexer.reset(source)
const actualTokens = toArray((lexer as unknown) as Array<Lexer.Token>)
const {tokens, nodes, filetype} = actualTokens.reduce<MatchState>(
(state, token) => matchSyntax(state, token),
null
)
expect(filetype).to.equal(GERBER)
expect(nodes).to.eql(expectedNodes)
expect(tokens.map(simplifyToken)).to.eql(
expectedTokens.map(simplifyToken)
)
})
})
})