Compare commits

...

3 Commits

Author SHA1 Message Date
4c794944ef Merge pull request 'bitwise' (#36) from bitwise into main
Reviewed-on: #36
2025-11-09 00:11:19 +00:00
99a5aa5312 update reef, fix precedence test 2025-11-08 16:11:05 -08:00
7bbf43a725 bitwise operators 2025-11-08 16:10:13 -08:00
9 changed files with 350 additions and 62 deletions

View File

@ -243,6 +243,24 @@ export class Compiler {
case '%': case '%':
instructions.push(['MOD']) instructions.push(['MOD'])
break break
case 'band':
instructions.push(['BIT_AND'])
break
case 'bor':
instructions.push(['BIT_OR'])
break
case 'bxor':
instructions.push(['BIT_XOR'])
break
case '<<':
instructions.push(['BIT_SHL'])
break
case '>>':
instructions.push(['BIT_SHR'])
break
case '>>>':
instructions.push(['BIT_USHR'])
break
default: default:
throw new CompilerError(`Unsupported binary operator: ${opValue}`, op.from, op.to) throw new CompilerError(`Unsupported binary operator: ${opValue}`, op.from, op.to)
} }

View File

@ -0,0 +1,178 @@
import { expect, describe, test } from 'bun:test'
describe('bitwise operators', () => {
describe('band (bitwise AND)', () => {
test('basic AND operation', () => {
expect('5 band 3').toEvaluateTo(1)
// 5 = 0101, 3 = 0011, result = 0001 = 1
})
test('AND with zero', () => {
expect('5 band 0').toEvaluateTo(0)
})
test('AND with all bits set', () => {
expect('15 band 7').toEvaluateTo(7)
// 15 = 1111, 7 = 0111, result = 0111 = 7
})
test('AND in assignment', () => {
expect('x = 12 band 10').toEvaluateTo(8)
// 12 = 1100, 10 = 1010, result = 1000 = 8
})
})
describe('bor (bitwise OR)', () => {
test('basic OR operation', () => {
expect('5 bor 3').toEvaluateTo(7)
// 5 = 0101, 3 = 0011, result = 0111 = 7
})
test('OR with zero', () => {
expect('5 bor 0').toEvaluateTo(5)
})
test('OR with all bits set', () => {
expect('8 bor 4').toEvaluateTo(12)
// 8 = 1000, 4 = 0100, result = 1100 = 12
})
})
describe('bxor (bitwise XOR)', () => {
test('basic XOR operation', () => {
expect('5 bxor 3').toEvaluateTo(6)
// 5 = 0101, 3 = 0011, result = 0110 = 6
})
test('XOR with itself returns zero', () => {
expect('5 bxor 5').toEvaluateTo(0)
})
test('XOR with zero returns same value', () => {
expect('7 bxor 0').toEvaluateTo(7)
})
test('XOR in assignment', () => {
expect('result = 8 bxor 12').toEvaluateTo(4)
// 8 = 1000, 12 = 1100, result = 0100 = 4
})
})
describe('bnot (bitwise NOT)', () => {
test('NOT of positive number', () => {
expect('bnot 5').toEvaluateTo(-6)
// ~5 = -6 (two\'s complement)
})
test('NOT of zero', () => {
expect('bnot 0').toEvaluateTo(-1)
})
test('NOT of negative number', () => {
expect('bnot -1').toEvaluateTo(0)
})
test('double NOT returns original', () => {
expect('bnot (bnot 5)').toEvaluateTo(5)
})
})
describe('<< (left shift)', () => {
test('basic left shift', () => {
expect('5 << 2').toEvaluateTo(20)
// 5 << 2 = 20
})
test('shift by zero', () => {
expect('5 << 0').toEvaluateTo(5)
})
test('shift by one', () => {
expect('3 << 1').toEvaluateTo(6)
})
test('large shift', () => {
expect('1 << 10').toEvaluateTo(1024)
})
})
describe('>> (signed right shift)', () => {
test('basic right shift', () => {
expect('20 >> 2').toEvaluateTo(5)
// 20 >> 2 = 5
})
test('shift by zero', () => {
expect('20 >> 0').toEvaluateTo(20)
})
test('preserves sign for negative numbers', () => {
expect('-20 >> 2').toEvaluateTo(-5)
// Sign is preserved
})
test('negative number right shift', () => {
expect('-8 >> 1').toEvaluateTo(-4)
})
})
describe('>>> (unsigned right shift)', () => {
test('basic unsigned right shift', () => {
expect('20 >>> 2').toEvaluateTo(5)
})
test('unsigned shift of -1', () => {
expect('-1 >>> 1').toEvaluateTo(2147483647)
// -1 >>> 1 = 2147483647 (unsigned, no sign extension)
})
test('unsigned shift of negative number', () => {
expect('-8 >>> 1').toEvaluateTo(2147483644)
})
})
describe('compound expressions', () => {
test('multiple bitwise operations', () => {
expect('(5 band 3) bor (8 bxor 12)').toEvaluateTo(5)
// (5 & 3) | (8 ^ 12) = 1 | 4 = 5
})
test('bitwise with variables', () => {
expect(`
a = 5
b = 3
a bor b
`).toEvaluateTo(7)
})
test('shift operations with variables', () => {
expect(`
x = 16
y = 2
x >> y
`).toEvaluateTo(4)
})
test('mixing shifts and bitwise', () => {
expect('(8 << 1) band 15').toEvaluateTo(0)
// (8 << 1) & 15 = 16 & 15 = 0
})
test('mixing shifts and bitwise 2', () => {
expect('(7 << 1) band 15').toEvaluateTo(14)
// (7 << 1) & 15 = 14 & 15 = 14
})
})
describe('precedence', () => {
test('bitwise has correct precedence with arithmetic', () => {
expect('1 + 2 band 3').toEvaluateTo(3)
// (1 + 2) & 3 = 3 & 3 = 3
})
test('shift has correct precedence', () => {
expect('4 + 8 << 1').toEvaluateTo(24)
// (4 + 8) << 1 = 12 << 1 = 24
})
})
})

View File

@ -5,6 +5,12 @@ type Operator = { str: string; tokenName: keyof typeof terms }
const operators: Array<Operator> = [ const operators: Array<Operator> = [
{ str: 'and', tokenName: 'And' }, { str: 'and', tokenName: 'And' },
{ str: 'or', tokenName: 'Or' }, { str: 'or', tokenName: 'Or' },
{ str: 'band', tokenName: 'Band' },
{ str: 'bor', tokenName: 'Bor' },
{ str: 'bxor', tokenName: 'Bxor' },
{ str: '>>>', tokenName: 'Ushr' }, // Must come before >>
{ str: '>>', tokenName: 'Shr' },
{ str: '<<', tokenName: 'Shl' },
{ str: '>=', tokenName: 'Gte' }, { str: '>=', tokenName: 'Gte' },
{ str: '<=', tokenName: 'Lte' }, { str: '<=', tokenName: 'Lte' },
{ str: '!=', tokenName: 'Neq' }, { str: '!=', tokenName: 'Neq' },

View File

@ -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, PlusEq, MinusEq, StarEq, SlashEq, ModuloEq } @external tokens operatorTokenizer from "./operatorTokenizer" { Star, Slash, Plus, Minus, And, Or, Eq, EqEq, Neq, Lt, Lte, Gt, Gte, Modulo, PlusEq, MinusEq, StarEq, SlashEq, ModuloEq, Band, Bor, Bxor, Shl, Shr, Ushr }
@tokens { @tokens {
@precedence { Number Regex } @precedence { Number Regex }
@ -51,6 +51,7 @@ null { @specialize[@name=Null]<Identifier, "null"> }
comparison @left, comparison @left,
multiplicative @left, multiplicative @left,
additive @left, additive @left,
bitwise @left,
call, call,
functionWithNewlines functionWithNewlines
} }
@ -189,7 +190,13 @@ BinOp {
(expression | BinOp) !multiplicative Star (expression | BinOp) | (expression | BinOp) !multiplicative Star (expression | BinOp) |
(expression | BinOp) !multiplicative Slash (expression | BinOp) | (expression | BinOp) !multiplicative Slash (expression | BinOp) |
(expression | BinOp) !additive Plus (expression | BinOp) | (expression | BinOp) !additive Plus (expression | BinOp) |
(expression | BinOp) !additive Minus (expression | BinOp) (expression | BinOp) !additive Minus (expression | BinOp) |
(expression | BinOp) !bitwise Band (expression | BinOp) |
(expression | BinOp) !bitwise Bor (expression | BinOp) |
(expression | BinOp) !bitwise Bxor (expression | BinOp) |
(expression | BinOp) !bitwise Shl (expression | BinOp) |
(expression | BinOp) !bitwise Shr (expression | BinOp) |
(expression | BinOp) !bitwise Ushr (expression | BinOp)
} }
ParenExpr { ParenExpr {

View File

@ -19,49 +19,55 @@ export const
StarEq = 17, StarEq = 17,
SlashEq = 18, SlashEq = 18,
ModuloEq = 19, ModuloEq = 19,
Identifier = 20, Band = 20,
AssignableIdentifier = 21, Bor = 21,
Word = 22, Bxor = 22,
IdentifierBeforeDot = 23, Shl = 23,
Do = 24, Shr = 24,
Comment = 25, Ushr = 25,
Program = 26, Identifier = 26,
PipeExpr = 27, AssignableIdentifier = 27,
WhileExpr = 29, Word = 28,
keyword = 71, IdentifierBeforeDot = 29,
ConditionalOp = 31, Do = 30,
ParenExpr = 32, Comment = 31,
FunctionCallWithNewlines = 33, Program = 32,
DotGet = 34, PipeExpr = 33,
Number = 35, WhileExpr = 35,
PositionalArg = 36, keyword = 77,
FunctionDef = 37, ConditionalOp = 37,
Params = 38, ParenExpr = 38,
NamedParam = 39, FunctionCallWithNewlines = 39,
NamedArgPrefix = 40, DotGet = 40,
String = 41, Number = 41,
StringFragment = 42, PositionalArg = 42,
Interpolation = 43, FunctionDef = 43,
EscapeSeq = 44, Params = 44,
Boolean = 45, NamedParam = 45,
Null = 46, NamedArgPrefix = 46,
colon = 47, String = 47,
CatchExpr = 48, StringFragment = 48,
Block = 50, Interpolation = 49,
FinallyExpr = 51, EscapeSeq = 50,
Underscore = 54, Boolean = 51,
NamedArg = 55, Null = 52,
IfExpr = 56, colon = 53,
FunctionCall = 58, CatchExpr = 54,
ElseIfExpr = 59, Block = 56,
ElseExpr = 61, FinallyExpr = 57,
FunctionCallOrIdentifier = 62, Underscore = 60,
BinOp = 63, NamedArg = 61,
Regex = 64, IfExpr = 62,
Dict = 65, FunctionCall = 64,
Array = 66, ElseIfExpr = 65,
FunctionCallWithBlock = 67, ElseExpr = 67,
TryExpr = 68, FunctionCallOrIdentifier = 68,
Throw = 70, BinOp = 69,
CompoundAssign = 72, Regex = 70,
Assign = 73 Dict = 71,
Array = 72,
FunctionCallWithBlock = 73,
TryExpr = 74,
Throw = 76,
CompoundAssign = 78,
Assign = 79

View File

@ -4,24 +4,24 @@ import {operatorTokenizer} from "./operatorTokenizer"
import {tokenizer, specializeKeyword} from "./tokenizer" import {tokenizer, specializeKeyword} from "./tokenizer"
import {trackScope} from "./parserScopeContext" import {trackScope} from "./parserScopeContext"
import {highlighting} from "./highlight" import {highlighting} from "./highlight"
const spec_Identifier = {__proto__:null,while:60, null:92, catch:98, finally:104, end:106, if:114, else:120, try:138, throw:142} const spec_Identifier = {__proto__:null,while:72, null:104, catch:110, finally:116, end:118, if:126, else:132, try:150, throw:154}
export const parser = LRParser.deserialize({ export const parser = LRParser.deserialize({
version: 14, version: 14,
states: "<OQYQbOOO!dOpO'#DOO!iOSO'#DVO$wQcO'#DlO'[QcO'#E[OOQ`'#Ej'#EjO'uQRO'#DmO)[QcO'#EYO)lQbO'#C|OOQa'#Do'#DoO+qQbO'#DpOOQa'#E['#E[O+xQcO'#E[O,cQcO'#EZO-`QcO'#EYO-jQRO'#DvOOQ`'#EY'#EYO.OQbO'#EYO.VQQO'#EXOOQ`'#EX'#EXOOQ`'#Dx'#DxQYQbOOO.bQbO'#DRO.mQbO'#DfO/bQbO'#CyO0VQQO'#DrO/bQbO'#DtO0[ObO,59jO0gQbO'#DXO0oQWO'#DYOOOO'#Eb'#EbOOOO'#D}'#D}O1TOSO,59qOOQa,59q,59qOOQ`'#DQ'#DQO1cQbO'#DeOOQ`'#E`'#E`OOQ`'#EQ'#EQO1mQbO,5:SOOQa'#EZ'#EZO/bQbO,5:XO/bQbO,5:XO/bQbO,5:XO/bQbO,59gO/bQbO,59gO/bQbO,59gOOQ`'#Dz'#DzO)lQbO,59hO2gQcO'#DlO2nQcO'#E[O3ZQRO,59hO3bQQO,59hO3gQQO,59hO3oQQO,59hO4xQRO,59hO5PQRO,59hO5bQQO'#CwO5gQbO,5:[O5nQQO,5:ZOOQa,5:[,5:[O5yQbO,5:[O6TQbO,5:cO6TQbO,5:bO7[QbO,5:]O7cQbO,59cOOQ`,5:s,5:sO6TQbO'#DyOOQ`-E7v-E7vOOQ`'#D{'#D{O7}QbO'#DSO8YQbO'#DTOOQO'#D|'#D|O8QQQO'#DSO8hQQO,59mO8mQcO'#EZO9gQRO'#EiO:^QRO'#EiOOQO'#Ei'#EiO:eQQO,5:QO:jQRO,59eO:qQRO,59eO7[QbO,5:^O:|QcO,5:`O<UQcO,5:`O<fQcO,5:`OOQa1G/U1G/UOOOO,59s,59sOOOO,59t,59tOOOO-E7{-E7{OOQa1G/]1G/]OOQ`,5:P,5:POOQ`-E8O-E8OOOQa1G/s1G/sO=eQcO1G/sO=oQcO1G/sO=yQcO1G/sOOQa1G/R1G/RO?uQcO1G/RO?|QcO1G/RO@TQcO1G/ROOQ`-E7x-E7xO@[QRO1G/SO@cQQO1G/SO@hQQO1G/SO@pQQO1G/SO@{QRO1G/SOASQRO1G/SOAeQbO,59iOAoQQO1G/SOOQa1G/S1G/SOAwQQO1G/uOOQa1G/v1G/vOBSQbO1G/vOOQO'#ES'#ESOAwQQO1G/uOOQa1G/u1G/uOOQ`'#ET'#ETOBSQbO1G/vOB^QbO1G/}OBxQbO1G/|OCdQbO'#D`OCuQbO'#D`ODYQbO1G/wOOQ`-E7w-E7wOOQ`,5:e,5:eOOQ`-E7y-E7yODeQQO,59nOOQO,59o,59oOOQO-E7z-E7zODmQbO1G/XO7[QbO1G/lO7[QbO1G/PODtQbO1G/xOEPQQO7+$nOOQa7+$n7+$nOEXQQO1G/TOEaQQO7+%aOOQa7+%a7+%aOElQbO7+%bOOQa7+%b7+%bOOQO-E8Q-E8QOOQ`-E8R-E8ROOQ`'#EO'#EOOEvQQO'#EOOFOQbO'#EhOOQ`,59z,59zOFcQbO'#D^OFhQQO'#DaOOQ`7+%c7+%cOFmQbO7+%cOFrQbO7+%cOFzQbO7+$sOGYQbO7+$sOGjQbO7+%WOGrQbO7+$kOOQ`7+%d7+%dOGwQbO7+%dOG|QbO7+%dOOQa<<HY<<HYOHUQbO7+$oOHcQQO7+$oOOQa<<H{<<H{OOQa<<H|<<H|OOQ`,5:j,5:jOOQ`-E7|-E7|OHkQQO,59xO7[QbO,59{OOQ`<<H}<<H}OHpQbO<<H}OOQ`<<H_<<H_OHuQbO<<H_OHzQbO<<H_OISQbO<<H_OOQ`'#ER'#EROI_QbO<<HrOIgQbO'#DkOOQ`<<Hr<<HrOIoQbO<<HrOOQ`<<HV<<HVOOQ`<<IO<<IOOItQbO<<IOOOQO,5:k,5:kOIyQbO<<HZOOQO-E7}-E7}O7[QbO1G/dOOQ`1G/g1G/gOOQ`AN>iAN>iOOQ`AN=yAN=yOJWQbOAN=yOJ]QbOAN=yOOQ`-E8P-E8POOQ`AN>^AN>^OJeQbOAN>^O.mQbO,5:TO7[QbO,5:VOOQ`AN>jAN>jPAeQbO'#DzOOQ`7+%O7+%OOOQ`G23eG23eOJjQbOG23ePIjQbO'#DiOOQ`G23xG23xOJoQQO1G/oOOQ`1G/q1G/qOOQ`LD)PLD)PO7[QbO7+%ZOOQ`<<Hu<<Hu", states: "<bQYQbOOO!dOpO'#DUO!iOSO'#D]O%ZQcO'#DrO(QQcO'#EbOOQ`'#Ep'#EpO(kQRO'#DsO*mQcO'#E`O+WQbO'#DSOOQa'#Du'#DuO-]QbO'#DvOOQa'#Eb'#EbO-dQcO'#EbO/_QcO'#EaO0dQcO'#E`O0nQRO'#D|OOQ`'#E`'#E`O1SQbO'#E`O1ZQQO'#E_OOQ`'#E_'#E_OOQ`'#EO'#EOQYQbOOO1fQbO'#DXO1qQbO'#DlO2fQbO'#DPO3ZQQO'#DxO2fQbO'#DzO3`ObO,59pO3kQbO'#D_O3sQWO'#D`OOOO'#Eh'#EhOOOO'#ET'#ETO4XOSO,59wOOQa,59w,59wOOQ`'#DW'#DWO4gQbO'#DkOOQ`'#Ef'#EfOOQ`'#EW'#EWO4qQbO,5:YOOQa'#Ea'#EaO2fQbO,5:_O2fQbO,5:_O2fQbO,5:_O2fQbO,5:_O2fQbO,59mO2fQbO,59mO2fQbO,59mOOQ`'#EQ'#EQO+WQbO,59nO5kQcO'#DrO5rQcO'#EbO5yQRO,59nO6TQQO,59nO6YQQO,59nO6bQQO,59nO6mQRO,59nO6tQRO,59nO7VQQO'#C}O7[QbO,5:bO7cQQO,5:aOOQa,5:b,5:bO7nQbO,5:bO7xQbO,5:iO7xQbO,5:hO9PQbO,5:cO9WQbO,59iOOQ`,5:y,5:yO7xQbO'#EPOOQ`-E7|-E7|OOQ`'#ER'#ERO9rQbO'#DYO9}QbO'#DZOOQO'#ES'#ESO9uQQO'#DYO:]QQO,59sO:bQcO'#EaO;[QRO'#EoO<RQRO'#EoOOQO'#Eo'#EoO<YQQO,5:WO<_QRO,59kO<fQRO,59kO9PQbO,5:dO<qQcO,5:fO>PQcO,5:fO>mQcO,5:fOOQa1G/[1G/[OOOO,59y,59yOOOO,59z,59zOOOO-E8R-E8ROOQa1G/c1G/cOOQ`,5:V,5:VOOQ`-E8U-E8UOOQa1G/y1G/yO@fQcO1G/yO@pQcO1G/yOBOQcO1G/yOBYQcO1G/yOBgQcO1G/yOOQa1G/X1G/XOCuQcO1G/XOC|QcO1G/XODTQcO1G/XOOQ`-E8O-E8OOD[QRO1G/YODfQQO1G/YODkQQO1G/YODsQQO1G/YOEOQRO1G/YOEVQRO1G/YOEhQbO,59oOErQQO1G/YOOQa1G/Y1G/YOEzQQO1G/{OOQa1G/|1G/|OFVQbO1G/|OOQO'#EY'#EYOEzQQO1G/{OOQa1G/{1G/{OOQ`'#EZ'#EZOFVQbO1G/|OFaQbO1G0TOF{QbO1G0SOGgQbO'#DfOGxQbO'#DfOH]QbO1G/}OOQ`-E7}-E7}OOQ`,5:k,5:kOOQ`-E8P-E8POHhQQO,59tOOQO,59u,59uOOQO-E8Q-E8QOHpQbO1G/_O9PQbO1G/rO9PQbO1G/VOHwQbO1G0OOISQQO7+$tOOQa7+$t7+$tOI[QQO1G/ZOIdQQO7+%gOOQa7+%g7+%gOIoQbO7+%hOOQa7+%h7+%hOOQO-E8W-E8WOOQ`-E8X-E8XOOQ`'#EU'#EUOIyQQO'#EUOJRQbO'#EnOOQ`,5:Q,5:QOJfQbO'#DdOJkQQO'#DgOOQ`7+%i7+%iOJpQbO7+%iOJuQbO7+%iOJ}QbO7+$yOK]QbO7+$yOKmQbO7+%^OKuQbO7+$qOOQ`7+%j7+%jOKzQbO7+%jOLPQbO7+%jOOQa<<H`<<H`OLXQbO7+$uOLfQQO7+$uOOQa<<IR<<IROOQa<<IS<<ISOOQ`,5:p,5:pOOQ`-E8S-E8SOLnQQO,5:OO9PQbO,5:ROOQ`<<IT<<ITOLsQbO<<ITOOQ`<<He<<HeOLxQbO<<HeOL}QbO<<HeOMVQbO<<HeOOQ`'#EX'#EXOMbQbO<<HxOMjQbO'#DqOOQ`<<Hx<<HxOMrQbO<<HxOOQ`<<H]<<H]OOQ`<<IU<<IUOMwQbO<<IUOOQO,5:q,5:qOM|QbO<<HaOOQO-E8T-E8TO9PQbO1G/jOOQ`1G/m1G/mOOQ`AN>oAN>oOOQ`AN>PAN>PONZQbOAN>PON`QbOAN>POOQ`-E8V-E8VOOQ`AN>dAN>dONhQbOAN>dO1qQbO,5:ZO9PQbO,5:]OOQ`AN>pAN>pPEhQbO'#EQOOQ`7+%U7+%UOOQ`G23kG23kONmQbOG23kPMmQbO'#DoOOQ`G24OG24OONrQQO1G/uOOQ`1G/w1G/wOOQ`LD)VLD)VO9PQbO7+%aOOQ`<<H{<<H{",
stateData: "Jw~O!zOSiOS~OdROe_OfZOgPOhfOnhOsZO}ZO!OZO!ZgO!bZO!giO!ijO#PWO#QcO#TQO#`XO#aYO~O#RkO~OznO#TqO#VlO#WmO~OdwOfZOgPOhfOsZOxsO}ZO!OZO!WrO!bZO#PWO#TQO#`XO#aYOP!}XQ!}XR!}XS!}XT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X^!}Xl!`X!P!`X#_!`X~O#Q!`X#c!`X!R!`X!U!`X!V!`X!^!`X~P!wOdwOfZOgPOhfOsZOxsO}ZO!OZO!WrO!bZO#PWO#TQO#`XO#aYOP#OXQ#OXR#OXS#OXT#OXU#OXW#OXX#OXY#OXZ#OX[#OX]#OX^#OXl#OX#_#OX~O#Q#OX#c#OX!P#OX!R#OX!U#OX!V#OX!^#OX~P%_OPyOQyORzOSzOT|OU}OW{OX{OY{OZ{O[{O]{O^xO~Ol!|X#Q!|X#c!|X!R!|X!U!|X!V!|X#_!|X!^!|X~OPyOQyORzOSzO~P(pOd!QOe_OfZOgPOhfOnhOsZO}ZO!OZO!ZgO!bZO!giO!ijO#PWO#Q!OO#TQO#`XO#aYO~OdwOfZOgPOsZOxsO}ZO!OZO!bZO#PWO#Q!OO#TQO#`XO#aYO~O#b!]O~P*vOV!_O#Q#OX#c#OX!R#OX!U#OX!V#OX!^#OX~P&ZOP!}XQ!}XR!}XS!}XT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X^!}X~P(pOT|OU}O~P(pOV!_O_!`O`!`Oa!`Ob!`Oc!`O~O!P!aO~P(pOl!dO#Q!cO#c!cO~Od!fOx!hO!PvP~Od!lOfZOgPOsZO}ZO!OZO!bZO#PWO#TQO#`XO#aYO~OdwOfZOgPOsZO}ZO!OZO!bZO#PWO#TQO#`XO#aYO~O!P!sO~Od!wOs!wO#PWO~Od!xO#PWO~O#T!yO#V!yO#W!yO#X!yO#Y!yO#Z!yO~OznO#T!{O#VlO#WmO~OhfO!W!|O~P/bOhfOxsO!WrOl![a!P![a#Q![a#c![a#_![a!R![a!U![a!V![a!^![a~P/bO#Q!OO~P!wO#Q!OO~P%_OPyOQyORzOSzO#Q!OOl!|X~O#_#aO~P2uO#_#aO~O#_#aOl!|X~O!P!aO#_#aOl!|X~OP!}XQ!}XR!}XS!}XT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X^!}Xl!|X~O#_#aO~P3zOT|OU}O#Q!OO#_#aOl!|X~Ol!dO~O#b#cO~P*vOxsO#Q#eO#b#gO~O#Q#hO#b#cO~P/bOdROe_OfZOgPOhfOnhOsZO}ZO!OZO!ZgO!bZO!giO!ijO#PWO#TQO#`XO#aYO~O#Q#mO~P6TOl!dO#Qka#cka#_ka!Rka!Uka!Vka!^ka~Od!fOx!hO!PvX~Os#sO}#sO!O#sO#TQO~O!P#uO~OhfOxsO!WrOT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X!P!}X~P/bOT|OU}O!P#]X~OT|OU}OW{OX{OY{OZ{O[{O]{O~O!P#]X~P9rO!P#vO~O!P#wO~P9rOT|OU}O!P#wO~Ol!ha#Q!ha#c!ha!R!ha!U!ha!V!ha#_!ha!^!ha~P'uOl!ha#Q!ha#c!ha!R!ha!U!ha!V!ha#_!ha!^!ha~OPyOQyORzOSzO~P;jOT|OU}O~P;jO^xOR!aiS!ail!ai#Q!ai#c!ai#_!ai!R!ai!U!ai!V!ai!^!ai~OP!aiQ!ai~P<pOPyOQyO~P<pOPyOQyOR!aiS!ail!ai#Q!ai#c!ai#_!ai!R!ai!U!ai!V!ai!^!ai~OW{OX{OY{OZ{O[{O]{OToiloi#Qoi#coi#_oi!Poi!Roi!Uoi!Voi!^oi~OU}O~P>qOU}O~P?TOUoi~P>qO#_#zO~P2uO#_#zO~O#_#zOl!|X~O!P!aO#_#zOl!|X~O#_#zO~P3zOT|OU}O#Q!OO#_#zOl!|X~OhfO!WrO~P*vO#Q!OO#_#zO~OxsO#Q#eO#b#}O~O#Q#hO#b$PO~P/bOl!dO#Q!ki#c!ki!R!ki!U!ki!V!ki#_!ki!^!ki~Ol!dO#Q!ji#c!ji!R!ji!U!ji!V!ji#_!ji!^!ji~Ol!dO!R!SX!U!SX!V!SX!^!SX~O#Q$SO!R#[P!U#[P!V#[P!^#[P~P6TO!R$WO!U$XO!V$YO~Ox!hO!Pva~O#Q$^O~P6TO!R$WO!U$XO!V$aO~O#Q!OO#_$dO~O#Q!OO#_qi~OxsO#Q#eO#b$gO~O#Q#hO#b$hO~P/bOl!dO#Q$iO~O#Q$SO!R#[X!U#[X!V#[X!^#[X~P6TOd$kO~O!P$lO~O!V$mO~O!U$XO!V$mO~Ol!dO!R$WO!U$XO!V$oO~O#Q$SO!R#[P!U#[P!V#[P~P6TO!V$vO!^$uO~O!V$xO~O!V$yO~O!U$XO!V$yO~OhfO!WrO#_qq~P*vO#Q!OO#_qq~O!P%OO~O!V%QO~O!V%RO~O!U$XO!V%RO~O!R$WO!U$XO!V%RO~O!V%VO!^$uO~O!P%YO!Z%XO~O!V%VO~O!V%ZO~OhfO!WrO#_qy~P*vO!V%^O~O!U$XO!V%^O~O!V%aO~O!V%dO~O!P%eO~Os!b~", stateData: "Nz~O#QOSoOS~OjROk_OlZOmPOnfOthOyZO!TZO!UZO!agO!hZO!miO!ojO#VWO#WcO#ZQO#fXO#gYO~O#XkO~O!QnO#ZqO#]lO#^mO~OjwOlZOmPOnfOyZO!OsO!TZO!UZO!^rO!hZO#VWO#ZQO#fXO#gYOP#TXQ#TXR#TXS#TXT#TXU#TXW#TXX#TXY#TXZ#TX[#TX]#TX^#TXd#TXe#TXf#TXg#TXh#TXi#TXr!fX!V!fX#e!fX~O#W!fX#i!fX!X!fX![!fX!]!fX!d!fX~P!wOjwOlZOmPOnfOyZO!OsO!TZO!UZO!^rO!hZO#VWO#ZQO#fXO#gYOP#UXQ#UXR#UXS#UXT#UXU#UXW#UXX#UXY#UXZ#UX[#UX]#UX^#UXd#UXe#UXf#UXg#UXh#UXi#UXr#UX#e#UX~O#W#UX#i#UX!V#UX!X#UX![#UX!]#UX!d#UX~P%qOPyOQyORzOSzOT}OU!OOW|OX|OY|OZ|O[|O]|O^xOd{Oe{Of{Og{Oh{Oi{O~OPyOQyORzOSzOd{Oe{Of{Og{Oh{Oi{Or#SX~O#W#SX#i#SX!X#SX![#SX!]#SX#e#SX!d#SX~P)xOj!ROk_OlZOmPOnfOthOyZO!TZO!UZO!agO!hZO!miO!ojO#VWO#W!PO#ZQO#fXO#gYO~OjwOlZOmPOyZO!OsO!TZO!UZO!hZO#VWO#W!PO#ZQO#fXO#gYO~O#h!^O~P,bOV!`O#W#UX#i#UX!X#UX![#UX!]#UX!d#UX~P&mOP#TXQ#TXR#TXS#TXT#TXU#TXW#TXX#TXY#TXZ#TX[#TX]#TX^#TXd#TXe#TXf#TXg#TXh#TXi#TXr#SX~O#W#SX#i#SX!X#SX![#SX!]#SX#e#SX!d#SX~P-}Or#SX#W#SX#i#SX!X#SX![#SX!]#SX#e#SX!d#SX~OT}OU!OO~P/xOV!`O_!aO`!aOa!aOb!aOc!aO~O!V!bO~P/xOr!eO#W!dO#i!dO~Oj!gO!O!iO!V|P~Oj!mOlZOmPOyZO!TZO!UZO!hZO#VWO#ZQO#fXO#gYO~OjwOlZOmPOyZO!TZO!UZO!hZO#VWO#ZQO#fXO#gYO~O!V!tO~Oj!xOy!xO#VWO~Oj!yO#VWO~O#Z!zO#]!zO#^!zO#_!zO#`!zO#a!zO~O!QnO#Z!|O#]lO#^mO~OnfO!^!}O~P2fOnfO!OsO!^rOr!ba!V!ba#W!ba#i!ba#e!ba!X!ba![!ba!]!ba!d!ba~P2fO#W!PO~P!wO#W!PO~P%qO#W!PO#e#dO~P)xO#e#dO~O#e#dOr#SX~O!V!bO#e#dOr#SX~O#e#dO~P-}OT}OU!OO#W!PO#e#dOr#SX~Or!eO~O#h#fO~P,bO!OsO#W#hO#h#jO~O#W#kO#h#fO~P2fOjROk_OlZOmPOnfOthOyZO!TZO!UZO!agO!hZO!miO!ojO#VWO#ZQO#fXO#gYO~O#W#pO~P7xOr!eO#Wqa#iqa#eqa!Xqa![qa!]qa!dqa~Oj!gO!O!iO!V|X~Oy#vO!T#vO!U#vO#ZQO~O!V#xO~OnfO!OsO!^rOT#TXU#TXW#TXX#TXY#TXZ#TX[#TX]#TX!V#TX~P2fOT}OU!OO!V#cX~OT}OU!OOW|OX|OY|OZ|O[|O]|O~O!V#cX~P;gO!V#yO~O!V#zO~P;gOT}OU!OO!V#zO~Or!na#W!na#i!na!X!na![!na!]!na#e!na!d!na~P(kOPyOQyORzOSzOd{Oe{Of{Og{Oh{Oi{O~Or!na#W!na#i!na!X!na![!na!]!na#e!na!d!na~P=_OT}OU!OOr!na#W!na#i!na!X!na![!na!]!na#e!na!d!na~O^xOR!giS!gid!gie!gif!gig!gih!gii!gir!gi#W!gi#i!gi#e!gi!X!gi![!gi!]!gi!d!gi~OP!giQ!gi~P?_OPyOQyO~P?_OPyOQyOd!gie!gif!gig!gih!gii!gir!gi#W!gi#i!gi#e!gi!X!gi![!gi!]!gi!d!gi~OR!giS!gi~P@zORzOSzO^xO~P@zORzOSzO~P@zOW|OX|OY|OZ|O[|O]|OTuirui#Wui#iui#eui!Vui!Xui![ui!]ui!dui~OU!OO~PBqOU!OO~PCTOUui~PBqO#W!PO#e#}O~P)xO#e#}O~O#e#}Or#SX~O!V!bO#e#}Or#SX~O#e#}O~P-}OT}OU!OO#W!PO#e#}Or#SX~OnfO!^rO~P,bO#W!PO#e#}O~O!OsO#W#hO#h$QO~O#W#kO#h$SO~P2fOr!eO#W!qi#i!qi!X!qi![!qi!]!qi#e!qi!d!qi~Or!eO#W!pi#i!pi!X!pi![!pi!]!pi#e!pi!d!pi~Or!eO!X!YX![!YX!]!YX!d!YX~O#W$VO!X#bP![#bP!]#bP!d#bP~P7xO!X$ZO![$[O!]$]O~O!O!iO!V|a~O#W$aO~P7xO!X$ZO![$[O!]$dO~O#W!PO#e$gO~O#W!PO#ewi~O!OsO#W#hO#h$jO~O#W#kO#h$kO~P2fOr!eO#W$lO~O#W$VO!X#bX![#bX!]#bX!d#bX~P7xOj$nO~O!V$oO~O!]$pO~O![$[O!]$pO~Or!eO!X$ZO![$[O!]$rO~O#W$VO!X#bP![#bP!]#bP~P7xO!]$yO!d$xO~O!]${O~O!]$|O~O![$[O!]$|O~OnfO!^rO#ewq~P,bO#W!PO#ewq~O!V%RO~O!]%TO~O!]%UO~O![$[O!]%UO~O!X$ZO![$[O!]%UO~O!]%YO!d$xO~O!V%]O!a%[O~O!]%YO~O!]%^O~OnfO!^rO#ewy~P,bO!]%aO~O![$[O!]%aO~O!]%dO~O!]%gO~O!V%hO~Oy!h~",
goto: "7t#_PPPPPPPPPPPPPPPPPPPPPPPPPPP#`P#yP$`%Z&g&mP'u(R({)OP)UP*Z*ZPPP*_P*k+TPPP+k#`P,T,nP,r,x-_P.R/T#y#yP#yP#y#y0X0_0k1_1e1o1u1|2S2^2d2nPPP2x2|3q5aPPP6iP6yPPPPP6}7T7Zr`Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!UWR#Z!Pw`OWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%er^Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!XWS!mg%XQ!rhQ!vjQ#S}Q#U|R#^!PvSOeg!_!`!a!d!s#m#u#v#w$U$^$l%O%X%Y%e!SZRSYhjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%[S!RW!PQ!wkR!xlQ!TWR#Y!PrROe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%e!SwRSYhjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%[S!QW!PT!lg%XetRSv!Q!R!l#_$e$|%[r`Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%edrRSv!Q!R!l#_$e$|%[Q!UWQ!|sR#Z!PR!kfX!if!g!j#r#OZORSWYeghjsvxyz{|}!P!Q!R!Z!^!_!`!a!d!l!s#_#d#i#m#u#v#w$O$U$^$e$l$|%O%X%Y%[%eR#s!hTnQpQ$[#nQ$c#xQ$q$]R%T$rQ#n!aQ#x!sQ$_#vQ$`#wQ%P$lQ%]%OQ%c%YR%f%eQ$Z#nQ$b#xQ$n$[Q$p$]Q$z$cS%S$q$rR%_%TdtRSv!Q!R!l#_$e$|%[Q![YQ#b!ZX#e![#b#f#|vTOWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eT!og%XT$s$_$tQ$w$_R%W$twTOWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%erVOe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!SWQ!ujQ#OyQ#RzR#X!P#PZORSWYeghjsvxyz{|}!P!Q!R!Z!^!_!`!a!d!l!s#_#d#i#m#u#v#w$O$U$^$e$l$|%O%X%Y%[%e!WZRSYghjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%X%[w[OWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQeOR!ee^!bb!Y#j#k#l$T$]R#o!bQ!PWQ!ZY`#W!P!Z#_#`#y$e$|%[S#_!Q!RS#`!S!XS#y#X#^Q$e#{R$|$fQ!gfR#q!gQ!jfQ#r!gT#t!j#rQpQR!zpS$U#m$^R$j$UQ$f#{R$}$fYvRS!Q!R!lR!}vQ$t$_R%U$tQ#f![Q#|#bT$Q#f#|Q#i!^Q$O#dT$R#i$OTdOeSbOeS!YW!PQ#j!_Q#k!``#l!a!s#v#w$l%O%Y%eQ#p!dU$T#m$U$^R$]#uvUOWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%edrRSv!Q!R!l#_$e$|%[Q!^YS!ng%XQ!qhQ!tjQ!|sQ#OxQ#PyQ#QzQ#S{Q#T|Q#V}Q#d!ZX#h!^#d#i$Or]Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%e!WwRSYghjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%X%[Q!WWR#]!P[uRSv!Q!R!lQ#{#_V${$e$|%[ToQpQ$V#mR$r$^Q!pgR%b%XraOe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!VWR#[!P", goto: "8W#ePPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#fP$PP$f%a&n&tP'}(Z)T)WP)^P*d*dPPP*hP*t+^PPP+t#fP,^,wP,{-R-hP._/b$P$PP$PP$P$P0g0m0y1m1s1}2T2[2b2l2r2|PPP3W3[4P5rPPP6{P7]PPPPP7a7g7mr`Oe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hQ!VWR#^!Qw`OWe!Q!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hr^Oe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hQ!YWS!ng%[Q!shQ!wjQ#V!OQ#X}R#a!QvSOeg!`!a!b!e!t#p#x#y#z$X$a$o%R%[%]%h!UZRSYhjsvxyz{|}!O!R!S![!_!m#b#g#l$R$h%P%_S!SW!QQ!xkR!ylQ!UWR#]!QrROe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%h!UwRSYhjsvxyz{|}!O!R!S![!_!m#b#g#l$R$h%P%_S!RW!QT!mg%[etRSv!R!S!m#b$h%P%_r`Oe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hdrRSv!R!S!m#b$h%P%_Q!VWQ!}sR#^!QR!lfX!jf!h!k#u#QZORSWYeghjsvxyz{|}!O!Q!R!S![!_!`!a!b!e!m!t#b#g#l#p#x#y#z$R$X$a$h$o%P%R%[%]%_%hR#v!iTnQpQ$_#qQ$f#{Q$t$`R%W$uQ#q!bQ#{!tQ$b#yQ$c#zQ%S$oQ%`%RQ%f%]R%i%hQ$^#qQ$e#{Q$q$_Q$s$`Q$}$fS%V$t$uR%b%WdtRSv!R!S!m#b$h%P%_Q!]YQ#e![X#h!]#e#i$PvTOWe!Q!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hT!pg%[T$v$b$wQ$z$bR%Z$wwTOWe!Q!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hrVOe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hQ!TWQ!vjQ#PyQ#SzQ#U{R#[!Q#RZORSWYeghjsvxyz{|}!O!Q!R!S![!_!`!a!b!e!m!t#b#g#l#p#x#y#z$R$X$a$h$o%P%R%[%]%_%h!YZRSYghjsvxyz{|}!O!R!S![!_!m#b#g#l$R$h%P%[%_w[OWe!Q!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hQeOR!fe^!cb!Z#m#n#o$W$`R#r!cQ!QWQ![Y`#Z!Q![#b#c#|$h%P%_S#b!R!SS#c!T!YS#|#[#aQ$h$OR%P$iQ!hfR#t!hQ!kfQ#u!hT#w!k#uQpQR!{pS$X#p$aR$m$XQ$i$OR%Q$iYvRS!R!S!mR#OvQ$w$bR%X$wQ#i!]Q$P#eT$T#i$PQ#l!_Q$R#gT$U#l$RTdOeSbOeS!ZW!QQ#m!`Q#n!a`#o!b!t#y#z$o%R%]%hQ#s!eU$W#p$X$aR$`#xvUOWe!Q!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hdrRSv!R!S!m#b$h%P%_Q!_YS!og%[Q!rhQ!ujQ!}sQ#PxQ#QyQ#RzQ#T{Q#V|Q#W}Q#Y!OQ#g![X#k!_#g#l$Rr]Oe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%h!YwRSYghjsvxyz{|}!O!R!S![!_!m#b#g#l$R$h%P%[%_Q!XWR#`!Q[uRSv!R!S!mQ$O#bV%O$h%P%_ToQpQ$Y#pR$u$aQ!qgR%e%[raOe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hQ!WWR#_!Q",
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 Comment Program PipeExpr operator WhileExpr keyword ConditionalOp ParenExpr FunctionCall DotGet Number PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation EscapeSeq Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg IfExpr keyword FunctionCall ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword CompoundAssign Assign", nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq Band Bor Bxor Shl Shr Ushr Identifier AssignableIdentifier Word IdentifierBeforeDot Do Comment Program PipeExpr operator WhileExpr keyword ConditionalOp ParenExpr FunctionCall DotGet Number PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation EscapeSeq Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg IfExpr keyword FunctionCall ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword CompoundAssign Assign",
maxTerm: 111, maxTerm: 117,
context: trackScope, context: trackScope,
nodeProps: [ nodeProps: [
["closedBy", 47,"end"] ["closedBy", 53,"end"]
], ],
propSources: [highlighting], propSources: [highlighting],
skippedNodes: [0,25], skippedNodes: [0,31],
repeatNodeCount: 12, repeatNodeCount: 12,
tokenData: "IS~R}OX$OXY$mYZ%WZp$Opq$mqs$Ost%qtu'Yuw$Owx'_xy'dyz'}z{$O{|(h|}$O}!O(h!O!P$O!P!Q0o!Q!R)Y!R![+w![!]9[!]!^%W!^!}$O!}#O9u#O#P;k#P#Q;p#Q#R$O#R#S<Z#S#T$O#T#Y<t#Y#Z>`#Z#b<t#b#cC|#c#f<t#f#gEP#g#h<t#h#iFS#i#o<t#o#p$O#p#qHd#q;'S$O;'S;=`$g<%l~$O~O$O~~H}S$TUzSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OS$jP;=`<%l$O^$tUzS!zYOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU%_UzS#QQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O^%xZiYzSOY%qYZ$OZt%qtu&kuw%qwx&kx#O%q#O#P&k#P;'S%q;'S;=`'S<%lO%qY&pSiYOY&kZ;'S&k;'S;=`&|<%lO&kY'PP;=`<%l&k^'VP;=`<%l%q~'_O#V~~'dO#T~U'kUzS#PQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU(UUzS#_QOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU(mXzSOt$Ouw$Ox!Q$O!Q!R)Y!R![+w![#O$O#P;'S$O;'S;=`$g<%lO$OU)a`zSsQOt$Ouw$Ox!O$O!O!P*c!P!Q$O!Q![+w![#O$O#P#R$O#R#S,t#S#U$O#U#V-c#V#l$O#l#m.w#m;'S$O;'S;=`$g<%lO$OU*hWzSOt$Ouw$Ox!Q$O!Q![+Q![#O$O#P;'S$O;'S;=`$g<%lO$OU+XYzSsQOt$Ouw$Ox!Q$O!Q![+Q![#O$O#P#R$O#R#S*c#S;'S$O;'S;=`$g<%lO$OU,O[zSsQOt$Ouw$Ox!O$O!O!P*c!P!Q$O!Q![+w![#O$O#P#R$O#R#S,t#S;'S$O;'S;=`$g<%lO$OU,yWzSOt$Ouw$Ox!Q$O!Q![+w![#O$O#P;'S$O;'S;=`$g<%lO$OU-hXzSOt$Ouw$Ox!Q$O!Q!R.T!R!S.T!S#O$O#P;'S$O;'S;=`$g<%lO$OU.[XzSsQOt$Ouw$Ox!Q$O!Q!R.T!R!S.T!S#O$O#P;'S$O;'S;=`$g<%lO$OU.|[zSOt$Ouw$Ox!Q$O!Q![/r![!c$O!c!i/r!i#O$O#P#T$O#T#Z/r#Z;'S$O;'S;=`$g<%lO$OU/y[zSsQOt$Ouw$Ox!Q$O!Q![/r![!c$O!c!i/r!i#O$O#P#T$O#T#Z/r#Z;'S$O;'S;=`$g<%lO$OU0tWzSOt$Ouw$Ox!P$O!P!Q1^!Q#O$O#P;'S$O;'S;=`$g<%lO$OU1c^zSOY2_YZ$OZt2_tu3buw2_wx3bx!P2_!P!Q$O!Q!}2_!}#O8T#O#P5p#P;'S2_;'S;=`9U<%lO2_U2f^zS!bQOY2_YZ$OZt2_tu3buw2_wx3bx!P2_!P!Q6V!Q!}2_!}#O8T#O#P5p#P;'S2_;'S;=`9U<%lO2_Q3gX!bQOY3bZ!P3b!P!Q4S!Q!}3b!}#O4q#O#P5p#P;'S3b;'S;=`6P<%lO3bQ4VP!P!Q4YQ4_U!bQ#Z#[4Y#]#^4Y#a#b4Y#g#h4Y#i#j4Y#m#n4YQ4tVOY4qZ#O4q#O#P5Z#P#Q3b#Q;'S4q;'S;=`5j<%lO4qQ5^SOY4qZ;'S4q;'S;=`5j<%lO4qQ5mP;=`<%l4qQ5sSOY3bZ;'S3b;'S;=`6P<%lO3bQ6SP;=`<%l3bU6[WzSOt$Ouw$Ox!P$O!P!Q6t!Q#O$O#P;'S$O;'S;=`$g<%lO$OU6{bzS!bQOt$Ouw$Ox#O$O#P#Z$O#Z#[6t#[#]$O#]#^6t#^#a$O#a#b6t#b#g$O#g#h6t#h#i$O#i#j6t#j#m$O#m#n6t#n;'S$O;'S;=`$g<%lO$OU8Y[zSOY8TYZ$OZt8Ttu4quw8Twx4qx#O8T#O#P5Z#P#Q2_#Q;'S8T;'S;=`9O<%lO8TU9RP;=`<%l8TU9XP;=`<%l2_U9cUzS!PQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU9|W#aQzSOt$Ouw$Ox!_$O!_!`:f!`#O$O#P;'S$O;'S;=`$g<%lO$OU:kVzSOt$Ouw$Ox#O$O#P#Q;Q#Q;'S$O;'S;=`$g<%lO$OU;XU#`QzSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~;pO#W~U;wU#bQzSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU<bUzS!WQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU<y^zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$OU=|UxQzSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU>e_zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#U?d#U#o<t#o;'S$O;'S;=`$g<%lO$OU?i`zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#`<t#`#a@k#a#o<t#o;'S$O;'S;=`$g<%lO$OU@p`zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#g<t#g#hAr#h#o<t#o;'S$O;'S;=`$g<%lO$OUAw`zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#X<t#X#YBy#Y#o<t#o;'S$O;'S;=`$g<%lO$OUCQ^}QzSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$O^DT^#XWzSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$O^EW^#ZWzSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$O^FZ`#YWzSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#f<t#f#gG]#g#o<t#o;'S$O;'S;=`$g<%lO$OUGb`zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#i<t#i#jAr#j#o<t#o;'S$O;'S;=`$g<%lO$OUHkUlQzSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~ISO#c~", tokenData: "IS~R}OX$OXY$mYZ%WZp$Opq$mqs$Ost%qtu'Yuw$Owx'_xy'dyz'}z{$O{|(h|}$O}!O(h!O!P$O!P!Q0o!Q!R)Y!R![+w![!]9[!]!^%W!^!}$O!}#O9u#O#P;k#P#Q;p#Q#R$O#R#S<Z#S#T$O#T#Y<t#Y#Z>`#Z#b<t#b#cC|#c#f<t#f#gEP#g#h<t#h#iFS#i#o<t#o#p$O#p#qHd#q;'S$O;'S;=`$g<%l~$O~O$O~~H}S$TU!QSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OS$jP;=`<%l$O^$tU!QS#QYOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU%_U!QS#WQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O^%xZoY!QSOY%qYZ$OZt%qtu&kuw%qwx&kx#O%q#O#P&k#P;'S%q;'S;=`'S<%lO%qY&pSoYOY&kZ;'S&k;'S;=`&|<%lO&kY'PP;=`<%l&k^'VP;=`<%l%q~'_O#]~~'dO#Z~U'kU!QS#VQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU(UU!QS#eQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU(mX!QSOt$Ouw$Ox!Q$O!Q!R)Y!R![+w![#O$O#P;'S$O;'S;=`$g<%lO$OU)a`!QSyQOt$Ouw$Ox!O$O!O!P*c!P!Q$O!Q![+w![#O$O#P#R$O#R#S,t#S#U$O#U#V-c#V#l$O#l#m.w#m;'S$O;'S;=`$g<%lO$OU*hW!QSOt$Ouw$Ox!Q$O!Q![+Q![#O$O#P;'S$O;'S;=`$g<%lO$OU+XY!QSyQOt$Ouw$Ox!Q$O!Q![+Q![#O$O#P#R$O#R#S*c#S;'S$O;'S;=`$g<%lO$OU,O[!QSyQOt$Ouw$Ox!O$O!O!P*c!P!Q$O!Q![+w![#O$O#P#R$O#R#S,t#S;'S$O;'S;=`$g<%lO$OU,yW!QSOt$Ouw$Ox!Q$O!Q![+w![#O$O#P;'S$O;'S;=`$g<%lO$OU-hX!QSOt$Ouw$Ox!Q$O!Q!R.T!R!S.T!S#O$O#P;'S$O;'S;=`$g<%lO$OU.[X!QSyQOt$Ouw$Ox!Q$O!Q!R.T!R!S.T!S#O$O#P;'S$O;'S;=`$g<%lO$OU.|[!QSOt$Ouw$Ox!Q$O!Q![/r![!c$O!c!i/r!i#O$O#P#T$O#T#Z/r#Z;'S$O;'S;=`$g<%lO$OU/y[!QSyQOt$Ouw$Ox!Q$O!Q![/r![!c$O!c!i/r!i#O$O#P#T$O#T#Z/r#Z;'S$O;'S;=`$g<%lO$OU0tW!QSOt$Ouw$Ox!P$O!P!Q1^!Q#O$O#P;'S$O;'S;=`$g<%lO$OU1c^!QSOY2_YZ$OZt2_tu3buw2_wx3bx!P2_!P!Q$O!Q!}2_!}#O8T#O#P5p#P;'S2_;'S;=`9U<%lO2_U2f^!QS!hQOY2_YZ$OZt2_tu3buw2_wx3bx!P2_!P!Q6V!Q!}2_!}#O8T#O#P5p#P;'S2_;'S;=`9U<%lO2_Q3gX!hQOY3bZ!P3b!P!Q4S!Q!}3b!}#O4q#O#P5p#P;'S3b;'S;=`6P<%lO3bQ4VP!P!Q4YQ4_U!hQ#Z#[4Y#]#^4Y#a#b4Y#g#h4Y#i#j4Y#m#n4YQ4tVOY4qZ#O4q#O#P5Z#P#Q3b#Q;'S4q;'S;=`5j<%lO4qQ5^SOY4qZ;'S4q;'S;=`5j<%lO4qQ5mP;=`<%l4qQ5sSOY3bZ;'S3b;'S;=`6P<%lO3bQ6SP;=`<%l3bU6[W!QSOt$Ouw$Ox!P$O!P!Q6t!Q#O$O#P;'S$O;'S;=`$g<%lO$OU6{b!QS!hQOt$Ouw$Ox#O$O#P#Z$O#Z#[6t#[#]$O#]#^6t#^#a$O#a#b6t#b#g$O#g#h6t#h#i$O#i#j6t#j#m$O#m#n6t#n;'S$O;'S;=`$g<%lO$OU8Y[!QSOY8TYZ$OZt8Ttu4quw8Twx4qx#O8T#O#P5Z#P#Q2_#Q;'S8T;'S;=`9O<%lO8TU9RP;=`<%l8TU9XP;=`<%l2_U9cU!QS!VQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU9|W#gQ!QSOt$Ouw$Ox!_$O!_!`:f!`#O$O#P;'S$O;'S;=`$g<%lO$OU:kV!QSOt$Ouw$Ox#O$O#P#Q;Q#Q;'S$O;'S;=`$g<%lO$OU;XU#fQ!QSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~;pO#^~U;wU#hQ!QSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU<bU!QS!^QOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU<y^!QSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$OU=|U!OQ!QSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU>e_!QSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#U?d#U#o<t#o;'S$O;'S;=`$g<%lO$OU?i`!QSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#`<t#`#a@k#a#o<t#o;'S$O;'S;=`$g<%lO$OU@p`!QSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#g<t#g#hAr#h#o<t#o;'S$O;'S;=`$g<%lO$OUAw`!QSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#X<t#X#YBy#Y#o<t#o;'S$O;'S;=`$g<%lO$OUCQ^!TQ!QSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$O^DT^#_W!QSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$O^EW^#aW!QSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$O^FZ`#`W!QSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#f<t#f#gG]#g#o<t#o;'S$O;'S;=`$g<%lO$OUGb`!QSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#i<t#i#jAr#j#o<t#o;'S$O;'S;=`$g<%lO$OUHkUrQ!QSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~ISO#i~",
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#R~~", 11)], tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#X~~", 11)],
topRules: {"Program":[0,26]}, topRules: {"Program":[0,32]},
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}], specialized: [{term: 26, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 26, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
tokenPrec: 1922 tokenPrec: 2109
}) })

View File

@ -0,0 +1,72 @@
import { expect, describe, test } from 'bun:test'
import '../shrimp.grammar' // Importing this so changes cause it to retest!
describe('bitwise operators - grammar', () => {
test('parses band (bitwise AND)', () => {
expect('5 band 3').toMatchTree(`
BinOp
Number 5
Band band
Number 3`)
})
test('parses bor (bitwise OR)', () => {
expect('5 bor 3').toMatchTree(`
BinOp
Number 5
Bor bor
Number 3`)
})
test('parses bxor (bitwise XOR)', () => {
expect('5 bxor 3').toMatchTree(`
BinOp
Number 5
Bxor bxor
Number 3`)
})
test('parses << (left shift)', () => {
expect('5 << 2').toMatchTree(`
BinOp
Number 5
Shl <<
Number 2`)
})
test('parses >> (signed right shift)', () => {
expect('20 >> 2').toMatchTree(`
BinOp
Number 20
Shr >>
Number 2`)
})
test('parses >>> (unsigned right shift)', () => {
expect('-1 >>> 1').toMatchTree(`
BinOp
Number -1
Ushr >>>
Number 1`)
})
test('parses bnot (bitwise NOT) as function call', () => {
expect('bnot 5').toMatchTree(`
FunctionCall
Identifier bnot
PositionalArg
Number 5`)
})
test('bitwise operators work in expressions', () => {
expect('x = 5 band 3').toMatchTree(`
Assign
AssignableIdentifier x
Eq =
BinOp
Number 5
Band band
Number 3`)
})
})

View File

@ -63,6 +63,7 @@ export const globals = {
// boolean/logic // boolean/logic
not: (v: any) => !v, not: (v: any) => !v,
bnot: (n: number) => ~(n | 0),
// utilities // utilities
inc: (n: number) => n + 1, inc: (n: number) => n + 1,