UNPKG

senselogic-def

Version:
656 lines (558 loc) 16.3 kB
// -- IMPORTS import { processDefQuotedString } from './processing.js'; // -- CONSTANTS var decimalRealExpression = /^-?\d+(\.\d+)?$/, exponentialDecimalRealExpression = /^-?\d+(\.\d+)?[eE][-+]?\d+$/, hexadecimalIntegerExpression = /^0x[0-9A-Fa-f]+$/; // -- FUNCTIONS function getKeyText( key ) { if ( key !== null && typeof key === 'object' ) { return JSON.stringify( key ); } else { return String( key ); } } // ~~ function getTokenArray( text ) { let tokenArray = []; let characterIndex = 0; while ( characterIndex < text.length ) { if ( text[ characterIndex ] === '\\' ) { if ( characterIndex + 1 < text.length ) { tokenArray.push( text[ characterIndex ] + text[ characterIndex + 1 ] ); characterIndex += 2; } else { tokenArray.push( text[ characterIndex ] ); ++characterIndex; } } else { let postCharacterIndex = characterIndex; while ( postCharacterIndex < text.length && text[ postCharacterIndex ] !== '\\' ) { ++postCharacterIndex; } tokenArray.push( text.substring( characterIndex, postCharacterIndex ) ); characterIndex = postCharacterIndex; } } return tokenArray; } // ~~ function getUnescapedText( tokenArray ) { let unescapedText = ''; for ( let token of tokenArray ) { if ( token.length === 2 && token[ 0 ] === '\\' ) { switch ( token[ 1 ] ) { case 'n': { unescapedText += '\n'; break; } case 't': { unescapedText += '\t'; break; } case 'r': { unescapedText += '\r'; break; } case 'b': { unescapedText += '\b'; break; } case 'f': { unescapedText += '\f'; break; } case '0': { unescapedText += '\0'; break; } default: { unescapedText += token[ 1 ]; } } } else { unescapedText += token; } } return unescapedText; } // ~~ export function throwParsingError( message, context, level ) { message = message + '\nText :\n' + context.text + '\nFile : ' + context.filePath + '\nLine ' + context.lineIndex + ' @ ' + level; if ( context.lineIndex > 0 && context.lineIndex <= context.lineArray.length ) { message += ' : ' + context.lineArray[ context.lineIndex - 1 ]; } throw new Error( message ); } // ~~ function parseDefLine( context, level ) { let line = context.lineArray[ context.lineIndex ]; let trimmedLine = line.trimStart(); let levelSpaceCount = level * context.levelSpaceCount; let lineSpaceCount = line.length - trimmedLine.length; if ( trimmedLine === '' ) { line = ''; lineSpaceCount = 0; } else { if ( lineSpaceCount < levelSpaceCount ) { throwParsingError( 'Invalid DEF line', context, level ); } line = line.slice( levelSpaceCount ).trimEnd(); lineSpaceCount -= levelSpaceCount; } context.lineIndex++; return { line, lineSpaceCount }; } // ~~ function parseDefUnquotedString( context, level ) { let string = ''; while ( context.lineIndex < context.lineArray.length ) { let { line, lineSpaceCount } = parseDefLine( context, level ); let tokenArray = getTokenArray( line ); let lastToken = ( tokenArray.length > 0 ) ? tokenArray[ tokenArray.length - 1 ] : ''; if ( lastToken === '\\' ) { tokenArray.pop(); string += getUnescapedText( tokenArray ); } else { if ( line.endsWith( '¨' ) && lastToken !== '\\¨' ) { tokenArray[ tokenArray.length - 1 ] = lastToken.slice( 0, -1 ); } string += getUnescapedText( tokenArray ); return string; } } throwParsingError( 'Invalid DEF unquoted string', context, level ); } // ~~ function parseDefQuotedString( context, level ) { let firstLineIndex = context.lineIndex + 1; let string = ''; let quote = ''; let escapedQuote = ''; while ( context.lineIndex < context.lineArray.length ) { let { line, lineSpaceCount } = parseDefLine( context, level ); if ( context.lineIndex === firstLineIndex ) { quote = line[ 0 ]; line = line.slice( 1 ); escapedQuote = '\\' + quote; } let tokenArray = getTokenArray( line ); let lastToken = ( tokenArray.length > 0 ) ? tokenArray[ tokenArray.length - 1 ] : ''; if ( lastToken === '\\' ) { tokenArray.pop(); string += getUnescapedText( tokenArray ); } else if ( lastToken.endsWith( quote ) && lastToken !== escapedQuote ) { tokenArray[ tokenArray.length - 1 ] = lastToken.slice( 0, -1 ); string += getUnescapedText( tokenArray ); if ( quote === context.stringProcessingQuote && context.processQuotedStringFunction !== null ) { return context.processQuotedStringFunction( string, context, level ); } else { return string; } } else { if ( lastToken.endsWith( '¨' ) && lastToken !== '\\¨' ) { tokenArray[ tokenArray.length - 1 ] = lastToken.slice( 0, -1 ); } string += getUnescapedText( tokenArray ) + '\n'; } } throwParsingError( 'Invalid DEF quoted string', context, level ); } // ~~ function parseDefArray( context, level ) { let array = []; while ( context.lineIndex < context.lineArray.length ) { let { line, lineSpaceCount } = parseDefLine( context, level ); if ( lineSpaceCount === 0 && line === ']' ) { return array; } else if ( lineSpaceCount === 0 && line === ']{}' ) { if ( array.length == 0 ) { return []; } else { if ( Array.isArray( array[ 0 ] ) ) { if ( array.length === 1 ) { return []; } else { let keyArray = []; for ( let key of array[ 0 ] ) { keyArray.push( getKeyText( key ) ); } let keyCount = keyArray.length; let objectArray = []; let objectCount = array.length - 1; for ( let objectIndex = 0; objectIndex < objectCount; ++objectIndex ) { let valueArray = array[ objectIndex + 1 ]; if ( Array.isArray( valueArray ) && valueArray.length === keyCount ) { let object = {}; for ( let keyIndex = 0; keyIndex < keyCount; ++keyIndex ) { object[ keyArray[ keyIndex ] ] = valueArray[ keyIndex ]; } objectArray.push( object ); } else { throwParsingError( 'Invalid DEF object array', context, level ); } } return objectArray; } } else { throwParsingError( 'Invalid DEF object array', context, level ); } } } else if ( lineSpaceCount === 0 && line === ']()' ) { if ( array.length == 0 ) { return []; } else { if ( Array.isArray( array[ 0 ] ) ) { if ( array.length === 1 ) { return []; } else { let keyArray = []; for ( let key of array[ 0 ] ) { keyArray.push( getKeyText( key ) ); } let keyCount = keyArray.length; let mapArray = []; let mapCount = array.length - 1; for ( let mapIndex = 0; mapIndex < mapCount; ++mapIndex ) { let valueArray = array[ mapIndex + 1 ]; if ( Array.isArray( valueArray ) && valueArray.length === keyCount ) { let map = new Map(); for ( let keyIndex = 0; keyIndex < keyCount; ++keyIndex ) { map.set( keyArray[ keyIndex ], valueArray[ keyIndex ] ); } mapArray.push( map ); } else { throwParsingError( 'Invalid DEF map array', context, level ); } } return mapArray; } } else { throwParsingError( 'Invalid DEF map array', context, level ); } } } else { context.lineIndex--; let value = parseDefValue( context, level + 1 ); array.push( value ); } } throwParsingError( 'Invalid DEF array', context, level ); } // ~~ function parseDefObject( context, level ) { let object = {}; while ( context.lineIndex < context.lineArray.length ) { let { line, lineSpaceCount } = parseDefLine( context, level ); if ( lineSpaceCount === 0 && line === '}' ) { return object; } else { context.lineIndex--; let key = parseDefValue( context, level + 1 ); let value = parseDefValue( context, level + 2 ); if ( key !== null && typeof key === 'object' ) { object[ JSON.stringify( key ) ] = value; } else { object[ String( key ) ] = value; } } } throwParsingError( 'Invalid DEF object', context, level ); } // ~~ function parseDefMap( context, level ) { let map = new Map(); while ( context.lineIndex < context.lineArray.length ) { let { line, lineSpaceCount } = parseDefLine( context, level ); if ( lineSpaceCount === 0 && line === ')' ) { return map; } else { context.lineIndex--; let key = parseDefValue( context, level + 1 ); let value = parseDefValue( context, level + 2 ); map.set( key, value ); } } throwParsingError( 'Invalid DEF map', context, level ); } // ~~ function parseDefValue( context, level ) { if ( context.lineIndex < context.lineArray.length ) { let { line, lineSpaceCount } = parseDefLine( context, level ); if ( line === '[' ) { return parseDefArray( context, level ); } else if ( line === '{' ) { return parseDefObject( context, level ); } else if ( line === '(' ) { return parseDefMap( context, level ); } else if ( line.startsWith( '\'' ) || line.startsWith( '"' ) || line.startsWith( '`' ) || line.startsWith( '´' ) ) { context.lineIndex--; return parseDefQuotedString( context, level ); } else { let tokenArray = getTokenArray( line ); if ( tokenArray.length === 1 && tokenArray[ 0 ] === line.trim() ) { if ( line === 'undefined' ) { return undefined; } else if ( line === 'null' ) { return null; } else if ( line === 'false' ) { return false; } else if ( line === 'true' ) { return true; } else if ( line === 'NaN' ) { return NaN; } else if ( line === '-Infinity' ) { return -Infinity; } else if ( line === 'Infinity' ) { return Infinity; } else if ( hexadecimalIntegerExpression.test( line ) || decimalRealExpression.test( line ) || exponentialDecimalRealExpression.test( line ) ) { let number = Number( line ); if ( !isNaN( number ) ) { return number; } } } context.lineIndex--; return parseDefUnquotedString( context, level ); } } else { throwParsingError( 'Invalid DEF value', context, level ); } } // ~~ export function parseDefText( text, { baseFolderPath = '', filePath = '', readTextFileFunction = null, stringProcessingQuote = '\'', processQuotedStringFunction = processDefQuotedString, levelSpaceCount = 4 } = {} ) { let lineArray = text .trimEnd() .replaceAll( '\t', ' '.repeat( levelSpaceCount ) ) .replaceAll( '\r', '' ) .split( '\n' ); let context = { baseFolderPath, filePath, readTextFileFunction, stringProcessingQuote, processQuotedStringFunction, levelSpaceCount, text, lineArray, lineIndex: 0 }; return parseDefValue( context, 0 ); }