Make dot-get work in the compiler AND with parens exprs

This commit is contained in:
Corey Johnson 2025-10-28 10:11:52 -07:00
parent bbc9316074
commit a1693078f9
8 changed files with 74 additions and 32 deletions

View File

@ -94,7 +94,6 @@ export class Compiler {
#compileNode(node: SyntaxNode, input: string): ProgramItem[] { #compileNode(node: SyntaxNode, input: string): ProgramItem[] {
const value = input.slice(node.from, node.to) const value = input.slice(node.from, node.to)
if (DEBUG) console.log(`🫦 ${node.name}: ${value}`) if (DEBUG) console.log(`🫦 ${node.name}: ${value}`)
switch (node.type.id) { switch (node.type.id) {
@ -190,10 +189,15 @@ export class Compiler {
} }
case terms.DotGet: { case terms.DotGet: {
const { objectName, propertyName } = getDotGetParts(node, input) const { objectName, property } = getDotGetParts(node, input)
const instructions: ProgramItem[] = [] const instructions: ProgramItem[] = []
instructions.push(['TRY_LOAD', objectName]) instructions.push(['TRY_LOAD', objectName])
instructions.push(['PUSH', propertyName]) if (property.type.id === terms.ParenExpr) {
instructions.push(...this.#compileNode(property, input))
} else {
const propertyValue = input.slice(property.from, property.to)
instructions.push(['PUSH', propertyValue])
}
instructions.push(['DOT_GET']) instructions.push(['DOT_GET'])
return instructions return instructions
} }
@ -265,6 +269,10 @@ export class Compiler {
} }
case terms.FunctionCallOrIdentifier: { case terms.FunctionCallOrIdentifier: {
if (node.firstChild?.type.id === terms.DotGet) {
return this.#compileNode(node.firstChild, input)
}
return [['TRY_CALL', value]] return [['TRY_CALL', value]]
} }

View File

@ -96,8 +96,7 @@ describe('compiler', () => {
end end
abc abc
`) `).toEvaluateTo(true)
.toEvaluateTo(true)
}) })
test('simple conditionals', () => { test('simple conditionals', () => {
@ -238,3 +237,20 @@ describe('native functions', () => {
expect(`add 5 9`).toEvaluateTo(14, { add }) expect(`add 5 9`).toEvaluateTo(14, { add })
}) })
}) })
describe('dot get', () => {
const array = (...items: any) => items
const dict = (atNamed: any) => atNamed
test('access array element', () => {
expect(`arr = array 'a' 'b' 'c'; arr.1`).toEvaluateTo('b', { array })
})
test('access dict element', () => {
expect(`dict = dict a=1 b=2; dict.a`).toEvaluateTo(1, { dict })
})
test('use parens expr with dot-get', () => {
expect(`a = 1; arr = array 'a' 'b' 'c'; arr.(1 + a)`).toEvaluateTo('c', { array })
})
})

View File

@ -203,7 +203,7 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => {
const children = getAllChildren(node) const children = getAllChildren(node)
const [object, property] = children const [object, property] = children
if (children.length !== 2) { if (!object || !property) {
throw new CompilerError( throw new CompilerError(
`DotGet expected 2 identifier children, got ${children.length}`, `DotGet expected 2 identifier children, got ${children.length}`,
node.from, node.from,
@ -219,7 +219,7 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => {
) )
} }
if (property.type.id !== terms.Identifier && property.type.id !== terms.Number) { if (![terms.Identifier, terms.Number, terms.ParenExpr].includes(property.type.id)) {
throw new CompilerError( throw new CompilerError(
`DotGet property must be an Identifier or Number, got ${property.type.name}`, `DotGet property must be an Identifier or Number, got ${property.type.name}`,
property.from, property.from,
@ -228,7 +228,6 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => {
} }
const objectName = input.slice(object.from, object.to) const objectName = input.slice(object.from, object.to)
const propertyName = input.slice(property.from, property.to)
return { objectName, propertyName } return { objectName, property }
} }

View File

@ -32,7 +32,6 @@ export const Editor = () => {
}) })
multilineModeSignal.connect((isMultiline) => { multilineModeSignal.connect((isMultiline) => {
console.log(`🌭 hey babe`, isMultiline)
view.dispatch({ view.dispatch({
effects: lineNumbersCompartment.reconfigure(isMultiline ? lineNumbers() : []), effects: lineNumbersCompartment.reconfigure(isMultiline ? lineNumbers() : []),
}) })

View File

@ -169,7 +169,7 @@ expression {
@skip {} { @skip {} {
DotGet { DotGet {
IdentifierBeforeDot dot (Number | Identifier) IdentifierBeforeDot dot (Number | Identifier | ParenExpr)
} }
String { "'" stringContent* "'" } String { "'" stringContent* "'" }

View File

@ -7,9 +7,9 @@ import {highlighting} from "./highlight"
const spec_Identifier = {__proto__:null,null:64, end:74, if:88, elseif:96, else:100} const spec_Identifier = {__proto__:null,null:64, end:74, if:88, elseif:96, else:100}
export const parser = LRParser.deserialize({ export const parser = LRParser.deserialize({
version: 14, version: 14,
states: "/SQYQbOOO!TOpO'#CqO#aQcO'#CtO$ZOSO'#CvO%aQcO'#DsOOQa'#Ds'#DsO&gQcO'#DrO'OQRO'#CuO'^QcO'#DnO'uQbO'#D{OOQ`'#DO'#DOO'}QbO'#CsOOQ`'#Do'#DoO(oQbO'#DnO(}QbO'#EROOQ`'#DX'#DXO)lQRO'#DaOOQ`'#Dn'#DnO)qQQO'#DmOOQ`'#Dm'#DmOOQ`'#Db'#DbQYQbOOO)yObO,59]OOQa'#Dr'#DrOOQ`'#DS'#DSO*RQbO'#DUOOQ`'#EQ'#EQOOQ`'#Df'#DfO*]QbO,59[O*pQbO'#CxO*xQWO'#CyOOOO'#Du'#DuOOOO'#Dc'#DcO+^OSO,59bOOQa,59b,59bO(}QbO,59aO(}QbO,59aOOQ`'#Dd'#DdO+lQbO'#DPO+tQQO,5:gO+yQRO,59_O-`QRO'#CuO-pQRO,59_O-|QQO,59_O.RQQO,59_O.ZQbO'#DgO.fQbO,59ZO.wQRO,5:mO/OQQO,5:mO/TQbO,59{OOQ`,5:X,5:XOOQ`-E7`-E7`OOQa1G.w1G.wOOQ`,59p,59pOOQ`-E7d-E7dOOOO,59d,59dOOOO,59e,59eOOOO-E7a-E7aOOQa1G.|1G.|OOQa1G.{1G.{O/_QcO1G.{OOQ`-E7b-E7bO/yQbO1G0ROOQa1G.y1G.yO(}QbO,59iO(}QbO,59iO!YQbO'#CtO$iQbO'#CpOOQ`,5:R,5:ROOQ`-E7e-E7eO0WQbO1G0XOOQ`1G/g1G/gO0eQbO7+%mO0jQbO7+%nOOQO1G/T1G/TO0zQRO1G/TOOQ`'#DZ'#DZO1UQbO7+%sO1ZQbO7+%tOOQ`<<IX<<IXOOQ`'#De'#DeO1qQQO'#DeO1vQbO'#EOO2^QbO<<IYOOQ`<<I_<<I_OOQ`'#D['#D[O2cQbO<<I`OOQ`,5:P,5:POOQ`-E7c-E7cOOQ`AN>tAN>tO(}QbO'#D]OOQ`'#Dh'#DhO2nQbOAN>zO2yQQO'#D_OOQ`AN>zAN>zO3OQbOAN>zO3TQRO,59wO3[QQO,59wOOQ`-E7f-E7fOOQ`G24fG24fO3aQbOG24fO3fQQO,59yO3kQQO1G/cOOQ`LD*QLD*QO0jQbO1G/eO1ZQbO7+$}OOQ`7+%P7+%POOQ`<<Hi<<Hi", states: "/SQYQbOOO#[QcO'#CtO$UOSO'#CvO%[QcO'#DsOOQa'#Ds'#DsO&bQcO'#DrO&yQRO'#CuO'XQcO'#DnO'pQbO'#D{OOQ`'#DO'#DOO'xQbO'#CsO(jOpO'#CqOOQ`'#Do'#DoO(oQbO'#DnO(}QbO'#EROOQ`'#DX'#DXO)lQRO'#DaOOQ`'#Dn'#DnO)qQQO'#DmOOQ`'#Dm'#DmOOQ`'#Db'#DbQYQbOOOOQa'#Dr'#DrOOQ`'#DS'#DSO)yQbO'#DUOOQ`'#EQ'#EQOOQ`'#Df'#DfO*TQbO,59[O*hQbO'#CxO*pQWO'#CyOOOO'#Du'#DuOOOO'#Dc'#DcO+UOSO,59bOOQa,59b,59bO(}QbO,59aO(}QbO,59aOOQ`'#Dd'#DdO+dQbO'#DPO+lQQO,5:gO+qQRO,59_O-WQRO'#CuO-hQRO,59_O-tQQO,59_O-yQQO,59_O.RObO,59]O.^QbO'#DgO.iQbO,59ZO.zQRO,5:mO/RQQO,5:mO/WQbO,59{OOQ`,5:X,5:XOOQ`-E7`-E7`OOQ`,59p,59pOOQ`-E7d-E7dOOOO,59d,59dOOOO,59e,59eOOOO-E7a-E7aOOQa1G.|1G.|OOQa1G.{1G.{O/bQcO1G.{OOQ`-E7b-E7bO/|QbO1G0ROOQa1G.y1G.yO(}QbO,59iO(}QbO,59iOOQa1G.w1G.wO!TQbO'#CtO$dQbO'#CpOOQ`,5:R,5:ROOQ`-E7e-E7eO0ZQbO1G0XOOQ`1G/g1G/gO0hQbO7+%mO0mQbO7+%nOOQO1G/T1G/TO0}QRO1G/TOOQ`'#DZ'#DZO1XQbO7+%sO1^QbO7+%tOOQ`<<IX<<IXOOQ`'#De'#DeO1tQQO'#DeO1yQbO'#EOO2aQbO<<IYOOQ`<<I_<<I_OOQ`'#D['#D[O2fQbO<<I`OOQ`,5:P,5:POOQ`-E7c-E7cOOQ`AN>tAN>tO(}QbO'#D]OOQ`'#Dh'#DhO2qQbOAN>zO2|QQO'#D_OOQ`AN>zAN>zO3RQbOAN>zO3WQRO,59wO3_QQO,59wOOQ`-E7f-E7fOOQ`G24fG24fO3dQbOG24fO3iQQO,59yO3nQQO1G/cOOQ`LD*QLD*QO0mQbO1G/eO1^QbO7+$}OOQ`7+%P7+%POOQ`<<Hi<<Hi",
stateData: "3s~O!_OS!`OS~O]QO^`O_TO`POaXOfTOnTOoTOpTO|^O!eZO!hRO!qcO~O!dfO~O]gO_TO`POaXOfTOnTOoTOpTOwhOyiO!eZO!hROzhX!qhX!whX!shXuhX~OP!fXQ!fXR!fXS!fXT!fXU!fXV!fXW!fXX!fXY!fXZ!fX[!fX~P!YOkoO!hrO!jmO!knO~O]gO_TO`POaXOfTOnTOoTOpTOwhOyiO!eZO!hRO~OP!gXQ!gXR!gXS!gX!q!gX!w!gXT!gXU!gXV!gXW!gXX!gXY!gXZ!gX[!gX!s!gXu!gX~P$iOP!fXQ!fXR!fXS!fX!q!bX!w!bXu!bX~OPsOQsORtOStO~OPsOQsORtOStO!q!bX!w!bXu!bX~O]uOtsP~O]QO_TO`POaXOfTOnTOoTOpTO!eZO!hRO~Oz}O!q!bX!w!bXu!bX~O]gO_TO`POfTOnTOoTOpTO!eZO!hRO~OV!RO~O!q!SO!w!SO~O]!UOf!UO~OaXOw!VO~P(}Ozda!qda!wda!sdauda~P$iO]!XO!eZO~O!h!YO!j!YO!k!YO!l!YO!m!YO!n!YO~OkoO!h![O!jmO!knO~O]uOtsX~Ot!`O~O!s!aOP!fXQ!fXR!fXS!fXT!fXU!fXV!fXW!fXX!fXY!fXZ!fX[!fX~OT!cOU!cOV!bOW!bOX!bOY!bOZ!bO[!bO~OPsOQsORtOStO~P,tOPsOQsORtOStO!s!aO~Oz}O!s!aO~O]!dO`PO!eZO~Oz}O!qca!wca!scauca~Ot!hO~P,tOt!hO~O^`O|^O~P'}OPsOQsORiiSii!qii!wii!siiuii~O^`O|^O!q!kO~P'}O^`O|^O!q!pO~P'}Ou!qO~O^`O|^O!q!rOu!rP~P'}O!sqitqi~P,tOu!vO~O^`O|^O!q!rOu!rP!Q!rP!S!rP~P'}O!q!yO~O^`O|^O!q!rOu!rX!Q!rX!S!rX~P'}Ou!{O~Ou#QO!Q!|O!S#PO~Ou#VO!Q!|O!S#PO~Ot#XO~Ou#VO~Ot#YO~P,tOt#YO~Ou#ZO~O!q#[O~O!q#]O~Ofo~", stateData: "3v~O!_OS!`OS~O]PO^`O_SO`ZOaWOfSOnSOoSOpSO|^O!eYO!hQO!qcO~O]fO_SO`ZOaWOfSOnSOoSOpSOwgOyhO!eYO!hQOzhX!qhX!whX!shXuhX~OP!fXQ!fXR!fXS!fXT!fXU!fXV!fXW!fXX!fXY!fXZ!fX[!fX~P!TOknO!hqO!jlO!kmO~O]fO_SO`ZOaWOfSOnSOoSOpSOwgOyhO!eYO!hQO~OP!gXQ!gXR!gXS!gX!q!gX!w!gXT!gXU!gXV!gXW!gXX!gXY!gXZ!gX[!gX!s!gXu!gX~P$dOP!fXQ!fXR!fXS!fX!q!bX!w!bXu!bX~OPrOQrORsOSsO~OPrOQrORsOSsO!q!bX!w!bXu!bX~O]tOtsP~O]PO_SO`ZOaWOfSOnSOoSOpSO!eYO!hQO~O!d|O~Oz}O!q!bX!w!bXu!bX~O]fO_SO`ZOfSOnSOoSOpSO!eYO!hQO~OV!RO~O!q!SO!w!SO~OaWOw!UO~P(}Ozda!qda!wda!sdauda~P$dO]!WO!eYO~O!h!XO!j!XO!k!XO!l!XO!m!XO!n!XO~OknO!h!ZO!jlO!kmO~O]tOtsX~Ot!_O~O!s!`OP!fXQ!fXR!fXS!fXT!fXU!fXV!fXW!fXX!fXY!fXZ!fX[!fX~OT!bOU!bOV!aOW!aOX!aOY!aOZ!aO[!aO~OPrOQrORsOSsO~P,lOPrOQrORsOSsO!s!`O~Oz}O!s!`O~O]!cOf!cO!eYO~O]!dO`ZO!eYO~Oz}O!qca!wca!scauca~Ot!hO~P,lOt!hO~O^`O|^O~P'xOPrOQrORiiSii!qii!wii!siiuii~O^`O|^O!q!kO~P'xO^`O|^O!q!pO~P'xOu!qO~O^`O|^O!q!rOu!rP~P'xO!sqitqi~P,lOu!vO~O^`O|^O!q!rOu!rP!Q!rP!S!rP~P'xO!q!yO~O^`O|^O!q!rOu!rX!Q!rX!S!rX~P'xOu!{O~Ou#QO!Q!|O!S#PO~Ou#VO!Q!|O!S#PO~Ot#XO~Ou#VO~Ot#YO~P,lOt#YO~Ou#ZO~O!q#[O~O!q#]O~Ofo~",
goto: ",`!wPPPPPPPPPPPPPPPPPPP!x#X#gP$V#X$x%_P%x%xPPP%|&Y&sPP&vP&vPP&}P'Z'^'gP'kP&}'q'w'}(T(^(g(nPPPP(t(x)^PP)p*mP+[PPPPP+`+`P+sP+{,S,SdaOe!R!`!h!k!p!t#[#]R{Zi[OZe}!R!`!h!k!p!t#[#]fQOZe!R!`!h!k!p!t#[#]hgQS^ilst!b!c!d!e!|R!d}fSOZe!R!`!h!k!p!t#[#]hTQS^ilst!b!c!d!e!|Q!XmR!e}dWOe!R!`!h!k!p!t#[#]QzZQ!]sR!^t!PTOQSZ^eilst!R!`!b!c!d!e!h!k!p!t!|#[#]ToRqQ{ZQ!Q^Q!l!cR#T!|daOe!R!`!h!k!p!t#[#]YhQSl!d!eQ{ZR!ViRwXZjQSl!d!eeaOe!R!`!h!k!p!t#[#]R!o!hQ!x!pQ#^#[R#_#]T!}!x#OQ#R!xR#W#OQeOR!TeQqRR!ZqQvXR!_vW!t!k!p#[#]R!z!tWlQS!d!eR!WlS!O]|R!g!OQ#O!xR#U#OTdOeSbOeQ!i!RQ!j!`Q!n!hZ!s!k!p!t#[#]d]Oe!R!`!h!k!p!t#[#]Q|ZR!f}dVOe!R!`!h!k!p!t#[#]YhQSl!d!eQyZQ!P^Q!ViQ!]sQ!^tQ!l!bQ!m!cR#S!|dUOe!R!`!h!k!p!t#[#]hgQS^ilst!b!c!d!e!|RxZTpRqsYOQSZeil!R!`!d!e!h!k!p!t#[#]Q!u!kV!w!p#[#]ZkQSl!d!ee_Oe!R!`!h!k!p!t#[#]", goto: ",c!wPPPPPPPPPPPPPPPPPPP!x#X#gP$V#X${%bP%{%{PPP&P&]&vPP&yP&yPP'QP'^'a'jP'nP'Q't'z(Q(W(a(j(qPPPP(w({)aPP)s*pP+_PPPPP+c+cP+vP,O,V,VdaOe!R!_!h!k!p!t#[#]RzYi[OYe}!R!_!h!k!p!t#[#]fPOYe!R!_!h!k!p!t#[#]hfPR^hkrs!a!b!d!e!|R!d}fROYe!R!_!h!k!p!t#[#]hSPR^hkrs!a!b!d!e!|Q!WlQ!c|R!e}dVOe!R!_!h!k!p!t#[#]QyYQ![rR!]s!PSOPRY^ehkrs!R!_!a!b!d!e!h!k!p!t!|#[#]TnQpQzYQ!Q^Q!l!bR#T!|daOe!R!_!h!k!p!t#[#]YgPRk!d!eQzYR!UhRvWZiPRk!d!eeaOe!R!_!h!k!p!t#[#]R!o!hQ!x!pQ#^#[R#_#]T!}!x#OQ#R!xR#W#OQeOR!TeQpQR!YpQuWR!^uW!t!k!p#[#]R!z!tWkPR!d!eR!VkS!O]{R!g!OQ#O!xR#U#OTdOeSbOeQ!i!RQ!j!_Q!n!hZ!s!k!p!t#[#]d]Oe!R!_!h!k!p!t#[#]Q{YR!f}dUOe!R!_!h!k!p!t#[#]YgPRk!d!eQxYQ!P^Q!UhQ![rQ!]sQ!l!aQ!m!bR#S!|dTOe!R!_!h!k!p!t#[#]hfPR^hkrs!a!b!d!e!|RwYToQpsXOPRYehk!R!_!d!e!h!k!p!t#[#]Q!u!kV!w!p#[#]ZjPRk!d!ee_Oe!R!_!h!k!p!t#[#]",
nodeNames: "⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Null ConditionalOp FunctionDef Params colon keyword PositionalArg Underscore NamedArg NamedArgPrefix operator IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign", nodeNames: "⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Null ConditionalOp FunctionDef Params colon keyword PositionalArg Underscore NamedArg NamedArgPrefix operator IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign",
maxTerm: 85, maxTerm: 85,
context: trackScope, context: trackScope,
@ -23,5 +23,5 @@ export const parser = LRParser.deserialize({
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!d~~", 11)], tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!d~~", 11)],
topRules: {"Program":[0,18]}, topRules: {"Program":[0,18]},
specialized: [{term: 13, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 13, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], specialized: [{term: 13, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 13, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
tokenPrec: 860 tokenPrec: 863
}) })

View File

@ -274,4 +274,28 @@ end`).toMatchTree(`
Identifier heya Identifier heya
`) `)
}) })
test('can use the result of a parens expression as the property of dot get', () => {
expect('obj = list 1 2 3; obj.(1 + 2)').toMatchTree(`
Assign
AssignableIdentifier obj
Eq =
FunctionCall
Identifier list
PositionalArg
Number 1
PositionalArg
Number 2
PositionalArg
Number 3
FunctionCallOrIdentifier
DotGet
IdentifierBeforeDot obj
ParenExpr
BinOp
Number 1
Plus +
Number 2
`)
})
}) })

View File

@ -3,7 +3,7 @@ import { parser } from '#parser/shrimp'
import { $ } from 'bun' import { $ } from 'bun'
import { assert, errorMessage } from '#utils/utils' import { assert, errorMessage } from '#utils/utils'
import { Compiler } from '#compiler/compiler' import { Compiler } from '#compiler/compiler'
import { run, VM } from 'reefvm' import { run, VM, type TypeScriptFunction } from 'reefvm'
import { treeToString, VMResultToValue } from '#utils/tree' import { treeToString, VMResultToValue } from '#utils/tree'
const regenerateParser = async () => { const regenerateParser = async () => {
@ -33,13 +33,16 @@ declare module 'bun:test' {
toMatchTree(expected: string): T toMatchTree(expected: string): T
toMatchExpression(expected: string): T toMatchExpression(expected: string): T
toFailParse(): T toFailParse(): T
toEvaluateTo(expected: unknown, nativeFunctions?: Record<string, Function>): Promise<T> toEvaluateTo(
expected: unknown,
nativeFunctions?: Record<string, TypeScriptFunction>
): Promise<T>
toFailEvaluation(): Promise<T> toFailEvaluation(): Promise<T>
} }
} }
expect.extend({ expect.extend({
toMatchTree(received: unknown, expected: string) { toMatchTree(received, expected) {
assert(typeof received === 'string', 'toMatchTree can only be used with string values') assert(typeof received === 'string', 'toMatchTree can only be used with string values')
const tree = parser.parse(received) const tree = parser.parse(received)
@ -58,7 +61,7 @@ expect.extend({
} }
}, },
toFailParse(received: unknown) { toFailParse(received) {
assert(typeof received === 'string', 'toFailParse can only be used with string values') assert(typeof received === 'string', 'toFailParse can only be used with string values')
try { try {
@ -93,11 +96,7 @@ expect.extend({
} }
}, },
async toEvaluateTo( async toEvaluateTo(received, expected, nativeFunctions = {}) {
received: unknown,
expected: unknown,
nativeFunctions: Record<string, Function> = {}
) {
assert(typeof received === 'string', 'toEvaluateTo can only be used with string values') assert(typeof received === 'string', 'toEvaluateTo can only be used with string values')
try { try {
@ -109,13 +108,10 @@ expect.extend({
if (expected instanceof RegExp) expected = String(expected) if (expected instanceof RegExp) expected = String(expected)
if (value instanceof RegExp) value = String(value) if (value instanceof RegExp) value = String(value)
if (value === expected) { expect(value).toEqual(expected)
return { pass: true } return {
} else { message: () => `Expected evaluation to be ${expected}, but got ${value}`,
return { pass: true,
message: () => `Expected evaluation to be ${expected}, but got ${value}`,
pass: false,
}
} }
} catch (error) { } catch (error) {
return { return {
@ -125,7 +121,7 @@ expect.extend({
} }
}, },
async toFailEvaluation(received: unknown) { async toFailEvaluation(received) {
assert(typeof received === 'string', 'toFailEvaluation can only be used with string values') assert(typeof received === 'string', 'toFailEvaluation can only be used with string values')
try { try {