Interpreter

Setting up grammar, sentence structure, and mechanism(s) for translating

This pattern is used to translate something from one representation into another given the rules. The benefits come with scalability of the solution. An item can be translated into many representations using the same mechanism. That is quite convenient. The drawback of the pattern also comes with the increase of translation mechanisms: maintaining grammar and rules may become tedious.
The pattern implementation requires two things: first a Context class that provides input, the original representation, and a placeholder for output, the end result. The second required thing is a set of rules, and it can be any class as long as it implements the Interpret method. The code example below shows a Context and an abstract class that specifies the Interpret method.
class Context {
    input: string;
    output: string;
    constructor(input: string) {
        this.input = input;
        this.output = '';
    }
}

abstract class Expression {
    Interpret(context: Context): void { }
}
Next, the set of rules, the meat of the pattern. The following implementation converts Oracle Data Definition Language into C# class code. This program helps convert an existing Oracle database into a code-first Entity Framework project. So an item like "STRING_FIELD" VARCHAR2(6 BYTE) is converted to public string String_Field {get; set;} = string.Empty;, and a field declared as, say, "ID" NUMBER(15,0) needs to be translated to public int Id {get; set;} and so on. The class listed below, has one method, Interpret, that takes an item of type Context, then consumes that Context's input field and appends translation to output field. Input/output could be properties, but the type Context in this case acts as a container, and the OracleToEFExpression class knows what to do with it. If another translation is needed, a different class can do that as long as it implements the Expression interface and uses input to provide a useful output.
class OracleToEFExpression implements Expression {
    Interpret(context: Context): void {
        let properties = context.input.split('\n');
        properties.forEach(element => {
            if (element.length > 0) {
                let name = this.extractName(element);
                let type = this.extractType(element)
                let specifiedDefaultValue = this.extractDefaultConstraint(element);
                let determinedDefaultValue = this.convertDefaultValue(specifiedDefaultValue, type);
                context.output += `public ${type} ${this.convertNameToCamelCase(name)} { get; set; }`;
                if (determinedDefaultValue.length > 0)
                    context.output += ` = ${determinedDefaultValue};`
                context.output += '\n'
            }
        });

        console.log(context.output)
    }

    extractName(input: string): string {
        let firstQuote = input.indexOf('"');
        let nextQuote = input.indexOf('"', firstQuote + 1)
        if (firstQuote >= 0 && nextQuote > firstQuote) {
            return input.substring(firstQuote + 1, nextQuote)
        }
        return 'NO_NAME_FOUND'
    }

    extractType(input: string): string {
        let firstQuote = input.indexOf('"');
        let nextQuote = input.indexOf('"', firstQuote + 1)
        if (input.indexOf('VARCHAR2') > nextQuote) {
            return 'string'
        } else if (input.indexOf('NUMBER') > nextQuote) {
            return 'int'
        } else if (input.indexOf('CHAR') > nextQuote) {
            return 'bool'
        } else if (input.indexOf('TIMESTAMP') > nextQuote) {
            return 'DateTime'
        }
        return 'int'
    }

    extractDefaultConstraint(input: string): string {
        let defaultConstraintStartsAt = input.indexOf(' DEFAULT ');
        if (defaultConstraintStartsAt < 0)
            return '';
        return input.substring(defaultConstraintStartsAt, input.length);
    }

    convertDefaultValue(input: string, determinedDataType: string): string {

        if (determinedDataType === 'string')
            return 'string.Empty'
        if (input.trim().length === 0)
            return '';
        if (determinedDataType === 'int') {
            let ret = input.replace('DEFAULT', '');
            return ret.substring(0, ret.indexOf(','))
        }
        if (determinedDataType === 'bool')
            return input.indexOf('0') >= 0 ? 'false' : 'true';

        return '';
    }

    convertNameToCamelCase(input: string): string {
        let parts = input.split('_');
        parts.forEach((p, i) => parts[i] = this.camelCaseString(p));
        return parts.join('_');
    }

    camelCaseString(input: string): string {
        return input[0] + input.substring(1, input.length).toLocaleLowerCase()
    }
}

The following code is used to call the interpreter mechanism described above:
let c = new Context(`
"ID" NUMBER(15,0), 
    "EMPLOYEE_CODE" VARCHAR2(6 BYTE), 
    "COMPANY_ID" NUMBER(15,0), 
    "FIRST_NAME" VARCHAR2(50 BYTE),
    "LAST_NAME" VARCHAR2(50 BYTE),
    "IS_ACTIVE" CHAR(1 BYTE) = '1',
    "HIRE_DATE" TIMESTAMP (6)`);


let oef = new OracleToEFExpression();
oef.Interpret(c)
And this is what we get back:
public int Id { get; set; }
public string Employee_Code { get; set; } = string.Empty;
public int Company_Id { get; set; }
public string First_Name { get; set; } = string.Empty;   
public string Last_Name { get; set; } = string.Empty;    
public bool Is_Active { get; set; }
public DateTime Hire_Date { get; set; }
Definitely useful for large conversions.