Actually, the message format is LLSD notation not python (d'oh)
This commit is contained in:
53
lib/classes/llsd/LLSDArray.ts
Normal file
53
lib/classes/llsd/LLSDArray.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { LLSDTokenGenerator } from './LLSDTokenGenerator';
|
||||
import { LLSDTokenType } from './LLSDTokenType';
|
||||
import { LLSDNotationParser } from './LLSDNotationParser';
|
||||
import { LLSDType } from './LLSDType';
|
||||
|
||||
export class LLSDArray
|
||||
{
|
||||
public static parse(gen: LLSDTokenGenerator): LLSDType[]
|
||||
{
|
||||
const arr: LLSDType[] = [];
|
||||
let value: LLSDType | undefined = undefined;
|
||||
while (true)
|
||||
{
|
||||
const token = gen();
|
||||
if (token === undefined)
|
||||
{
|
||||
throw new Error('Unexpected end of input in array');
|
||||
}
|
||||
switch (token.type)
|
||||
{
|
||||
case LLSDTokenType.WHITESPACE:
|
||||
{
|
||||
continue;
|
||||
}
|
||||
case LLSDTokenType.ARRAY_END:
|
||||
{
|
||||
if (value !== undefined)
|
||||
{
|
||||
arr.push(value);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
case LLSDTokenType.COMMA:
|
||||
{
|
||||
if (value === undefined)
|
||||
{
|
||||
throw new Error('Expected value before comma');
|
||||
}
|
||||
arr.push(value);
|
||||
value = undefined;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (value !== undefined)
|
||||
{
|
||||
throw new Error('Comma or end brace expected');
|
||||
}
|
||||
|
||||
value = LLSDNotationParser.parseValueToken(gen, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
115
lib/classes/llsd/LLSDMap.ts
Normal file
115
lib/classes/llsd/LLSDMap.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { LLSDObject } from './LLSDObject';
|
||||
import { LLSDTokenGenerator } from './LLSDTokenGenerator';
|
||||
import { LLSDTokenType } from './LLSDTokenType';
|
||||
import { LLSDNotationParser } from './LLSDNotationParser';
|
||||
import { LLSDType } from './LLSDType';
|
||||
|
||||
export class LLSDMap extends LLSDObject
|
||||
{
|
||||
public data: Map<LLSDType, LLSDType> = new Map<LLSDType, LLSDType>();
|
||||
|
||||
public static parse(gen: LLSDTokenGenerator): LLSDMap
|
||||
{
|
||||
const m = new LLSDMap();
|
||||
let expectsKey = true
|
||||
let key: LLSDType | undefined = undefined;
|
||||
let value: LLSDType | undefined = undefined;
|
||||
while (true)
|
||||
{
|
||||
const token = gen();
|
||||
if (token === undefined)
|
||||
{
|
||||
throw new Error('Unexpected end of input in map');
|
||||
}
|
||||
switch (token.type)
|
||||
{
|
||||
case LLSDTokenType.WHITESPACE:
|
||||
{
|
||||
continue;
|
||||
}
|
||||
case LLSDTokenType.MAP_END:
|
||||
{
|
||||
if (expectsKey)
|
||||
{
|
||||
throw new Error('Unexpected end of map');
|
||||
}
|
||||
if (key !== undefined && value !== undefined)
|
||||
{
|
||||
m.data.set(key, value);
|
||||
}
|
||||
else if (m.data.size > 0)
|
||||
{
|
||||
throw new Error('Expected value before end of map');
|
||||
}
|
||||
return m;
|
||||
}
|
||||
case LLSDTokenType.COLON:
|
||||
{
|
||||
if (!expectsKey)
|
||||
{
|
||||
throw new Error('Unexpected symbol: :');
|
||||
}
|
||||
if (key === undefined)
|
||||
{
|
||||
throw new Error('Empty key not allowed');
|
||||
}
|
||||
expectsKey = false;
|
||||
continue;
|
||||
}
|
||||
case LLSDTokenType.COMMA:
|
||||
{
|
||||
if (expectsKey)
|
||||
{
|
||||
throw new Error('Empty map entry not allowed');
|
||||
}
|
||||
if (value === undefined)
|
||||
{
|
||||
throw new Error('Empty map value not allowed');
|
||||
}
|
||||
if (key !== undefined)
|
||||
{
|
||||
m.data.set(key, value);
|
||||
}
|
||||
key = undefined;
|
||||
value = undefined;
|
||||
expectsKey = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (expectsKey && key !== undefined)
|
||||
{
|
||||
throw new Error('Colon expected');
|
||||
}
|
||||
else if (value !== undefined)
|
||||
{
|
||||
throw new Error('Comma or end brace expected');
|
||||
}
|
||||
|
||||
const val = LLSDNotationParser.parseValueToken(gen, token);
|
||||
if (expectsKey)
|
||||
{
|
||||
key = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get length(): number
|
||||
{
|
||||
return Object.keys(this.data).length;
|
||||
}
|
||||
|
||||
public get(key: LLSDType): LLSDType | undefined
|
||||
{
|
||||
return this.data.get(key);
|
||||
}
|
||||
|
||||
public toJSON(): unknown
|
||||
{
|
||||
return Object.fromEntries(this.data);
|
||||
}
|
||||
}
|
||||
204
lib/classes/llsd/LLSDNotationParser.spec.ts
Normal file
204
lib/classes/llsd/LLSDNotationParser.spec.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
import * as assert from 'assert';
|
||||
import { LLSDNotationParser } from './LLSDNotationParser';
|
||||
import { LLSDMap } from './LLSDMap';
|
||||
import { UUID } from '../UUID';
|
||||
|
||||
describe('LLSDNotationParser', () =>
|
||||
{
|
||||
describe('parse', () =>
|
||||
{
|
||||
it('can parse a complex LLSD Notation document', () =>
|
||||
{
|
||||
const notationDoc = `{
|
||||
'nested_map': {
|
||||
'nested_again': {
|
||||
'array': [
|
||||
i0,
|
||||
'string',
|
||||
"string2",
|
||||
[
|
||||
"another",
|
||||
"array",
|
||||
r4.3,
|
||||
i22,
|
||||
!,
|
||||
{
|
||||
'isThis': i42
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
'undef': !,
|
||||
'booleans': [
|
||||
true,
|
||||
false,
|
||||
1,
|
||||
0,
|
||||
T,
|
||||
F,
|
||||
t,
|
||||
f,
|
||||
TRUE,
|
||||
FALSE
|
||||
],
|
||||
'integer': i69,
|
||||
'negInt': i-69,
|
||||
'real': r3.141,
|
||||
'realNeg': r-3.141,
|
||||
'uuid': ufb54f6b1-8120-40c9-9aa3-f9abef1a168f,
|
||||
'string1': "gday\\"mate",
|
||||
'string2': 'gday\\'mate',
|
||||
'string3': s(11)"hello"there",
|
||||
'uri': l"https://secondlife.com/",
|
||||
'date': d"2023-11-10T13:32:32.93Z",
|
||||
'binary': b64"amVsbHlmaXNo",
|
||||
'binary2': b16"6261636F6E62697473",
|
||||
'binary3': b(32)"KÚ~¬\béGÀt|Ïh,9µEK¹*;]ÆÁåb/"
|
||||
}`;
|
||||
const parsed = LLSDNotationParser.parse(notationDoc);
|
||||
if (!(parsed instanceof LLSDMap))
|
||||
{
|
||||
assert('Parsed document is not a map');
|
||||
return;
|
||||
}
|
||||
|
||||
const nested = parsed.get('nested_map');
|
||||
if (nested instanceof LLSDMap)
|
||||
{
|
||||
const nestedAgain = nested.get('nested_again');
|
||||
if (nestedAgain instanceof LLSDMap)
|
||||
{
|
||||
const arr = nestedAgain.get('array');
|
||||
if (!Array.isArray(arr))
|
||||
{
|
||||
assert(false, 'Nested array is not an array');
|
||||
}
|
||||
else
|
||||
{
|
||||
assert.equal(arr.length, 4);
|
||||
assert.equal(arr[0], 0);
|
||||
assert.equal(arr[1], 'string');
|
||||
assert.equal(arr[2], 'string2');
|
||||
const nestedAgainArr = arr[3];
|
||||
if (!Array.isArray(nestedAgainArr))
|
||||
{
|
||||
assert(false, 'Nested again array is not an array');
|
||||
}
|
||||
else
|
||||
{
|
||||
assert.equal(nestedAgainArr.length, 6);
|
||||
assert.equal(nestedAgainArr[0], 'another');
|
||||
assert.equal(nestedAgainArr[1], 'array');
|
||||
assert.equal(nestedAgainArr[2], 4.3);
|
||||
assert.equal(nestedAgainArr[3], 22);
|
||||
assert.equal(nestedAgainArr[4], null);
|
||||
const thirdNestedMap = nestedAgainArr[5];
|
||||
if (thirdNestedMap instanceof LLSDMap)
|
||||
{
|
||||
assert.equal(thirdNestedMap.get('isThis'), 42);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, 'Third nested map is not a map');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, 'NestedAgain is not a map');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, 'Nested is not a map');
|
||||
}
|
||||
assert.equal(parsed.get('undef'), null);
|
||||
const bools = parsed.get('booleans');
|
||||
if (!Array.isArray(bools))
|
||||
{
|
||||
assert(false, 'Booleans array is not bools');
|
||||
}
|
||||
else
|
||||
{
|
||||
assert.equal(bools.length, 10);
|
||||
assert.equal(bools[0], true);
|
||||
assert.equal(bools[1], false);
|
||||
assert.equal(bools[2], true);
|
||||
assert.equal(bools[3], false);
|
||||
assert.equal(bools[4], true);
|
||||
assert.equal(bools[5], false);
|
||||
assert.equal(bools[6], true);
|
||||
assert.equal(bools[7], false);
|
||||
assert.equal(bools[8], true);
|
||||
assert.equal(bools[9], false);
|
||||
}
|
||||
|
||||
/*
|
||||
'string1': "gday\"mate",
|
||||
'string2': 'gday\'mate',
|
||||
'string3': s(11)"hello"there",
|
||||
'uri': l"https://secondlife.com/",
|
||||
'date': d"2023-11-10T13:32:32.93Z",
|
||||
'binary': b64"amVsbHlmaXNo",
|
||||
'binary2': b16"6261636F6E62697473",
|
||||
'binary3': b(32)"KÚ~¬éGÀt|Ïh,9µEK¹*;]ÆÁåb/"
|
||||
*/
|
||||
assert.equal(parsed.get('integer'), 69);
|
||||
assert.equal(parsed.get('negInt'), -69);
|
||||
assert.equal(parsed.get('real'), 3.141);
|
||||
assert.equal(parsed.get('realNeg'), -3.141);
|
||||
const u = parsed.get('uuid');
|
||||
if (u instanceof UUID)
|
||||
{
|
||||
assert.equal(u.equals('fb54f6b1-8120-40c9-9aa3-f9abef1a168f'), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, 'UUID is not a UUID');
|
||||
}
|
||||
assert.equal(parsed.get('string1'), 'gday"mate');
|
||||
assert.equal(parsed.get('string2'), 'gday\'mate');
|
||||
assert.equal(parsed.get('string3'), 'hello"there');
|
||||
assert.equal(parsed.get('uri'), 'https://secondlife.com/');
|
||||
const d = parsed.get('date');
|
||||
if (d instanceof Date)
|
||||
{
|
||||
assert.equal(d.getTime(), 1699623152930);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, 'Date entry is not a date');
|
||||
}
|
||||
const buf1 = parsed.get('binary');
|
||||
if (buf1 instanceof Buffer)
|
||||
{
|
||||
assert.equal(buf1.toString('utf-8'), 'jellyfish');
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, 'Buf1 is not a buffer');
|
||||
}
|
||||
const buf2 = parsed.get('binary2');
|
||||
if (buf2 instanceof Buffer)
|
||||
{
|
||||
assert.equal(buf2.toString('utf-8'), 'baconbits');
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, 'Buf2 is not a buffer');
|
||||
}
|
||||
const buf3 = parsed.get('binary3');
|
||||
if (buf3 instanceof Buffer)
|
||||
{
|
||||
assert.equal(buf3.equals(Buffer.from('KÚ~¬\béGÀt|Ïh,9µEK¹*;]ÆÁåb/', 'binary')), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, 'Buf3 is not a buffer');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
320
lib/classes/llsd/LLSDNotationParser.ts
Normal file
320
lib/classes/llsd/LLSDNotationParser.ts
Normal file
@@ -0,0 +1,320 @@
|
||||
import { LLSDTokenType } from './LLSDTokenType';
|
||||
import { LLSDType } from './LLSDType';
|
||||
import { LLSDTokenSpec } from './LLSDTokenSpec';
|
||||
import { LLSDToken } from './LLSDToken';
|
||||
import { LLSDTokenGenerator } from './LLSDTokenGenerator';
|
||||
import { LLSDMap } from './LLSDMap';
|
||||
import { UUID } from '../UUID';
|
||||
import { LLSDArray } from './LLSDArray';
|
||||
|
||||
export class LLSDNotationParser
|
||||
{
|
||||
private static tokenSpecs: LLSDTokenSpec[] =
|
||||
[
|
||||
{ regex: /^\s+/, type: LLSDTokenType.WHITESPACE },
|
||||
{ regex: /^!/, type: LLSDTokenType.NULL },
|
||||
{ regex: /^\{/, type: LLSDTokenType.MAP_START },
|
||||
{ regex: /^}/, type: LLSDTokenType.MAP_END },
|
||||
{ regex: /^:/, type: LLSDTokenType.COLON },
|
||||
{ regex: /^,/, type: LLSDTokenType.COMMA },
|
||||
{ regex: /^\[/, type: LLSDTokenType.ARRAY_START },
|
||||
{ regex: /^]/, type: LLSDTokenType.ARRAY_END },
|
||||
{ regex: /^(?:true|false|TRUE|FALSE|1|0|T|F|t|f)/, type: LLSDTokenType.BOOLEAN },
|
||||
{ regex: /^i(-?[0-9]+)/, type: LLSDTokenType.INTEGER },
|
||||
{ regex: /^r(-?[0-9.]+)/, type: LLSDTokenType.REAL },
|
||||
{ regex: /^u([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/, type: LLSDTokenType.UUID },
|
||||
{ regex: /^'([^'\\]*(?:\\.[^'\\\n]*)*)'/, type: LLSDTokenType.STRING_FIXED_SINGLE },
|
||||
{ regex: /^"([^"\\]*(?:\\.[^"\\\n]*)*)"/, type: LLSDTokenType.STRING_FIXED_DOUBLE },
|
||||
{ regex: /^s\(([0-9]+)\)"/, type: LLSDTokenType.STRING_DYNAMIC_START },
|
||||
{ regex: /^l"([^"]*?)"/, type: LLSDTokenType.URI },
|
||||
{ regex: /^d"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{2}Z)"/, type: LLSDTokenType.DATE },
|
||||
{ regex: /^b[0-9]{2}"[0-9a-zA-Z+\/=]*?"/, type: LLSDTokenType.BINARY_STATIC },
|
||||
{ regex: /^b\(([0-9]+)\)"/, type: LLSDTokenType.BINARY_DYNAMIC_START }
|
||||
];
|
||||
|
||||
private static* tokenize(inpt: string): Generator<LLSDToken | void>
|
||||
{
|
||||
const dataContainer = {
|
||||
input: inpt,
|
||||
index: 0
|
||||
}
|
||||
while (dataContainer.index < dataContainer.input.length)
|
||||
{
|
||||
const currentInput = dataContainer.input.slice(dataContainer.index);
|
||||
|
||||
if (currentInput.length === 0)
|
||||
{
|
||||
return; // End of input
|
||||
}
|
||||
|
||||
let matched = false;
|
||||
for (const { regex, type } of this.tokenSpecs)
|
||||
{
|
||||
const tokenMatch = currentInput.match(regex);
|
||||
if (tokenMatch)
|
||||
{
|
||||
matched = true;
|
||||
let value = tokenMatch[0];
|
||||
if (tokenMatch.length > 1)
|
||||
{
|
||||
value = tokenMatch[tokenMatch.length - 1];
|
||||
}
|
||||
dataContainer.index += tokenMatch[0].length; // Move past this token
|
||||
|
||||
yield { type, value, rawValue: tokenMatch[0], dataContainer };
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched)
|
||||
{
|
||||
dataContainer.index++;
|
||||
yield { type: LLSDTokenType.UNKNOWN, value: dataContainer.input[dataContainer.index - 1], rawValue: dataContainer.input[dataContainer.index - 1], dataContainer };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static parseValueToken(gen: LLSDTokenGenerator, initialToken?: LLSDToken): LLSDType
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
let t: LLSDToken | undefined;
|
||||
if (initialToken !== undefined)
|
||||
{
|
||||
t = initialToken;
|
||||
initialToken = undefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = gen();
|
||||
if (t === undefined)
|
||||
{
|
||||
throw new Error('Unexpected end of input');
|
||||
}
|
||||
}
|
||||
switch (t.type)
|
||||
{
|
||||
case LLSDTokenType.UNKNOWN:
|
||||
{
|
||||
throw new Error('Unexpected token: ' + t.value);
|
||||
}
|
||||
case LLSDTokenType.WHITESPACE:
|
||||
{
|
||||
continue;
|
||||
}
|
||||
case LLSDTokenType.NULL:
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case LLSDTokenType.BOOLEAN:
|
||||
{
|
||||
return t.value === 'true' || t.value === 'TRUE' || t.value === 'T' || t.value === 't' || t.value === '1';
|
||||
}
|
||||
case LLSDTokenType.INTEGER:
|
||||
{
|
||||
return parseInt(t.value, 10);
|
||||
}
|
||||
case LLSDTokenType.REAL:
|
||||
{
|
||||
return parseFloat(t.value);
|
||||
}
|
||||
case LLSDTokenType.UUID:
|
||||
{
|
||||
return new UUID(t.value);
|
||||
}
|
||||
case LLSDTokenType.STRING_FIXED_SINGLE:
|
||||
{
|
||||
return t.value.replace(/\\'/, '\'')
|
||||
.replace(/\\\\/g, '\\');
|
||||
}
|
||||
case LLSDTokenType.STRING_FIXED_DOUBLE:
|
||||
{
|
||||
return t.value.replace(/\\"/, '"')
|
||||
.replace(/\\\\/g, '\\');
|
||||
}
|
||||
case LLSDTokenType.URI:
|
||||
{
|
||||
return t.value;
|
||||
}
|
||||
case LLSDTokenType.DATE:
|
||||
{
|
||||
return new Date(t.value);
|
||||
}
|
||||
case LLSDTokenType.BINARY_STATIC:
|
||||
{
|
||||
const b = t.value.match(/^b([0-9]{2})"([0-9a-zA-Z+\/=]*?)"/);
|
||||
if (b === null || b.length < 3)
|
||||
{
|
||||
throw new Error('Invalid BINARY_STATIC');
|
||||
}
|
||||
const base = parseInt(b[1], 10);
|
||||
if (base !== 16 && base !== 64)
|
||||
{
|
||||
throw new Error('Unsupported base ' + String(base));
|
||||
}
|
||||
return Buffer.from(b[2], base === 64 ? 'base64' : 'hex');
|
||||
}
|
||||
case LLSDTokenType.STRING_DYNAMIC_START:
|
||||
{
|
||||
const length = parseInt(t.value, 10);
|
||||
const s = t.dataContainer.input.slice(t.dataContainer.index, t.dataContainer.index + length);
|
||||
t.dataContainer.index += length;
|
||||
if (t.dataContainer.input[t.dataContainer.index] !== '"')
|
||||
{
|
||||
throw new Error('Expected " at end of dynamic string')
|
||||
}
|
||||
t.dataContainer.index += 1;
|
||||
return s;
|
||||
}
|
||||
case LLSDTokenType.BINARY_DYNAMIC_START:
|
||||
{
|
||||
const length = parseInt(t.value, 10);
|
||||
const s = t.dataContainer.input.slice(t.dataContainer.index, t.dataContainer.index + length);
|
||||
t.dataContainer.index += length;
|
||||
if (t.dataContainer.input[t.dataContainer.index] !== '"')
|
||||
{
|
||||
throw new Error('Expected " at end of dynamic binary string')
|
||||
}
|
||||
t.dataContainer.index += 1;
|
||||
return Buffer.from(s, 'binary');
|
||||
}
|
||||
case LLSDTokenType.MAP_START:
|
||||
{
|
||||
return LLSDMap.parse(gen);
|
||||
}
|
||||
case LLSDTokenType.ARRAY_START:
|
||||
{
|
||||
return LLSDArray.parse(gen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static parse(input: string): LLSDType
|
||||
{
|
||||
const generator = this.tokenize(input);
|
||||
const getToken: LLSDTokenGenerator = (): LLSDToken | undefined =>
|
||||
{
|
||||
return generator.next().value;
|
||||
}
|
||||
const data = this.parseValueToken(getToken);
|
||||
do
|
||||
{
|
||||
const t = getToken();
|
||||
if (t === undefined)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
if (t.type !== LLSDTokenType.WHITESPACE)
|
||||
{
|
||||
throw new Error('Unexpected token at end of document: ' + t.value);
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
}
|
||||
|
||||
/*
|
||||
private static readObject(cont: LLSDParserContainer): LLSDType
|
||||
{
|
||||
let tokenType: LLSDTokenType | null = null;
|
||||
const stack: LLSDObject[] = [];
|
||||
while (cont.pos < cont.str.length)
|
||||
{
|
||||
const subString = cont.str.slice(cont.pos);
|
||||
let value: string | null = null;
|
||||
for (const { regex, type } of this.tokenSpecs)
|
||||
{
|
||||
const tokenMatch = subString.match(regex);
|
||||
if (tokenMatch)
|
||||
{
|
||||
value = tokenMatch[0];
|
||||
tokenType = type;
|
||||
if (tokenMatch.length > 1)
|
||||
{
|
||||
value = tokenMatch[tokenMatch.length - 1];
|
||||
}
|
||||
cont.pos += tokenMatch[0].length; // Move past this token
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tokenType === null)
|
||||
{
|
||||
tokenType = LLSDTokenType.UNKNOWN;
|
||||
value = cont.str[cont.pos++];
|
||||
}
|
||||
if (stack.length > 0)
|
||||
{
|
||||
if (stack[stack.length - 1].acceptToken(tokenType, value))
|
||||
{
|
||||
// stack object completed
|
||||
}
|
||||
}
|
||||
switch (tokenType)
|
||||
{
|
||||
case LLSDTokenType.WHITESPACE:
|
||||
{
|
||||
continue;
|
||||
}
|
||||
case LLSDTokenType.UNKNOWN:
|
||||
{
|
||||
throw new Error('Unexpected token: ' + value);
|
||||
}
|
||||
case LLSDTokenType.NULL:
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case LLSDTokenType.MAP_START:
|
||||
{
|
||||
|
||||
break;
|
||||
}
|
||||
case LLSDTokenType.MAP_END:
|
||||
break;
|
||||
case LLSDTokenType.COLON:
|
||||
break;
|
||||
case LLSDTokenType.COMMA:
|
||||
break;
|
||||
case LLSDTokenType.ARRAY_START:
|
||||
break;
|
||||
case LLSDTokenType.ARRAY_END:
|
||||
break;
|
||||
case LLSDTokenType.BOOLEAN:
|
||||
break;
|
||||
case LLSDTokenType.INTEGER:
|
||||
break;
|
||||
case LLSDTokenType.REAL:
|
||||
break;
|
||||
case LLSDTokenType.UUID:
|
||||
break;
|
||||
case LLSDTokenType.STRING_FIXED:
|
||||
break;
|
||||
case LLSDTokenType.STRING_DYNAMIC_START:
|
||||
break;
|
||||
case LLSDTokenType.URI:
|
||||
break;
|
||||
case LLSDTokenType.DATE:
|
||||
break;
|
||||
case LLSDTokenType.BINARY_STATIC:
|
||||
break;
|
||||
case LLSDTokenType.BINARY_DYNAMIC_START:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static parse(input: string): LLSDType
|
||||
{
|
||||
const cont: LLSDParserContainer = {
|
||||
str: input,
|
||||
pos: 0
|
||||
};
|
||||
|
||||
const token = this.readObject(cont);
|
||||
if (cont.pos < input.length)
|
||||
{
|
||||
throw new Error('Only one root object expected');
|
||||
}
|
||||
return token;
|
||||
}
|
||||
*/
|
||||
}
|
||||
9
lib/classes/llsd/LLSDObject.ts
Normal file
9
lib/classes/llsd/LLSDObject.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export abstract class LLSDObject
|
||||
{
|
||||
public toString(): string
|
||||
{
|
||||
return JSON.stringify(this.toJSON());
|
||||
}
|
||||
|
||||
public abstract toJSON(): unknown;
|
||||
}
|
||||
12
lib/classes/llsd/LLSDToken.ts
Normal file
12
lib/classes/llsd/LLSDToken.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { LLSDTokenType } from './LLSDTokenType';
|
||||
|
||||
export interface LLSDToken
|
||||
{
|
||||
type: LLSDTokenType;
|
||||
value: string;
|
||||
rawValue: string;
|
||||
dataContainer: {
|
||||
input: string,
|
||||
index: number
|
||||
}
|
||||
}
|
||||
9
lib/classes/llsd/LLSDTokenContainer.ts
Normal file
9
lib/classes/llsd/LLSDTokenContainer.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { LLSDToken } from './LLSDToken';
|
||||
import { LLSDTokenGenerator } from './LLSDTokenGenerator';
|
||||
|
||||
export interface LLSDTokenContainer
|
||||
{
|
||||
tokens: LLSDToken[];
|
||||
index: number;
|
||||
gen: LLSDTokenGenerator;
|
||||
}
|
||||
3
lib/classes/llsd/LLSDTokenGenerator.ts
Normal file
3
lib/classes/llsd/LLSDTokenGenerator.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { LLSDToken } from './LLSDToken';
|
||||
|
||||
export type LLSDTokenGenerator = () => LLSDToken | undefined;
|
||||
7
lib/classes/llsd/LLSDTokenSpec.ts
Normal file
7
lib/classes/llsd/LLSDTokenSpec.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { LLSDTokenType } from './LLSDTokenType';
|
||||
|
||||
export interface LLSDTokenSpec
|
||||
{
|
||||
regex: RegExp;
|
||||
type: LLSDTokenType;
|
||||
}
|
||||
23
lib/classes/llsd/LLSDTokenType.ts
Normal file
23
lib/classes/llsd/LLSDTokenType.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export enum LLSDTokenType
|
||||
{
|
||||
UNKNOWN = 0,
|
||||
WHITESPACE,
|
||||
NULL,
|
||||
MAP_START,
|
||||
MAP_END,
|
||||
COLON,
|
||||
COMMA,
|
||||
ARRAY_START,
|
||||
ARRAY_END,
|
||||
BOOLEAN,
|
||||
INTEGER,
|
||||
REAL,
|
||||
UUID,
|
||||
STRING_FIXED_SINGLE,
|
||||
STRING_FIXED_DOUBLE,
|
||||
STRING_DYNAMIC_START,
|
||||
URI,
|
||||
DATE,
|
||||
BINARY_STATIC,
|
||||
BINARY_DYNAMIC_START,
|
||||
}
|
||||
4
lib/classes/llsd/LLSDType.ts
Normal file
4
lib/classes/llsd/LLSDType.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { LLSDObject } from './LLSDObject';
|
||||
import { UUID } from '../UUID';
|
||||
|
||||
export type LLSDType = number | boolean | string | Buffer | LLSDObject | null | UUID | Date | LLSDType[];
|
||||
Reference in New Issue
Block a user