SnareChops/SQiggL

View on GitHub
src/lexers/variable.lexer.ts

Summary

Maintainability
D
2 days
Test Coverage
import {Lexer, LexerOptions} from '../lexer';
import {DSLVariable, DSLVariableType} from '../dsl';
import {SQiggLError} from "../error";

/**
 * The Lexer responsible for all DSL generation of variable statements
 *
 * @internal
 */
export class VariableLexer{

    /**
     * Creates a new instance of the VariableLexer
     *
     * @internal
     * @param options {LexerOptions} - The LexerOptions to use when generating DSL
     */
    constructor(private options: LexerOptions){}

    /**
     * Walk through a variable declaration and return a variable DSL
     *
     * TODO: Add rules
     *
     * @internal
     * @param input {string}
     * @returns {DSLVariable}
     */
    public invoke(input: string): DSLVariable{
        let currentType: DSLVariableType = DSLVariableType.key,
            original: string = input,
            idx: number = 0,
            startIdx: number = 0,
            inString: boolean = false,
            isArray: boolean = false,
            stringChar: string,
            dsl: DSLVariable = {literal: input};
        while(idx < input.length){
            switch(input.charAt(idx)){
                case "'":
                    if(currentType === DSLVariableType.key) throw SQiggLError('LV2000', 'Variable keys should not be wrapped in quotes.');
                    if(!inString){
                        inString = true;
                        stringChar = "'";
                        idx++;
                        break;
                    }
                    if(input[idx] === stringChar){
                        if(input[idx - 1] === this.options.stringEscapeChar){
                            input = input.slice(0, idx - 1) + input.slice(idx);
                            break;
                        } else inString = false;
                    }
                    idx++;
                    break;
                case '"':
                    if(currentType === DSLVariableType.key) throw SQiggLError('LV2000', 'Variable keys should not be wrapped in quotes.');
                    if(!inString){
                        inString = true;
                        stringChar = '"';
                        idx++;
                        break;
                    }
                    if(input[idx] === stringChar){
                        if(input[idx - 1] === this.options.stringEscapeChar){
                            input = input.slice(0, idx -1) + input.slice(idx);
                            break;
                        }
                        else inString = false;
                    }
                    idx++;
                    break;
                case '[':
                    if(currentType === DSLVariableType.key) throw SQiggLError('LV2001', `Invalid character '[' found in variable key: '${original}'.`);
                    if(!inString){
                        if (idx !== startIdx) throw SQiggLError('LV2002', `Arrays in variables cannot be nested. At '${original}'.`);
                        input = input.slice(0, idx) + input.slice(idx + 1);
                        isArray = true;
                    }
                    break;
                case ']':
                    if(currentType === DSLVariableType.key) throw SQiggLError('LV2001', `Invalid character ']' found in variable key: '${original}'.`);
                    if(!inString){
                        if(idx !== input.length - 1) throw SQiggLError('LV2003', `Variables that define arrays must not include other values: '${original}'.`);
                        input = input.slice(0, idx) + input.slice(idx+1);
                    }
                    console.log(input);
                    idx++;
                    break;
                case this.options.variableAssignmentChar:
                    dsl = this.generateDSL(dsl, currentType, input.slice(startIdx, idx), isArray);
                    idx++;
                    startIdx = idx;
                    currentType = DSLVariableType.value;
                    break;
                default:
                    idx++;
            }
        }
        if(startIdx !== 0) dsl = this.generateDSL(dsl, currentType, input.slice(startIdx), isArray);
        return dsl;
    }

    /**
     * Generate the DSL for a variable piece by piece. Building on the definition until complete.
     *
     * @internal
     * @param dsl {DSLVariable}
     * @param type {DSLVariableType}
     * @param value {string}
     * @param isArray {boolean}
     * @returns {DSLVariable}
     */
    private generateDSL(dsl: DSLVariable, type: DSLVariableType, value: string, isArray: boolean): DSLVariable{
        switch(type){
            case DSLVariableType.key:
                dsl.key = value;
                break;
            case DSLVariableType.value:
                dsl.value = isArray ? this.convertCSVToArray(value) : value;
                break;
            /* istanbul ignore next */
            default:
                /* istanbul ignore next */
                throw SQiggLError('LV100', 'Unrecognized DSLVariableType');
        }
        return dsl;
    }

    private convertCSVToArray(input: string): string[]{
        let items = input.split(','),
            result: string[] = [];
        for(var item of items){
            item = item.trim();
            result.push(Lexer.removeEscapeCharactersFromStringPart(item, this.options));
        }
        return result;
    }

    /**
     * Clean and prepare the input for parsing
     *
     * @internal
     * @param input {string}
     * @returns {string}
     */
    public static cleanStringForLexing(input: string): string{
        return input.replace('\n', ' ').replace(/ (?=(?:(?:\\.|"(?:\\.|[^"\\])*"|[^\\'"])*'(?:\\.|"(?:\\.|[^"'\\])*"|[^\\'])*')*(?:\\.|"(?:\\.|[^"\\])*"|[^\\'])*$)(?=(?:(?:\\.|'(?:\\.|[^'\\])*'|[^\\'"])*"(?:\\.|'(?:\\.|[^'"\\])*'|[^\\"])*")*(?:\\.|'(?:\\.|[^'\\])*'|[^\\"])*$)/g, '').trim();
    }
}