* Send multiple errors and warnings to the frontend and LSP Signed-off-by: Nick Cameron <nrc@ncameron.org> * Refactor the parser to use CompilationError for parsing errors rather than KclError Signed-off-by: Nick Cameron <nrc@ncameron.org> * Refactoring: move CompilationError, etc. Signed-off-by: Nick Cameron <nrc@ncameron.org> * Integrate compilation errors with the frontend and CodeMirror Signed-off-by: Nick Cameron <nrc@ncameron.org> * Fix tests Signed-off-by: Nick Cameron <nrc@ncameron.org> * Review comments Signed-off-by: Nick Cameron <nrc@ncameron.org> * Fix module id/source range stuff Signed-off-by: Nick Cameron <nrc@ncameron.org> * More test fixups Signed-off-by: Nick Cameron <nrc@ncameron.org> --------- Signed-off-by: Nick Cameron <nrc@ncameron.org>
629 lines
23 KiB
TypeScript
629 lines
23 KiB
TypeScript
import { assertParse, Expr, recast, initPromise, Program } from '../wasm'
|
|
import {
|
|
getConstraintType,
|
|
getTransformInfos,
|
|
transformAstSketchLines,
|
|
transformSecondarySketchLinesTagFirst,
|
|
ConstraintType,
|
|
ConstraintLevel,
|
|
getConstraintLevelFromSourceRange,
|
|
} from './sketchcombos'
|
|
import { ToolTip } from 'lang/langHelpers'
|
|
import { Selections, Selection } from 'lib/selections'
|
|
import { err } from 'lib/trap'
|
|
import { enginelessExecutor } from '../../lib/testHelpers'
|
|
import { codeRefFromRange } from './artifactGraph'
|
|
|
|
beforeAll(async () => {
|
|
await initPromise
|
|
})
|
|
|
|
describe('testing getConstraintType', () => {
|
|
const helper = getConstraintTypeFromSourceHelper
|
|
it('testing line', () => {
|
|
expect(helper(`line([5, myVar], %)`)).toBe('yRelative')
|
|
expect(helper(`line([myVar, 5], %)`)).toBe('xRelative')
|
|
})
|
|
it('testing lineTo', () => {
|
|
expect(helper(`lineTo([5, myVar], %)`)).toBe('yAbsolute')
|
|
expect(helper(`lineTo([myVar, 5], %)`)).toBe('xAbsolute')
|
|
})
|
|
it('testing angledLine', () => {
|
|
expect(helper(`angledLine([5, myVar], %)`)).toBe('length')
|
|
expect(helper(`angledLine([myVar, 5], %)`)).toBe('angle')
|
|
})
|
|
it('testing angledLineOfXLength', () => {
|
|
expect(helper(`angledLineOfXLength([5, myVar], %)`)).toBe('xRelative')
|
|
expect(helper(`angledLineOfXLength([myVar, 5], %)`)).toBe('angle')
|
|
})
|
|
it('testing angledLineToX', () => {
|
|
expect(helper(`angledLineToX([5, myVar], %)`)).toBe('xAbsolute')
|
|
expect(helper(`angledLineToX([myVar, 5], %)`)).toBe('angle')
|
|
})
|
|
it('testing angledLineOfYLength', () => {
|
|
expect(helper(`angledLineOfYLength([5, myVar], %)`)).toBe('yRelative')
|
|
expect(helper(`angledLineOfYLength([myVar, 5], %)`)).toBe('angle')
|
|
})
|
|
it('testing angledLineToY', () => {
|
|
expect(helper(`angledLineToY([5, myVar], %)`)).toBe('yAbsolute')
|
|
expect(helper(`angledLineToY([myVar, 5], %)`)).toBe('angle')
|
|
})
|
|
const helper2 = getConstraintTypeFromSourceHelper2
|
|
it('testing xLine', () => {
|
|
expect(helper2(`xLine(5, %)`)).toBe('yRelative')
|
|
})
|
|
it('testing yLine', () => {
|
|
expect(helper2(`yLine(5, %)`)).toBe('xRelative')
|
|
})
|
|
it('testing xLineTo', () => {
|
|
expect(helper2(`xLineTo(5, %)`)).toBe('yAbsolute')
|
|
})
|
|
it('testing yLineTo', () => {
|
|
expect(helper2(`yLineTo(5, %)`)).toBe('xAbsolute')
|
|
})
|
|
})
|
|
|
|
function getConstraintTypeFromSourceHelper(
|
|
code: string
|
|
): ReturnType<typeof getConstraintType> | Error {
|
|
const ast = assertParse(code)
|
|
|
|
const args = (ast.body[0] as any).expression.arguments[0].elements as [
|
|
Expr,
|
|
Expr
|
|
]
|
|
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
|
return getConstraintType(args, fnName)
|
|
}
|
|
function getConstraintTypeFromSourceHelper2(
|
|
code: string
|
|
): ReturnType<typeof getConstraintType> | Error {
|
|
const ast = assertParse(code)
|
|
|
|
const arg = (ast.body[0] as any).expression.arguments[0] as Expr
|
|
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
|
return getConstraintType(arg, fnName)
|
|
}
|
|
|
|
function makeSelections(
|
|
graphSelections: Selections['graphSelections']
|
|
): Selections {
|
|
return {
|
|
graphSelections: graphSelections,
|
|
otherSelections: [],
|
|
}
|
|
}
|
|
|
|
describe('testing transformAstForSketchLines for equal length constraint', () => {
|
|
describe(`should always reorder selections to have the base selection first`, () => {
|
|
const inputScript = `sketch001 = startSketchOn('XZ')
|
|
|> startProfileAt([0, 0], %)
|
|
|> line([5, 5], %)
|
|
|> line([-2, 5], %)
|
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
|> close(%)`
|
|
|
|
const expectedModifiedScript = `sketch001 = startSketchOn('XZ')
|
|
|> startProfileAt([0, 0], %)
|
|
|> line([5, 5], %, $seg01)
|
|
|> angledLine([112, segLen(seg01)], %)
|
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
|> close(%)
|
|
`
|
|
|
|
const selectLine = (
|
|
script: string,
|
|
lineNumber: number,
|
|
ast: Program
|
|
): Selection => {
|
|
const lines = script.split('\n')
|
|
const codeBeforeLine = lines.slice(0, lineNumber).join('\n').length
|
|
const line = lines.find((_, i) => i === lineNumber)
|
|
if (!line) {
|
|
throw new Error(
|
|
`line index ${lineNumber} not found in test sample, friend`
|
|
)
|
|
}
|
|
const start = codeBeforeLine + line.indexOf('|> ' + 5)
|
|
const range: [number, number, boolean] = [start, start, true]
|
|
return {
|
|
codeRef: codeRefFromRange(range, ast),
|
|
}
|
|
}
|
|
|
|
async function applyTransformation(
|
|
inputCode: string,
|
|
selectionRanges: Selections['graphSelections']
|
|
) {
|
|
const ast = assertParse(inputCode)
|
|
const execState = await enginelessExecutor(ast)
|
|
const transformInfos = getTransformInfos(
|
|
makeSelections(selectionRanges.slice(1)),
|
|
ast,
|
|
'equalLength'
|
|
)
|
|
|
|
const transformedSelection = makeSelections(selectionRanges)
|
|
|
|
const newAst = transformSecondarySketchLinesTagFirst({
|
|
ast,
|
|
selectionRanges: transformedSelection,
|
|
transformInfos,
|
|
programMemory: execState.memory,
|
|
})
|
|
if (err(newAst)) return Promise.reject(newAst)
|
|
|
|
const newCode = recast(newAst.modifiedAst)
|
|
return newCode
|
|
}
|
|
|
|
it(`Should reorder when user selects first-to-last`, async () => {
|
|
const ast = assertParse(inputScript)
|
|
const selectionRanges: Selections['graphSelections'] = [
|
|
selectLine(inputScript, 3, ast),
|
|
selectLine(inputScript, 4, ast),
|
|
]
|
|
|
|
const newCode = await applyTransformation(inputScript, selectionRanges)
|
|
expect(newCode).toBe(expectedModifiedScript)
|
|
})
|
|
|
|
it(`Should reorder when user selects last-to-first`, async () => {
|
|
const ast = assertParse(inputScript)
|
|
const selectionRanges: Selections['graphSelections'] = [
|
|
selectLine(inputScript, 4, ast),
|
|
selectLine(inputScript, 3, ast),
|
|
]
|
|
|
|
const newCode = await applyTransformation(inputScript, selectionRanges)
|
|
expect(newCode).toBe(expectedModifiedScript)
|
|
})
|
|
})
|
|
const inputScript = `myVar = 3
|
|
myVar2 = 5
|
|
myVar3 = 6
|
|
myAng = 40
|
|
myAng2 = 134
|
|
part001 = startSketchOn('XY')
|
|
|> startProfileAt([0, 0], %)
|
|
|> line([1, 3.82], %) // ln-should-get-tag
|
|
|> lineTo([myVar, 1], %) // ln-lineTo-xAbsolute should use angleToMatchLengthX helper
|
|
|> lineTo([1, myVar], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper
|
|
|> lineTo([2, 4], %) // ln-lineTo-free should become angledLine
|
|
|> angledLineToX([45, 2.5], %) // ln-angledLineToX-free should become angledLine
|
|
|> angledLineToX([myAng, 3], %) // ln-angledLineToX-angle should become angledLine
|
|
|> angledLineToX([45, myVar2], %) // ln-angledLineToX-xAbsolute should use angleToMatchLengthX to get angle
|
|
|> angledLineToY([135, 5], %) // ln-angledLineToY-free should become angledLine
|
|
|> angledLineToY([myAng2, 4], %) // ln-angledLineToY-angle should become angledLine
|
|
|> angledLineToY([45, myVar3], %) // ln-angledLineToY-yAbsolute should use angleToMatchLengthY to get angle
|
|
|> line([myVar, 1], %) // ln-should use legLen for y
|
|
|> line([myVar, -1], %) // ln-legLen but negative
|
|
|> line([-0.62, -1.54], %) // ln-should become angledLine
|
|
|> angledLine([myVar, 1.04], %) // ln-use segLen for second arg
|
|
|> angledLine([45, 1.04], %) // ln-segLen again
|
|
|> angledLineOfXLength([54, 2.35], %) // ln-should be transformed to angledLine
|
|
|> angledLineOfXLength([50, myVar], %) // ln-should use legAngX to calculate angle
|
|
|> angledLineOfXLength([209, myVar], %) // ln-same as above but should have + 180 to match original quadrant
|
|
|> line([1, myVar], %) // ln-legLen again but yRelative
|
|
|> line([-1, myVar], %) // ln-negative legLen yRelative
|
|
|> angledLineOfYLength([58, 0.7], %) // ln-angledLineOfYLength-free should become angledLine
|
|
|> angledLineOfYLength([myAng, 0.7], %) // ln-angledLineOfYLength-angle should become angledLine
|
|
|> angledLineOfYLength([35, myVar], %) // ln-angledLineOfYLength-yRelative use legAngY
|
|
|> angledLineOfYLength([305, myVar], %) // ln-angledLineOfYLength-yRelative with angle > 90 use binExp
|
|
|> xLine(1.03, %) // ln-xLine-free should sub in segLen
|
|
|> yLine(1.04, %) // ln-yLine-free should sub in segLen
|
|
|> xLineTo(30, %) // ln-xLineTo-free should convert to xLine
|
|
|> yLineTo(20, %) // ln-yLineTo-free should convert to yLine
|
|
`
|
|
const expectModifiedScript = `myVar = 3
|
|
myVar2 = 5
|
|
myVar3 = 6
|
|
myAng = 40
|
|
myAng2 = 134
|
|
part001 = startSketchOn('XY')
|
|
|> startProfileAt([0, 0], %)
|
|
|> line([1, 3.82], %, $seg01) // ln-should-get-tag
|
|
|> angledLineToX([
|
|
-angleToMatchLengthX(seg01, myVar, %),
|
|
myVar
|
|
], %) // ln-lineTo-xAbsolute should use angleToMatchLengthX helper
|
|
|> angledLineToY([
|
|
-angleToMatchLengthY(seg01, myVar, %),
|
|
myVar
|
|
], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper
|
|
|> angledLine([45, segLen(seg01)], %) // ln-lineTo-free should become angledLine
|
|
|> angledLine([45, segLen(seg01)], %) // ln-angledLineToX-free should become angledLine
|
|
|> angledLine([myAng, segLen(seg01)], %) // ln-angledLineToX-angle should become angledLine
|
|
|> angledLineToX([
|
|
angleToMatchLengthX(seg01, myVar2, %),
|
|
myVar2
|
|
], %) // ln-angledLineToX-xAbsolute should use angleToMatchLengthX to get angle
|
|
|> angledLine([-45, segLen(seg01)], %) // ln-angledLineToY-free should become angledLine
|
|
|> angledLine([myAng2, segLen(seg01)], %) // ln-angledLineToY-angle should become angledLine
|
|
|> angledLineToY([
|
|
angleToMatchLengthY(seg01, myVar3, %),
|
|
myVar3
|
|
], %) // ln-angledLineToY-yAbsolute should use angleToMatchLengthY to get angle
|
|
|> line([
|
|
min(segLen(seg01), myVar),
|
|
legLen(segLen(seg01), myVar)
|
|
], %) // ln-should use legLen for y
|
|
|> line([
|
|
min(segLen(seg01), myVar),
|
|
-legLen(segLen(seg01), myVar)
|
|
], %) // ln-legLen but negative
|
|
|> angledLine([-112, segLen(seg01)], %) // ln-should become angledLine
|
|
|> angledLine([myVar, segLen(seg01)], %) // ln-use segLen for second arg
|
|
|> angledLine([45, segLen(seg01)], %) // ln-segLen again
|
|
|> angledLine([54, segLen(seg01)], %) // ln-should be transformed to angledLine
|
|
|> angledLineOfXLength([
|
|
legAngX(segLen(seg01), myVar),
|
|
min(segLen(seg01), myVar)
|
|
], %) // ln-should use legAngX to calculate angle
|
|
|> angledLineOfXLength([
|
|
180 + legAngX(segLen(seg01), myVar),
|
|
min(segLen(seg01), myVar)
|
|
], %) // ln-same as above but should have + 180 to match original quadrant
|
|
|> line([
|
|
legLen(segLen(seg01), myVar),
|
|
min(segLen(seg01), myVar)
|
|
], %) // ln-legLen again but yRelative
|
|
|> line([
|
|
-legLen(segLen(seg01), myVar),
|
|
min(segLen(seg01), myVar)
|
|
], %) // ln-negative legLen yRelative
|
|
|> angledLine([58, segLen(seg01)], %) // ln-angledLineOfYLength-free should become angledLine
|
|
|> angledLine([myAng, segLen(seg01)], %) // ln-angledLineOfYLength-angle should become angledLine
|
|
|> angledLineOfXLength([
|
|
legAngY(segLen(seg01), myVar),
|
|
min(segLen(seg01), myVar)
|
|
], %) // ln-angledLineOfYLength-yRelative use legAngY
|
|
|> angledLineOfXLength([
|
|
270 + legAngY(segLen(seg01), myVar),
|
|
min(segLen(seg01), myVar)
|
|
], %) // ln-angledLineOfYLength-yRelative with angle > 90 use binExp
|
|
|> xLine(segLen(seg01), %) // ln-xLine-free should sub in segLen
|
|
|> yLine(segLen(seg01), %) // ln-yLine-free should sub in segLen
|
|
|> xLine(segLen(seg01), %) // ln-xLineTo-free should convert to xLine
|
|
|> yLine(segLen(seg01), %) // ln-yLineTo-free should convert to yLine
|
|
`
|
|
it('should transform the ast', async () => {
|
|
const ast = assertParse(inputScript)
|
|
|
|
const selectionRanges: Selections['graphSelections'] = inputScript
|
|
.split('\n')
|
|
.filter((ln) => ln.includes('//'))
|
|
.map((ln) => {
|
|
const comment = ln.split('//')[1]
|
|
const start = inputScript.indexOf('//' + comment) - 7
|
|
return {
|
|
codeRef: codeRefFromRange([start, start, true], ast),
|
|
}
|
|
})
|
|
|
|
const execState = await enginelessExecutor(ast)
|
|
const transformInfos = getTransformInfos(
|
|
makeSelections(selectionRanges.slice(1)),
|
|
ast,
|
|
'equalLength'
|
|
)
|
|
|
|
const newAst = transformSecondarySketchLinesTagFirst({
|
|
ast,
|
|
selectionRanges: makeSelections(selectionRanges),
|
|
transformInfos,
|
|
programMemory: execState.memory,
|
|
})
|
|
if (err(newAst)) return Promise.reject(newAst)
|
|
|
|
const newCode = recast(newAst.modifiedAst)
|
|
expect(newCode).toBe(expectModifiedScript)
|
|
})
|
|
})
|
|
|
|
describe('testing transformAstForSketchLines for vertical and horizontal constraint', () => {
|
|
const inputScript = `myVar = 2
|
|
myVar2 = 12
|
|
myVar3 = -10
|
|
part001 = startSketchOn('XY')
|
|
|> startProfileAt([0, 0], %)
|
|
|> lineTo([1, 1], %)
|
|
|> line([-6.28, 1.4], %) // select for horizontal constraint 1
|
|
|> line([-1.07, myVar], %) // select for vertical constraint 1
|
|
|> line([myVar, 4.32], %) // select for horizontal constraint 2
|
|
|> line([6.35, -1.12], %) // select for vertical constraint 2
|
|
|> lineTo([5, 8], %) // select for horizontal constraint 3
|
|
|> lineTo([3, 11], %) // select for vertical constraint 3
|
|
|> lineTo([myVar2, 12.63], %) // select for horizontal constraint 4
|
|
|> lineTo([4.08, myVar2], %) // select for vertical constraint 4
|
|
|> angledLine([156, 1.34], %) // select for horizontal constraint 5
|
|
|> angledLine([103, 1.44], %) // select for vertical constraint 5
|
|
|> angledLine([-178, myVar], %) // select for horizontal constraint 6
|
|
|> angledLine([129, myVar], %) // select for vertical constraint 6
|
|
|> angledLineOfXLength([237, 1.05], %) // select for horizontal constraint 7
|
|
|> angledLineOfYLength([196, 1.11], %) // select for vertical constraint 7
|
|
|> angledLineOfXLength([194, myVar], %) // select for horizontal constraint 8
|
|
|> angledLineOfYLength([248, myVar], %) // select for vertical constraint 8
|
|
|> angledLineToX([202, -10.92], %) // select for horizontal constraint 9
|
|
|> angledLineToY([223, 7.68], %) // select for vertical constraint 9
|
|
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
|
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
|
`
|
|
it('should transform horizontal lines the ast', async () => {
|
|
const expectModifiedScript = `myVar = 2
|
|
myVar2 = 12
|
|
myVar3 = -10
|
|
part001 = startSketchOn('XY')
|
|
|> startProfileAt([0, 0], %)
|
|
|> lineTo([1, 1], %)
|
|
|> xLine(-6.28, %) // select for horizontal constraint 1
|
|
|> line([-1.07, myVar], %) // select for vertical constraint 1
|
|
|> xLine(myVar, %) // select for horizontal constraint 2
|
|
|> line([6.35, -1.12], %) // select for vertical constraint 2
|
|
|> xLineTo(5, %) // select for horizontal constraint 3
|
|
|> lineTo([3, 11], %) // select for vertical constraint 3
|
|
|> xLineTo(myVar2, %) // select for horizontal constraint 4
|
|
|> lineTo([4.08, myVar2], %) // select for vertical constraint 4
|
|
|> xLine(-1.22, %) // select for horizontal constraint 5
|
|
|> angledLine([103, 1.44], %) // select for vertical constraint 5
|
|
|> xLine(-myVar, %) // select for horizontal constraint 6
|
|
|> angledLine([129, myVar], %) // select for vertical constraint 6
|
|
|> xLine(-1.05, %) // select for horizontal constraint 7
|
|
|> angledLineOfYLength([196, 1.11], %) // select for vertical constraint 7
|
|
|> xLine(-myVar, %) // select for horizontal constraint 8
|
|
|> angledLineOfYLength([248, myVar], %) // select for vertical constraint 8
|
|
|> xLineTo(-10.92, %) // select for horizontal constraint 9
|
|
|> angledLineToY([223, 7.68], %) // select for vertical constraint 9
|
|
|> xLineTo(myVar3, %) // select for horizontal constraint 10
|
|
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
|
`
|
|
const ast = assertParse(inputScript)
|
|
|
|
const selectionRanges: Selections['graphSelections'] = inputScript
|
|
.split('\n')
|
|
.filter((ln) => ln.includes('// select for horizontal constraint'))
|
|
.map((ln) => {
|
|
const comment = ln.split('//')[1]
|
|
const start = inputScript.indexOf('//' + comment) - 7
|
|
return {
|
|
codeRef: codeRefFromRange([start, start, true], ast),
|
|
}
|
|
})
|
|
|
|
const execState = await enginelessExecutor(ast)
|
|
const transformInfos = getTransformInfos(
|
|
makeSelections(selectionRanges),
|
|
ast,
|
|
'horizontal'
|
|
)
|
|
|
|
const newAst = transformAstSketchLines({
|
|
ast,
|
|
selectionRanges: makeSelections(selectionRanges),
|
|
transformInfos,
|
|
programMemory: execState.memory,
|
|
referenceSegName: '',
|
|
})
|
|
if (err(newAst)) return Promise.reject(newAst)
|
|
|
|
const newCode = recast(newAst.modifiedAst)
|
|
expect(newCode).toBe(expectModifiedScript)
|
|
})
|
|
it('should transform vertical lines the ast', async () => {
|
|
const expectModifiedScript = `myVar = 2
|
|
myVar2 = 12
|
|
myVar3 = -10
|
|
part001 = startSketchOn('XY')
|
|
|> startProfileAt([0, 0], %)
|
|
|> lineTo([1, 1], %)
|
|
|> line([-6.28, 1.4], %) // select for horizontal constraint 1
|
|
|> yLine(myVar, %) // select for vertical constraint 1
|
|
|> line([myVar, 4.32], %) // select for horizontal constraint 2
|
|
|> yLine(-1.12, %) // select for vertical constraint 2
|
|
|> lineTo([5, 8], %) // select for horizontal constraint 3
|
|
|> yLineTo(11, %) // select for vertical constraint 3
|
|
|> lineTo([myVar2, 12.63], %) // select for horizontal constraint 4
|
|
|> yLineTo(myVar2, %) // select for vertical constraint 4
|
|
|> angledLine([156, 1.34], %) // select for horizontal constraint 5
|
|
|> yLine(1.4, %) // select for vertical constraint 5
|
|
|> angledLine([-178, myVar], %) // select for horizontal constraint 6
|
|
|> yLine(myVar, %) // select for vertical constraint 6
|
|
|> angledLineOfXLength([237, 1.05], %) // select for horizontal constraint 7
|
|
|> yLine(-1.11, %) // select for vertical constraint 7
|
|
|> angledLineOfXLength([194, myVar], %) // select for horizontal constraint 8
|
|
|> yLine(-myVar, %) // select for vertical constraint 8
|
|
|> angledLineToX([202, -10.92], %) // select for horizontal constraint 9
|
|
|> yLineTo(7.68, %) // select for vertical constraint 9
|
|
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
|
|> yLineTo(myVar, %) // select for vertical constraint 10
|
|
`
|
|
const ast = assertParse(inputScript)
|
|
|
|
const selectionRanges: Selections['graphSelections'] = inputScript
|
|
.split('\n')
|
|
.filter((ln) => ln.includes('// select for vertical constraint'))
|
|
.map((ln) => {
|
|
const comment = ln.split('//')[1]
|
|
const start = inputScript.indexOf('//' + comment) - 7
|
|
return {
|
|
codeRef: codeRefFromRange([start, start, true], ast),
|
|
}
|
|
})
|
|
|
|
const execState = await enginelessExecutor(ast)
|
|
const transformInfos = getTransformInfos(
|
|
makeSelections(selectionRanges),
|
|
ast,
|
|
'vertical'
|
|
)
|
|
|
|
const newAst = transformAstSketchLines({
|
|
ast,
|
|
selectionRanges: makeSelections(selectionRanges),
|
|
transformInfos,
|
|
programMemory: execState.memory,
|
|
referenceSegName: '',
|
|
})
|
|
if (err(newAst)) return Promise.reject(newAst)
|
|
|
|
const newCode = recast(newAst.modifiedAst)
|
|
expect(newCode).toBe(expectModifiedScript)
|
|
})
|
|
})
|
|
|
|
describe('testing transformAstForSketchLines for vertical and horizontal distance constraints', () => {
|
|
describe('testing setHorzDistance for line', () => {
|
|
const inputScript = `myVar = 1
|
|
part001 = startSketchOn('XY')
|
|
|> startProfileAt([0, 0], %)
|
|
|> line([0.31, 1.67], %) // base selection
|
|
|> line([0.45, 1.46], %)
|
|
|> line([0.45, 1.46], %) // free
|
|
|> line([myVar, 0.01], %) // xRelative
|
|
|> line([0.7, myVar], %) // yRelative
|
|
`
|
|
it('testing for free to horizontal and vertical distance', async () => {
|
|
const expectedHorizontalCode = await helperThing(
|
|
inputScript,
|
|
['// base selection', '// free'],
|
|
'setHorzDistance'
|
|
)
|
|
const expectedVerticalCode = await helperThing(
|
|
inputScript,
|
|
['// base selection', '// free'],
|
|
'setVertDistance'
|
|
)
|
|
expect(expectedHorizontalCode).toContain(
|
|
`lineTo([segEndX(seg01) + 0.9, 4.59], %) // free`
|
|
)
|
|
expect(expectedVerticalCode).toContain(
|
|
`lineTo([1.21, segEndY(seg01) + 2.92], %) // free`
|
|
)
|
|
})
|
|
it('testing for xRelative to vertical distance', async () => {
|
|
const expectedCode = await helperThing(
|
|
inputScript,
|
|
['// base selection', '// xRelative'],
|
|
'setVertDistance'
|
|
)
|
|
expect(expectedCode).toContain(`|> lineTo([
|
|
lastSegX(%) + myVar,
|
|
segEndY(seg01) + 2.93
|
|
], %) // xRelative`)
|
|
})
|
|
it('testing for yRelative to horizontal distance', async () => {
|
|
const expectedCode = await helperThing(
|
|
inputScript,
|
|
['// base selection', '// yRelative'],
|
|
'setHorzDistance'
|
|
)
|
|
expect(expectedCode).toContain(`|> lineTo([
|
|
segEndX(seg01) + 2.6,
|
|
lastSegY(%) + myVar
|
|
], %) // yRelative`)
|
|
})
|
|
})
|
|
})
|
|
|
|
async function helperThing(
|
|
inputScript: string,
|
|
linesOfInterest: string[],
|
|
constraint: ConstraintType
|
|
): Promise<string> {
|
|
const ast = assertParse(inputScript)
|
|
|
|
const selectionRanges: Selections['graphSelections'] = inputScript
|
|
.split('\n')
|
|
.filter((ln) =>
|
|
linesOfInterest.some((lineOfInterest) => ln.includes(lineOfInterest))
|
|
)
|
|
.map((ln) => {
|
|
const comment = ln.split('//')[1]
|
|
const start = inputScript.indexOf('//' + comment) - 7
|
|
return {
|
|
codeRef: codeRefFromRange([start, start, true], ast),
|
|
}
|
|
})
|
|
|
|
const execState = await enginelessExecutor(ast)
|
|
const transformInfos = getTransformInfos(
|
|
makeSelections(selectionRanges.slice(1)),
|
|
ast,
|
|
constraint
|
|
)
|
|
|
|
const newAst = transformSecondarySketchLinesTagFirst({
|
|
ast,
|
|
selectionRanges: makeSelections(selectionRanges),
|
|
transformInfos,
|
|
programMemory: execState.memory,
|
|
})
|
|
|
|
if (err(newAst)) return Promise.reject(newAst)
|
|
const recasted = recast(newAst.modifiedAst)
|
|
|
|
if (err(recasted)) return Promise.reject(recasted)
|
|
return recasted
|
|
}
|
|
|
|
describe('testing getConstraintLevelFromSourceRange', () => {
|
|
it('should divide up lines into free, partial and fully contrained', () => {
|
|
const code = `const baseLength = 3
|
|
const baseThick = 1
|
|
const armThick = 0.5
|
|
const totalHeight = 4
|
|
const armAngle = 60
|
|
const totalLength = 9.74
|
|
const yDatum = 0
|
|
|
|
const baseThickHalf = baseThick / 2
|
|
const halfHeight = totalHeight / 2
|
|
const halfArmAngle = armAngle / 2
|
|
|
|
part001 = startSketchOn('XY')
|
|
|> startProfileAt([-0.01, -0.05], %)
|
|
|> line([0.01, 0.94 + 0], %) // partial
|
|
|> xLine(3.03, %) // partial
|
|
|> angledLine({
|
|
angle: halfArmAngle,
|
|
length: 2.45,
|
|
}, %, $seg01bing) // partial
|
|
|> xLine(4.4, %) // partial
|
|
|> yLine(-1, %) // partial
|
|
|> xLine(-4.2 + 0, %) // full
|
|
|> angledLine([segAng(seg01bing) + 180, 1.79], %) // partial
|
|
|> line([1.44, -0.74], %) // free
|
|
|> xLine(3.36, %) // partial
|
|
|> line([-1.49, 1.06], %) // free
|
|
|> xLine(-3.43 + 0, %) // full
|
|
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full`
|
|
const ast = assertParse(code)
|
|
const constraintLevels: ConstraintLevel[] = ['full', 'partial', 'free']
|
|
constraintLevels.forEach((constraintLevel) => {
|
|
const recursivelySearchCommentsAndCheckConstraintLevel = (
|
|
str: string,
|
|
offset: number = 0
|
|
): null => {
|
|
const index = str.indexOf(`// ${constraintLevel}`, offset)
|
|
if (index === -1) {
|
|
return null
|
|
}
|
|
const offsetIndex = index - 7
|
|
const expectedConstraintLevel = getConstraintLevelFromSourceRange(
|
|
[offsetIndex, offsetIndex, true],
|
|
ast
|
|
)
|
|
if (err(expectedConstraintLevel)) {
|
|
throw expectedConstraintLevel
|
|
}
|
|
expect(expectedConstraintLevel.level).toBe(constraintLevel)
|
|
return recursivelySearchCommentsAndCheckConstraintLevel(
|
|
str,
|
|
index + constraintLevel.length
|
|
)
|
|
}
|
|
recursivelySearchCommentsAndCheckConstraintLevel(code)
|
|
})
|
|
})
|
|
})
|