Add += and friends
This commit is contained in:
parent
83fad9a68f
commit
bc0684185a
|
|
@ -9,6 +9,7 @@ import {
|
||||||
checkTreeForErrors,
|
checkTreeForErrors,
|
||||||
getAllChildren,
|
getAllChildren,
|
||||||
getAssignmentParts,
|
getAssignmentParts,
|
||||||
|
getCompoundAssignmentParts,
|
||||||
getBinaryParts,
|
getBinaryParts,
|
||||||
getDotGetParts,
|
getDotGetParts,
|
||||||
getFunctionCallParts,
|
getFunctionCallParts,
|
||||||
|
|
@ -245,6 +246,34 @@ export class Compiler {
|
||||||
return instructions
|
return instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case terms.CompoundAssign: {
|
||||||
|
const { identifier, operator, right } = getCompoundAssignmentParts(node)
|
||||||
|
const identifierName = input.slice(identifier.from, identifier.to)
|
||||||
|
const instructions: ProgramItem[] = []
|
||||||
|
|
||||||
|
// will throw if undefined
|
||||||
|
instructions.push(['LOAD', identifierName])
|
||||||
|
|
||||||
|
instructions.push(...this.#compileNode(right, input))
|
||||||
|
|
||||||
|
const opValue = input.slice(operator.from, operator.to)
|
||||||
|
switch (opValue) {
|
||||||
|
case '+=': instructions.push(['ADD']); break
|
||||||
|
case '-=': instructions.push(['SUB']); break
|
||||||
|
case '*=': instructions.push(['MUL']); break
|
||||||
|
case '/=': instructions.push(['DIV']); break
|
||||||
|
case '%=': instructions.push(['MOD']); break
|
||||||
|
default:
|
||||||
|
throw new CompilerError(`Unknown compound operator: ${opValue}`, operator.from, operator.to)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DUP and store (same as regular assignment)
|
||||||
|
instructions.push(['DUP'])
|
||||||
|
instructions.push(['STORE', identifierName])
|
||||||
|
|
||||||
|
return instructions
|
||||||
|
}
|
||||||
|
|
||||||
case terms.ParenExpr: {
|
case terms.ParenExpr: {
|
||||||
const child = node.firstChild
|
const child = node.firstChild
|
||||||
if (!child) return [] // I guess it is empty parentheses?
|
if (!child) return [] // I guess it is empty parentheses?
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,38 @@ describe('compiler', () => {
|
||||||
expect('sum = 2 + 3; sum').toEvaluateTo(5)
|
expect('sum = 2 + 3; sum').toEvaluateTo(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('compound assignment +=', () => {
|
||||||
|
expect('x = 10; x += 5; x').toEvaluateTo(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('compound assignment -= ', () => {
|
||||||
|
expect('x = 10; x -= 3; x').toEvaluateTo(7)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('compound assignment *=', () => {
|
||||||
|
expect('x = 5; x *= 3; x').toEvaluateTo(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('compound assignment /=', () => {
|
||||||
|
expect('x = 20; x /= 4; x').toEvaluateTo(5)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('compound assignment %=', () => {
|
||||||
|
expect('x = 17; x %= 5; x').toEvaluateTo(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('compound assignment with expression', () => {
|
||||||
|
expect('x = 10; x += 2 + 3; x').toEvaluateTo(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('compound assignment returns value', () => {
|
||||||
|
expect('x = 5; x += 10; x').toEvaluateTo(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('compound assignment fails on undefined variable', () => {
|
||||||
|
expect('undefined-var += 5').toFailEvaluation()
|
||||||
|
})
|
||||||
|
|
||||||
test('parentheses', () => {
|
test('parentheses', () => {
|
||||||
expect('(2 + 3) * 4').toEvaluateTo(20)
|
expect('(2 + 3) * 4').toEvaluateTo(20)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,27 @@ export const getAssignmentParts = (node: SyntaxNode) => {
|
||||||
return { identifier: left, right }
|
return { identifier: left, right }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getCompoundAssignmentParts = (node: SyntaxNode) => {
|
||||||
|
const children = getAllChildren(node)
|
||||||
|
const [left, operator, right] = children
|
||||||
|
|
||||||
|
if (!left || left.type.id !== terms.AssignableIdentifier) {
|
||||||
|
throw new CompilerError(
|
||||||
|
`CompoundAssign left child must be an AssignableIdentifier, got ${left ? left.type.name : 'none'}`,
|
||||||
|
node.from,
|
||||||
|
node.to
|
||||||
|
)
|
||||||
|
} else if (!operator || !right) {
|
||||||
|
throw new CompilerError(
|
||||||
|
`CompoundAssign expected 3 children, got ${children.length}`,
|
||||||
|
node.from,
|
||||||
|
node.to
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { identifier: left, operator, right }
|
||||||
|
}
|
||||||
|
|
||||||
export const getFunctionDefParts = (node: SyntaxNode, input: string) => {
|
export const getFunctionDefParts = (node: SyntaxNode, input: string) => {
|
||||||
const children = getAllChildren(node)
|
const children = getAllChildren(node)
|
||||||
const [fnKeyword, paramsNode, colon, ...bodyNodes] = children
|
const [fnKeyword, paramsNode, colon, ...bodyNodes] = children
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,14 @@ const operators: Array<Operator> = [
|
||||||
{ str: '!=', tokenName: 'Neq' },
|
{ str: '!=', tokenName: 'Neq' },
|
||||||
{ str: '==', tokenName: 'EqEq' },
|
{ str: '==', tokenName: 'EqEq' },
|
||||||
|
|
||||||
// // Single-char operators
|
// Compound assignment operators (must come before single-char operators)
|
||||||
|
{ str: '+=', tokenName: 'PlusEq' },
|
||||||
|
{ str: '-=', tokenName: 'MinusEq' },
|
||||||
|
{ str: '*=', tokenName: 'StarEq' },
|
||||||
|
{ str: '/=', tokenName: 'SlashEq' },
|
||||||
|
{ str: '%=', tokenName: 'ModuloEq' },
|
||||||
|
|
||||||
|
// Single-char operators
|
||||||
{ str: '*', tokenName: 'Star' },
|
{ str: '*', tokenName: 'Star' },
|
||||||
{ str: '=', tokenName: 'Eq' },
|
{ str: '=', tokenName: 'Eq' },
|
||||||
{ str: '/', tokenName: 'Slash' },
|
{ str: '/', tokenName: 'Slash' },
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
@top Program { item* }
|
@top Program { item* }
|
||||||
|
|
||||||
@external tokens operatorTokenizer from "./operatorTokenizer" { Star, Slash, Plus, Minus, And, Or, Eq, EqEq, Neq, Lt, Lte, Gt, Gte, Modulo }
|
@external tokens operatorTokenizer from "./operatorTokenizer" { Star, Slash, Plus, Minus, And, Or, Eq, EqEq, Neq, Lt, Lte, Gt, Gte, Modulo, PlusEq, MinusEq, StarEq, SlashEq, ModuloEq }
|
||||||
|
|
||||||
@tokens {
|
@tokens {
|
||||||
@precedence { Number Regex }
|
@precedence { Number Regex }
|
||||||
|
|
@ -53,6 +53,7 @@ consumeToTerminator {
|
||||||
ambiguousFunctionCall |
|
ambiguousFunctionCall |
|
||||||
IfExpr |
|
IfExpr |
|
||||||
FunctionDef |
|
FunctionDef |
|
||||||
|
CompoundAssign |
|
||||||
Assign |
|
Assign |
|
||||||
BinOp |
|
BinOp |
|
||||||
ConditionalOp |
|
ConditionalOp |
|
||||||
|
|
@ -151,6 +152,10 @@ Assign {
|
||||||
AssignableIdentifier Eq consumeToTerminator
|
AssignableIdentifier Eq consumeToTerminator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompoundAssign {
|
||||||
|
AssignableIdentifier (PlusEq | MinusEq | StarEq | SlashEq | ModuloEq) consumeToTerminator
|
||||||
|
}
|
||||||
|
|
||||||
BinOp {
|
BinOp {
|
||||||
expression !multiplicative Modulo expression |
|
expression !multiplicative Modulo expression |
|
||||||
(expression | BinOp) !multiplicative Star (expression | BinOp) |
|
(expression | BinOp) !multiplicative Star (expression | BinOp) |
|
||||||
|
|
|
||||||
|
|
@ -14,40 +14,46 @@ export const
|
||||||
Gt = 12,
|
Gt = 12,
|
||||||
Gte = 13,
|
Gte = 13,
|
||||||
Modulo = 14,
|
Modulo = 14,
|
||||||
Identifier = 15,
|
PlusEq = 15,
|
||||||
AssignableIdentifier = 16,
|
MinusEq = 16,
|
||||||
Word = 17,
|
StarEq = 17,
|
||||||
IdentifierBeforeDot = 18,
|
SlashEq = 18,
|
||||||
Do = 19,
|
ModuloEq = 19,
|
||||||
Program = 20,
|
Identifier = 20,
|
||||||
PipeExpr = 21,
|
AssignableIdentifier = 21,
|
||||||
FunctionCall = 22,
|
Word = 22,
|
||||||
DotGet = 23,
|
IdentifierBeforeDot = 23,
|
||||||
Number = 24,
|
Do = 24,
|
||||||
ParenExpr = 25,
|
Program = 25,
|
||||||
FunctionCallOrIdentifier = 26,
|
PipeExpr = 26,
|
||||||
BinOp = 27,
|
FunctionCall = 27,
|
||||||
String = 28,
|
DotGet = 28,
|
||||||
StringFragment = 29,
|
Number = 29,
|
||||||
Interpolation = 30,
|
ParenExpr = 30,
|
||||||
EscapeSeq = 31,
|
FunctionCallOrIdentifier = 31,
|
||||||
Boolean = 32,
|
BinOp = 32,
|
||||||
Regex = 33,
|
String = 33,
|
||||||
Dict = 34,
|
StringFragment = 34,
|
||||||
NamedArg = 35,
|
Interpolation = 35,
|
||||||
NamedArgPrefix = 36,
|
EscapeSeq = 36,
|
||||||
FunctionDef = 37,
|
Boolean = 37,
|
||||||
Params = 38,
|
Regex = 38,
|
||||||
colon = 39,
|
Dict = 39,
|
||||||
keyword = 54,
|
NamedArg = 40,
|
||||||
Underscore = 41,
|
NamedArgPrefix = 41,
|
||||||
Array = 42,
|
FunctionDef = 42,
|
||||||
Null = 43,
|
Params = 43,
|
||||||
ConditionalOp = 44,
|
colon = 44,
|
||||||
PositionalArg = 45,
|
keyword = 59,
|
||||||
IfExpr = 47,
|
Underscore = 46,
|
||||||
SingleLineThenBlock = 49,
|
Array = 47,
|
||||||
ThenBlock = 50,
|
Null = 48,
|
||||||
ElseIfExpr = 51,
|
ConditionalOp = 49,
|
||||||
ElseExpr = 53,
|
PositionalArg = 50,
|
||||||
Assign = 55
|
IfExpr = 52,
|
||||||
|
SingleLineThenBlock = 54,
|
||||||
|
ThenBlock = 55,
|
||||||
|
ElseIfExpr = 56,
|
||||||
|
ElseExpr = 58,
|
||||||
|
CompoundAssign = 60,
|
||||||
|
Assign = 61
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,24 @@ import {operatorTokenizer} from "./operatorTokenizer"
|
||||||
import {tokenizer, specializeKeyword} from "./tokenizer"
|
import {tokenizer, specializeKeyword} from "./tokenizer"
|
||||||
import {trackScope} from "./scopeTracker"
|
import {trackScope} from "./scopeTracker"
|
||||||
import {highlighting} from "./highlight"
|
import {highlighting} from "./highlight"
|
||||||
const spec_Identifier = {__proto__:null,end:80, null:86, if:96, elseif:104, else:108}
|
const spec_Identifier = {__proto__:null,end:90, null:96, if:106, elseif:114, else:118}
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states: "3UQYQbOOO#hQcO'#CvO$eOSO'#CxO$sQbO'#EVOOQ`'#DR'#DROOQa'#DO'#DOO%vQbO'#DWO&{QcO'#DzOOQa'#Dz'#DzO)PQcO'#DyO)xQRO'#CwO*]QcO'#DuO*tQcO'#DuO+VQbO'#CuO+}OpO'#CsOOQ`'#Dv'#DvO,SQbO'#DuO,bQbO'#E]OOQ`'#D]'#D]O-VQRO'#DeOOQ`'#Du'#DuO-[QQO'#DtOOQ`'#Dt'#DtOOQ`'#Df'#DfQYQbOOO-dQbO'#DPOOQa'#Dy'#DyOOQ`'#DZ'#DZOOQ`'#E['#E[OOQ`'#Dm'#DmO-nQbO,59^O.RQbO'#CzO.ZQWO'#C{OOOO'#D|'#D|OOOO'#Dg'#DgO.oOSO,59dOOQa,59d,59dOOQ`'#Di'#DiO.}QbO'#DSO/VQQO,5:qOOQ`'#Dh'#DhO/[QbO,59rO/cQQO,59jOOQa,59r,59rO/nQbO,59rO,bQbO,59cO,bQbO,59cO,bQbO,59cO,bQbO,59tO,bQbO,59tO,bQbO,59tO/xQRO,59aO0PQRO,59aO0bQRO,59aO0]QQO,59aO0mQQO,59aO0uObO,59_O1QQbO'#DnO1]QbO,59]O1nQRO,5:wO1uQRO,5:wO2QQbO,5:POOQ`,5:`,5:`OOQ`-E7d-E7dOOQ`,59k,59kOOQ`-E7k-E7kOOOO,59f,59fOOOO,59g,59gOOOO-E7e-E7eOOQa1G/O1G/OOOQ`-E7g-E7gO2[QbO1G0]OOQ`-E7f-E7fO2iQQO1G/UOOQa1G/^1G/^O2tQbO1G/^OOQO'#Dk'#DkO2iQQO1G/UOOQa1G/U1G/UOOQ`'#Dl'#DlO2tQbO1G/^OOQa1G.}1G.}O3gQcO1G.}O3qQcO1G.}O3{QcO1G.}OOQa1G/`1G/`O5_QcO1G/`O5fQcO1G/`O5mQcO1G/`OOQa1G.{1G.{OOQa1G.y1G.yO!ZQbO'#CvO%}QbO'#CrOOQ`,5:Y,5:YOOQ`-E7l-E7lO5tQbO1G0cOOQ`1G/k1G/kO6RQbO7+%wO6WQbO7+%xO6hQQO7+$pOOQa7+$p7+$pO6sQbO7+$xOOQa7+$x7+$xOOQO-E7i-E7iOOQ`-E7j-E7jOOQ`'#D_'#D_O6}QbO7+%}O7SQbO7+&OOOQ`<<Ic<<IcOOQ`'#Dj'#DjO7jQQO'#DjO7oQbO'#EXO8VQbO<<IdOOQa<<H[<<H[OOQa<<Hd<<HdOOQ`<<Ii<<IiOOQ`'#D`'#D`O8[QbO<<IjOOQ`,5:U,5:UOOQ`-E7h-E7hOOQ`AN?OAN?OO,bQbO'#DaOOQ`'#Do'#DoO8gQbOAN?UO8rQQO'#DcOOQ`AN?UAN?UO8wQbOAN?UO8|QRO,59{O9TQRO,59{OOQ`-E7m-E7mOOQ`G24pG24pO9`QbOG24pO9eQQO,59}O9jQQO1G/gOOQ`LD*[LD*[O6WQbO1G/iO7SQbO7+%ROOQ`7+%T7+%TOOQ`<<Hm<<Hm",
|
states: "3bQYQbOOO#hQcO'#C{O$eOSO'#C}O$sQbO'#E]OOQ`'#DW'#DWOOQa'#DT'#DTO%vQbO'#D]O&{QcO'#EQOOQa'#EQ'#EQO)PQcO'#EPO)xQRO'#C|O*]QcO'#D{O*tQcO'#D{O+VQbO'#CzO+}OpO'#CxOOQ`'#D|'#D|O,SQbO'#D{O,bQbO'#EcOOQ`'#Db'#DbO-VQRO'#DjOOQ`'#D{'#D{O-kQQO'#DzOOQ`'#Dz'#DzOOQ`'#Dl'#DlQYQbOOO-sQbO'#DUOOQa'#EP'#EPOOQ`'#D`'#D`OOQ`'#Eb'#EbOOQ`'#Ds'#DsO-}QbO,59cO.bQbO'#DPO.jQWO'#DQOOOO'#ES'#ESOOOO'#Dm'#DmO/OOSO,59iOOQa,59i,59iOOQ`'#Do'#DoO/^QbO'#DXO/fQQO,5:wOOQ`'#Dn'#DnO/kQbO,59wO/rQQO,59oOOQa,59w,59wO/}QbO,59wO,bQbO,59hO,bQbO,59hO,bQbO,59hO,bQbO,59yO,bQbO,59yO,bQbO,59yO0XQRO,59fO0`QRO,59fO0qQRO,59fO0lQQO,59fO0|QQO,59fO1UObO,59dO1aQbO'#DtO1lQbO,59bO1}QRO,5:}O2UQRO,5:}O2aQbO,5:UO2aQbO,5:VOOQ`,5:f,5:fOOQ`-E7j-E7jOOQ`,59p,59pOOQ`-E7q-E7qOOOO,59k,59kOOOO,59l,59lOOOO-E7k-E7kOOQa1G/T1G/TOOQ`-E7m-E7mO2kQbO1G0cOOQ`-E7l-E7lO2xQQO1G/ZOOQa1G/c1G/cO3TQbO1G/cOOQO'#Dq'#DqO2xQQO1G/ZOOQa1G/Z1G/ZOOQ`'#Dr'#DrO3TQbO1G/cOOQa1G/S1G/SO3vQcO1G/SO4QQcO1G/SO4[QcO1G/SOOQa1G/e1G/eO5nQcO1G/eO5uQcO1G/eO5|QcO1G/eOOQa1G/Q1G/QOOQa1G/O1G/OO!ZQbO'#C{O%}QbO'#CwOOQ`,5:`,5:`OOQ`-E7r-E7rO6TQbO1G0iOOQ`1G/p1G/pOOQ`1G/q1G/qO6bQbO7+%}O6gQbO7+&OO6wQQO7+$uOOQa7+$u7+$uO7SQbO7+$}OOQa7+$}7+$}OOQO-E7o-E7oOOQ`-E7p-E7pOOQ`'#Dd'#DdO7^QbO7+&TO7cQbO7+&UOOQ`<<Ii<<IiOOQ`'#Dp'#DpO7yQQO'#DpO8OQbO'#E_O8fQbO<<IjOOQa<<Ha<<HaOOQa<<Hi<<HiOOQ`<<Io<<IoOOQ`'#De'#DeO8kQbO<<IpOOQ`,5:[,5:[OOQ`-E7n-E7nOOQ`AN?UAN?UO,bQbO'#DfOOQ`'#Du'#DuO8vQbOAN?[O9RQQO'#DhOOQ`AN?[AN?[O9WQbOAN?[O9]QRO,5:QO9dQRO,5:QOOQ`-E7s-E7sOOQ`G24vG24vO9oQbOG24vO9tQQO,5:SO9yQQO1G/lOOQ`LD*bLD*bO6gQbO1G/nO7cQbO7+%WOOQ`7+%Y7+%YOOQ`<<Hr<<Hr",
|
||||||
stateData: "9r~O!fOS!gOS~O_PO`cOaWOb^OcROhWOpWOqWO{WO!QaO!l]O!oQO!vTO!wUO!xfO~O_jOaWOb^OcROhWOpWOqWOtiOykO{WO!l]O!oQO!vTO!wUO!OjX!xjX#RjX!}jXxjX~OP!mXQ!mXR!mXS!mXT!mXU!mXW!mXX!mXY!mXZ!mX[!mX]!mX^!mX~P!ZOmqO!otO!qoO!rpO~O_uOwvP~O_jOaWOb^OhWOpWOqWOtiO{WO!l]O!oQO!vTO!wUO!xxO~O!|{O~P${O_jOaWOb^OcROhWOpWOqWOtiOykO{WO!l]O!oQO!vTO!wUO~OP!nXQ!nXR!nXS!nXT!nXU!nXW!nXX!nXY!nXZ!nX[!nX]!nX^!nX!x!nX#R!nX!}!nXx!nX~P%}OP!mXQ!mXR!mXS!mXT!mXU!mXW!mXX!mXY!mXZ!mX[!mX]!mX^!mX~O!x!iX#R!iXx!iX~P(UOT!ROU!SOW!QOX!QOY!QOZ!QO[!QO]!QO~OP!OOQ!OOR!POS!PO^}O~P)^OP!OOQ!OOR!POS!PO!x!iX#R!iXx!iX~OT!ROU!SO!x!iX#R!iXx!iX~O_POaWOb^OcROhWOpWOqWO{WO!l]O!oQO!vTO!wUO~O!k!YO~O!O!ZO!x!iX#R!iXx!iX~O_jOaWOb^OhWOpWOqWO{WO!l]O!oQO!vTO!wUO~OV!_O~O!x!`O#R!`O~OcROy!bO~P,bO!Ofa!xfa#Rfa!}faxfa~P%}O_!dO!l]O~O!o!eO!q!eO!r!eO!s!eO!t!eO!u!eO~OmqO!o!gO!qoO!rpO~O_uOwvX~Ow!iO~O!|!lO~P${OtiO!x!nO!|!pO~O!x!qO!|!lO~P,bO!}!{O~P(UOP!OOQ!OOR!POS!PO!}!{O~OT!ROU!SO!}!{O~O!O!ZO!}!{O~O_!|Oh!|O!l]O~O_!}Ob^O!l]O~O!O!ZO!xea#Rea!}eaxea~Ow#RO~P)^OT!ROU!SOw#RO~O`cO!QaO~P+VO`cO!QaO!x#UO~P+VOtiO!x!nO!|#WO~O!x!qO!|#YO~P,bO^}ORkiSki!xki#Rki!}kixki~OPkiQki~P3OOP!OOQ!OO~P3OOP!OOQ!OORkiSki!xki#Rki!}kixki~OW!QOX!QOY!QOZ!QO[!QO]!QOT|i!x|i#R|i!}|iw|ix|i~OU!SO~P4gOU!SO~P4yOU|i~P4gO`cO!QaO!x#_O~P+VOx#`O~O`cO!QaO!x#aOx!{P~P+VOtiO!x!nO!|#eO~O!x!qO!|#fO~P,bOx#gO~O`cO!QaO!x#aOx!{P!U!{P!W!{P~P+VO!x#jO~O`cO!QaO!x#aOx!{X!U!{X!W!{X~P+VOx#lO~Ox#qO!U#mO!W#pO~Ox#vO!U#mO!W#pO~Ow#xO~Ox#vO~Ow#yO~P)^OT!ROU!SOw#yO~Ox#zO~O!x#{O~O!x#|O~Ohq~",
|
stateData: ":R~O!lOS!mOS~OdPOecOfWOg^OhROmWOuWOvWO!QWO!VaO!r]O!uQO!|TO!}UO#OfO~OdjOfWOg^OhROmWOuWOvWOyiO!OkO!QWO!r]O!uQO!|TO!}UO!ToX#OoX#XoX#ToX}oX~OP!sXQ!sXR!sXS!sXT!sXU!sXW!sXX!sXY!sXZ!sX[!sX]!sX^!sX~P!ZOrqO!utO!woO!xpO~OduO|{P~OdjOfWOg^OmWOuWOvWOyiO!QWO!r]O!uQO!|TO!}UO#OxO~O#S{O~P${OdjOfWOg^OhROmWOuWOvWOyiO!OkO!QWO!r]O!uQO!|TO!}UO~OP!tXQ!tXR!tXS!tXT!tXU!tXW!tXX!tXY!tXZ!tX[!tX]!tX^!tX#O!tX#X!tX#T!tX}!tX~P%}OP!sXQ!sXR!sXS!sXT!sXU!sXW!sXX!sXY!sXZ!sX[!sX]!sX^!sX~O#O!oX#X!oX}!oX~P(UOT!ROU!SOW!QOX!QOY!QOZ!QO[!QO]!QO~OP!OOQ!OOR!POS!PO^}O~P)^OP!OOQ!OOR!POS!PO#O!oX#X!oX}!oX~OT!ROU!SO#O!oX#X!oX}!oX~OdPOfWOg^OhROmWOuWOvWO!QWO!r]O!uQO!|TO!}UO~O!q!YO~O!T!ZO#O!oX#X!oX}!oX~OdjOfWOg^OmWOuWOvWO!QWO!r]O!uQO!|TO!}UO~OV!`O_!_O`!_Oa!_Ob!_Oc!_O~O#O!aO#X!aO~OhRO!O!cO~P,bO!Tka#Oka#Xka#Tka}ka~P%}Od!eO!r]O~O!u!fO!w!fO!x!fO!y!fO!z!fO!{!fO~OrqO!u!hO!woO!xpO~OduO|{X~O|!jO~O#S!mO~P${OyiO#O!oO#S!qO~O#O!rO#S!mO~P,bO#T!|O~P(UOP!OOQ!OOR!POS!PO#T!|O~OT!ROU!SO#T!|O~O!T!ZO#T!|O~Od!}Om!}O!r]O~Od#OOg^O!r]O~O!T!ZO#Oja#Xja#Tja}ja~O|#SO~P)^OT!ROU!SO|#SO~OecO!VaO~P+VOecO!VaO#O#WO~P+VOyiO#O!oO#S#YO~O#O!rO#S#[O~P,bO^}ORpiSpi#Opi#Xpi#Tpi}pi~OPpiQpi~P3_OP!OOQ!OO~P3_OP!OOQ!OORpiSpi#Opi#Xpi#Tpi}pi~OW!QOX!QOY!QOZ!QO[!QO]!QOT!Ri#O!Ri#X!Ri#T!Ri|!Ri}!Ri~OU!SO~P4vOU!SO~P5YOU!Ri~P4vOecO!VaO#O#aO~P+VO}#bO~OecO!VaO#O#cO}#RP~P+VOyiO#O!oO#S#gO~O#O!rO#S#hO~P,bO}#iO~OecO!VaO#O#cO}#RP!Z#RP!]#RP~P+VO#O#lO~OecO!VaO#O#cO}#RX!Z#RX!]#RX~P+VO}#nO~O}#sO!Z#oO!]#rO~O}#xO!Z#oO!]#rO~O|#zO~O}#xO~O|#{O~P)^OT!ROU!SO|#{O~O}#|O~O#O#}O~O#O$OO~Omv~",
|
||||||
goto: ".}#RPPPPPPPPPPPPPPPPPPPPP#S#c#qP$i#c%g%|P&o&oPP%|&sP'W'qPPP%|P't(aP(hP(t(w)QP)UP(h)[)b)h)n)t)}*X*c*l*sPPPP*y*}+cPP+u-SP-yPPPPPPPP-}-}.bPP.j.q.qddOh!_!i#R#U#_#c#{#|R!W]i_O]h!Z!_!i#R#U#_#c#{#|fPO]h!_!i#R#U#_#c#{#|xjPUVainy|}!O!P!Q!R!S!m!r!}#O#X#mR!}!ZfVO]h!_!i#R#U#_#c#{#|xWPUVainy|}!O!P!Q!R!S!m!r!}#O#X#mQ!doQ!|!YR#O!ZdZOh!_!i#R#U#_#c#{#|Q!U]Q!s!OR!v!P!aWOPUV]ahiny|}!O!P!Q!R!S!_!i!m!r!}#O#R#U#X#_#c#m#{#|TqQsYlPVn!}#OQzUQ!kyX!nz!k!o#VddOh!_!i#R#U#_#c#{#|YkPVn!}#OQ!W]R!biRwRd[Oh!_!i#R#U#_#c#{#|Q!V]Q!^aQ!w!SQ!y!RR#t#mZlPVn!}#OedOh!_!i#R#U#_#c#{#|R#^#RQ#i#_Q#}#{R$O#|T#n#i#oQ#r#iR#w#oQhOR!ahQsQR!fsQyUR!jyQvRR!hvW#c#U#_#{#|R#k#cQ!ozQ#V!kT#Z!o#VQ!r|Q#X!mT#[!r#XWnPV!}#OR!cnS![`!XR#Q![Q#o#iR#u#oTgOhSeOhQ#S!_Q#T!iQ#]#RZ#b#U#_#c#{#|d`Oh!_!i#R#U#_#c#{#|Q!X]R#P!ZfYO]h!_!i#R#U#_#c#{#|YkPVn!}#OQ|UQ!]aQ!biQ!myW!q|!m!r#XQ!s}Q!t!OQ!u!PQ!w!QQ!x!RQ!z!SR#s#mdXOh!_!i#R#U#_#c#{#|xjPUVainy|}!O!P!Q!R!S!m!r!}#O#X#mR!T]TrQssSOPV]hin!_!i!}#O#R#U#_#c#{#|Q#d#UV#h#_#{#|ZmPVn!}#OebOh!_!i#R#U#_#c#{#|",
|
goto: "/f#XPPPPPPPPPPPPPPPPPPPPPPPPPP#Y#j#yP$r#j%q&XP&{&{PP&X'PP'd(OPPP&XP(R(oP(vP)T)W)aP)eP(v(v)k)q)w)}*T*^*h*r*{+SPPPP+Y+^+uPP,Y-hP.`PPPPPPPP.d.d.xPP/Q/X/XfdOh!_!`!j#S#W#a#e#}$OR!W]k_O]h!Z!_!`!j#S#W#a#e#}$OhPO]h!_!`!j#S#W#a#e#}$OxjPUVainy|}!O!P!Q!R!S!n!s#O#P#Z#oR#O!ZhVO]h!_!`!j#S#W#a#e#}$OxWPUVainy|}!O!P!Q!R!S!n!s#O#P#Z#oQ!eoQ!}!YR#P!ZfZOh!_!`!j#S#W#a#e#}$OQ!U]Q!t!OR!w!P!cWOPUV]ahiny|}!O!P!Q!R!S!_!`!j!n!s#O#P#S#W#Z#a#e#o#}$OTqQsYlPVn#O#PQzUQ!lyX!oz!l!p#XfdOh!_!`!j#S#W#a#e#}$OYkPVn#O#PQ!W]R!ciRwRf[Oh!_!`!j#S#W#a#e#}$OQ!V]Q!^aQ!x!SQ!z!RR#v#oZlPVn#O#PgdOh!_!`!j#S#W#a#e#}$OR#`#SQ#k#aQ$P#}R$Q$OT#p#k#qQ#t#kR#y#qQhOR!bhQsQR!gsQyUR!kyQvRR!ivW#e#W#a#}$OR#m#eQ!pzQ#X!lT#]!p#XQ!s|Q#Z!nT#^!s#ZWnPV#O#PR!dnS![`!XR#R![Q#q#kR#w#qTgOhSeOhQ#T!_Q#U!`Q#V!jQ#_#SZ#d#W#a#e#}$Of`Oh!_!`!j#S#W#a#e#}$OQ!X]R#Q!ZhYO]h!_!`!j#S#W#a#e#}$OYkPVn#O#PQ|UQ!]aQ!ciQ!nyW!r|!n!s#ZQ!t}Q!u!OQ!v!PQ!x!QQ!y!RQ!{!SR#u#ofXOh!_!`!j#S#W#a#e#}$OxjPUVainy|}!O!P!Q!R!S!n!s#O#P#Z#oR!T]TrQsuSOPV]hin!_!`!j#O#P#S#W#a#e#}$OQ#f#WV#j#a#}$OZmPVn#O#PgbOh!_!`!j#S#W#a#e#}$O",
|
||||||
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Dict NamedArg NamedArgPrefix FunctionDef Params colon keyword Underscore Array Null ConditionalOp PositionalArg operator IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign",
|
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Dict NamedArg NamedArgPrefix FunctionDef Params colon keyword Underscore Array Null ConditionalOp PositionalArg operator IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword CompoundAssign Assign",
|
||||||
maxTerm: 95,
|
maxTerm: 101,
|
||||||
context: trackScope,
|
context: trackScope,
|
||||||
nodeProps: [
|
nodeProps: [
|
||||||
["closedBy", 39,"end"]
|
["closedBy", 44,"end"]
|
||||||
],
|
],
|
||||||
propSources: [highlighting],
|
propSources: [highlighting],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 10,
|
repeatNodeCount: 10,
|
||||||
tokenData: "AO~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'Vuw#{wx'[xy'ayz'zz{#{{|(e|}#{}!O(e!O!P#{!P!Q+X!Q![)S![!]3t!]!^%T!^!}#{!}#O4_#O#P6T#P#Q6Y#Q#R#{#R#S6s#S#T#{#T#Y7^#Y#Z8l#Z#b7^#b#c<z#c#f7^#f#g=q#g#h7^#h#i>h#i#o7^#o#p#{#p#q@`#q;'S#{;'S;=`$d<%l~#{~O#{~~@yS$QUmSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qUmS!fYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[UmS!xQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZmS!gYOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mS!gYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O!q~~'aO!o~U'hUmS!lQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RUmS!}QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jWmSOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZYmShQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OWmSOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oWmShQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^WmSOt#{uw#{x!P#{!P!Q+v!Q#O#{#P;'S#{;'S;=`$d<%lO#{U+{^mSOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q#{!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wU-O^mSqQOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q0o!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wQ.PXqQOY-zZ!P-z!P!Q.l!Q!}-z!}#O/Z#O#P0Y#P;'S-z;'S;=`0i<%lO-zQ.oP!P!Q.rQ.wUqQ#Z#[.r#]#^.r#a#b.r#g#h.r#i#j.r#m#n.rQ/^VOY/ZZ#O/Z#O#P/s#P#Q-z#Q;'S/Z;'S;=`0S<%lO/ZQ/vSOY/ZZ;'S/Z;'S;=`0S<%lO/ZQ0VP;=`<%l/ZQ0]SOY-zZ;'S-z;'S;=`0i<%lO-zQ0lP;=`<%l-zU0tWmSOt#{uw#{x!P#{!P!Q1^!Q#O#{#P;'S#{;'S;=`$d<%lO#{U1ebmSqQOt#{uw#{x#O#{#P#Z#{#Z#[1^#[#]#{#]#^1^#^#a#{#a#b1^#b#g#{#g#h1^#h#i#{#i#j1^#j#m#{#m#n1^#n;'S#{;'S;=`$d<%lO#{U2r[mSOY2mYZ#{Zt2mtu/Zuw2mwx/Zx#O2m#O#P/s#P#Q,w#Q;'S2m;'S;=`3h<%lO2mU3kP;=`<%l2mU3qP;=`<%l,wU3{UmSwQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U4fW!wQmSOt#{uw#{x!_#{!_!`5O!`#O#{#P;'S#{;'S;=`$d<%lO#{U5TVmSOt#{uw#{x#O#{#P#Q5j#Q;'S#{;'S;=`$d<%lO#{U5qU!vQmSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~6YO!r~U6aU!|QmSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6zUmSyQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7cYmSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{U8YUtQmSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U8qZmSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#U9d#U#o7^#o;'S#{;'S;=`$d<%lO#{U9i[mSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#`7^#`#a:_#a#o7^#o;'S#{;'S;=`$d<%lO#{U:d[mSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#g7^#g#h;Y#h#o7^#o;'S#{;'S;=`$d<%lO#{U;_[mSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#X7^#X#Y<T#Y#o7^#o;'S#{;'S;=`$d<%lO#{U<[YpQmSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{^=RY!sWmSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{^=xY!uWmSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{^>o[!tWmSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#f7^#f#g?e#g#o7^#o;'S#{;'S;=`$d<%lO#{U?j[mSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#i7^#i#j;Y#j#o7^#o;'S#{;'S;=`$d<%lO#{U@gU!OQmSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~AOO#R~",
|
tokenData: "AO~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'Vuw#{wx'[xy'ayz'zz{#{{|(e|}#{}!O(e!O!P#{!P!Q+X!Q![)S![!]3t!]!^%T!^!}#{!}#O4_#O#P6T#P#Q6Y#Q#R#{#R#S6s#S#T#{#T#Y7^#Y#Z8l#Z#b7^#b#c<z#c#f7^#f#g=q#g#h7^#h#i>h#i#o7^#o#p#{#p#q@`#q;'S#{;'S;=`$d<%l~#{~O#{~~@yS$QUrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qUrS!lYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[UrS#OQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZrS!mYOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mS!mYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O!w~~'aO!u~U'hUrS!rQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RUrS#TQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jWrSOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZYrSmQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OWrSOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oWrSmQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^WrSOt#{uw#{x!P#{!P!Q+v!Q#O#{#P;'S#{;'S;=`$d<%lO#{U+{^rSOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q#{!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wU-O^rSvQOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q0o!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wQ.PXvQOY-zZ!P-z!P!Q.l!Q!}-z!}#O/Z#O#P0Y#P;'S-z;'S;=`0i<%lO-zQ.oP!P!Q.rQ.wUvQ#Z#[.r#]#^.r#a#b.r#g#h.r#i#j.r#m#n.rQ/^VOY/ZZ#O/Z#O#P/s#P#Q-z#Q;'S/Z;'S;=`0S<%lO/ZQ/vSOY/ZZ;'S/Z;'S;=`0S<%lO/ZQ0VP;=`<%l/ZQ0]SOY-zZ;'S-z;'S;=`0i<%lO-zQ0lP;=`<%l-zU0tWrSOt#{uw#{x!P#{!P!Q1^!Q#O#{#P;'S#{;'S;=`$d<%lO#{U1ebrSvQOt#{uw#{x#O#{#P#Z#{#Z#[1^#[#]#{#]#^1^#^#a#{#a#b1^#b#g#{#g#h1^#h#i#{#i#j1^#j#m#{#m#n1^#n;'S#{;'S;=`$d<%lO#{U2r[rSOY2mYZ#{Zt2mtu/Zuw2mwx/Zx#O2m#O#P/s#P#Q,w#Q;'S2m;'S;=`3h<%lO2mU3kP;=`<%l2mU3qP;=`<%l,wU3{UrS|QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U4fW!}QrSOt#{uw#{x!_#{!_!`5O!`#O#{#P;'S#{;'S;=`$d<%lO#{U5TVrSOt#{uw#{x#O#{#P#Q5j#Q;'S#{;'S;=`$d<%lO#{U5qU!|QrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~6YO!x~U6aU#SQrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6zUrS!OQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7cYrSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{U8YUyQrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U8qZrSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#U9d#U#o7^#o;'S#{;'S;=`$d<%lO#{U9i[rSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#`7^#`#a:_#a#o7^#o;'S#{;'S;=`$d<%lO#{U:d[rSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#g7^#g#h;Y#h#o7^#o;'S#{;'S;=`$d<%lO#{U;_[rSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#X7^#X#Y<T#Y#o7^#o;'S#{;'S;=`$d<%lO#{U<[YuQrSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{^=RY!yWrSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{^=xY!{WrSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{^>o[!zWrSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#f7^#f#g?e#g#o7^#o;'S#{;'S;=`$d<%lO#{U?j[rSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#i7^#i#j;Y#j#o7^#o;'S#{;'S;=`$d<%lO#{U@gU!TQrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~AOO#X~",
|
||||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!k~~", 11)],
|
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!q~~", 11)],
|
||||||
topRules: {"Program":[0,20]},
|
topRules: {"Program":[0,25]},
|
||||||
specialized: [{term: 15, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 15, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
|
specialized: [{term: 20, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 20, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
|
||||||
tokenPrec: 1135
|
tokenPrec: 1150
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -532,6 +532,72 @@ describe('Assign', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('CompoundAssign', () => {
|
||||||
|
test('parses += operator', () => {
|
||||||
|
expect('x += 5').toMatchTree(`
|
||||||
|
CompoundAssign
|
||||||
|
AssignableIdentifier x
|
||||||
|
PlusEq +=
|
||||||
|
Number 5`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses -= operator', () => {
|
||||||
|
expect('count -= 1').toMatchTree(`
|
||||||
|
CompoundAssign
|
||||||
|
AssignableIdentifier count
|
||||||
|
MinusEq -=
|
||||||
|
Number 1`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses *= operator', () => {
|
||||||
|
expect('total *= 2').toMatchTree(`
|
||||||
|
CompoundAssign
|
||||||
|
AssignableIdentifier total
|
||||||
|
StarEq *=
|
||||||
|
Number 2`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses /= operator', () => {
|
||||||
|
expect('value /= 10').toMatchTree(`
|
||||||
|
CompoundAssign
|
||||||
|
AssignableIdentifier value
|
||||||
|
SlashEq /=
|
||||||
|
Number 10`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses %= operator', () => {
|
||||||
|
expect('remainder %= 3').toMatchTree(`
|
||||||
|
CompoundAssign
|
||||||
|
AssignableIdentifier remainder
|
||||||
|
ModuloEq %=
|
||||||
|
Number 3`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses compound assignment with expression', () => {
|
||||||
|
expect('x += 1 + 2').toMatchTree(`
|
||||||
|
CompoundAssign
|
||||||
|
AssignableIdentifier x
|
||||||
|
PlusEq +=
|
||||||
|
BinOp
|
||||||
|
Number 1
|
||||||
|
Plus +
|
||||||
|
Number 2`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses compound assignment with function call', () => {
|
||||||
|
expect('total += add 5 3').toMatchTree(`
|
||||||
|
CompoundAssign
|
||||||
|
AssignableIdentifier total
|
||||||
|
PlusEq +=
|
||||||
|
FunctionCall
|
||||||
|
Identifier add
|
||||||
|
PositionalArg
|
||||||
|
Number 5
|
||||||
|
PositionalArg
|
||||||
|
Number 3`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('DotGet whitespace sensitivity', () => {
|
describe('DotGet whitespace sensitivity', () => {
|
||||||
test('no whitespace - DotGet works when identifier in scope', () => {
|
test('no whitespace - DotGet works when identifier in scope', () => {
|
||||||
expect('basename = 5; basename.prop').toMatchTree(`
|
expect('basename = 5; basename.prop').toMatchTree(`
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,17 @@ const chooseIdentifierToken = (input: InputStream, stack: Stack): number => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextCh = getFullCodePoint(input, peekPos)
|
const nextCh = getFullCodePoint(input, peekPos)
|
||||||
|
const nextCh2 = getFullCodePoint(input, peekPos + 1)
|
||||||
|
|
||||||
|
// Check for compound assignment operators: +=, -=, *=, /=, %=
|
||||||
|
if ([43/* + */, 45/* - */, 42/* * */, 47/* / */, 37/* % */].includes(nextCh) && nextCh2 === 61/* = */) {
|
||||||
|
// Found compound operator, check if it's followed by whitespace
|
||||||
|
const charAfterOp = getFullCodePoint(input, peekPos + 2)
|
||||||
|
if (isWhiteSpace(charAfterOp) || charAfterOp === -1 /* EOF */) {
|
||||||
|
return AssignableIdentifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (nextCh === 61 /* = */) {
|
if (nextCh === 61 /* = */) {
|
||||||
// Found '=', but check if it's followed by whitespace
|
// Found '=', but check if it's followed by whitespace
|
||||||
// If '=' is followed by non-whitespace (like '=cool*'), it won't be tokenized as Eq
|
// If '=' is followed by non-whitespace (like '=cool*'), it won't be tokenized as Eq
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user