FormatCompiler.ts
134 lines
| 4.0 KiB
| video/mp2t
|
TypeScriptLexer
|
|
r79 | import { FormatScanner, TokeType } from "./FormatScanner"; | ||
|
|
r82 | import { isNullOrEmptyString, isPrimitive, get } from "../safe"; | ||
| import { TextWriter, MapOf } from "../interfaces"; | ||||
| type CompiledPattern = (writer: TextWriter, args: any) => void; | ||||
|
|
r79 | |||
| export class FormatCompiler { | ||||
|
|
r81 | _scanner: FormatScanner; | ||
|
|
r115 | static _cache: MapOf<CompiledPattern> = {}; | ||
| _parts: Array<string | { name: string; format?: string; }>; | ||||
| constructor(scanner: FormatScanner) { | ||||
| this._scanner = scanner; | ||||
| this._parts = []; | ||||
| } | ||||
|
|
r79 | |||
|
|
r115 | compile() { | ||
| this.visitText(); | ||||
| const parts = this._parts; | ||||
|
|
r82 | |||
|
|
r115 | return (writer: TextWriter, args: any) => { | ||
| parts.forEach(x => { | ||||
| if (isPrimitive(x)) | ||||
| writer.writeValue(x); | ||||
| else | ||||
| writer.writeValue(get(x.name, args), x.format); | ||||
| }); | ||||
| }; | ||||
| } | ||||
| static compile(pattern: string) { | ||||
|
|
r82 | let compiledPattern = this._cache && this._cache[pattern]; | ||
| if (!compiledPattern) { | ||||
|
|
r115 | const compiler = new this(new FormatScanner(pattern)); | ||
|
|
r81 | |||
|
|
r115 | compiledPattern = compiler.compile(); | ||
| this._cache[pattern] = compiledPattern; | ||||
|
|
r82 | } | ||
| return compiledPattern; | ||||
|
|
r81 | } | ||
| visitText() { | ||||
| while (this._scanner.next()) { | ||||
|
|
r82 | // console.log(this._scanner.getTokenType(), this._scanner.getTokenValue()); | ||
|
|
r81 | switch (this._scanner.getTokenType()) { | ||
| case TokeType.CurlOpen: | ||||
| this.visitCurlOpen(); | ||||
| break; | ||||
| case TokeType.CurlClose: | ||||
| this.visitCurlClose(); | ||||
| break; | ||||
| default: | ||||
| this.pushText(this._scanner.getTokenValue()); | ||||
| } | ||||
|
|
r79 | } | ||
| } | ||||
|
|
r81 | visitCurlClose() { | ||
| if (!this._scanner.next()) | ||||
| this.dieUnexpectedEnd("}"); | ||||
| if (this._scanner.getTokenType() !== TokeType.CurlClose) | ||||
| this.dieUnexpectedToken("}"); | ||||
| this.pushText("}"); | ||||
| } | ||||
| visitCurlOpen() { | ||||
|
|
r82 | if (!this._scanner.next()) | ||
| this.dieUnexpectedEnd("{ | TEXT"); | ||||
| if (this._scanner.getTokenType() === TokeType.CurlOpen) | ||||
| this.pushText("{"); | ||||
| else | ||||
| this.visitTemplateSubst(); | ||||
|
|
r79 | } | ||
|
|
r81 | visitTemplateSubst() { | ||
| if (this._scanner.getTokenType() !== TokeType.Text) | ||||
| this.dieUnexpectedToken("TEXT"); | ||||
|
|
r79 | |||
|
|
r81 | const fieldName = this._scanner.getTokenValue(); | ||
|
|
r115 | const filedFormat = this.readColon() ? this.readFieldFormat() : undefined; | ||
|
|
r82 | |||
| if (this._scanner.getTokenType() !== TokeType.CurlClose) | ||||
| this.dieUnexpectedToken("}"); | ||||
|
|
r79 | |||
| this.pushSubst(fieldName, filedFormat); | ||||
| } | ||||
|
|
r80 | |||
|
|
r81 | readFieldFormat() { | ||
| const parts = new Array<string>(); | ||||
|
|
r82 | do { | ||
|
|
r81 | if (this._scanner.getTokenType() === TokeType.CurlClose) { | ||
| return parts.join(""); | ||||
| } else { | ||||
| parts.push(this._scanner.getTokenValue()); | ||||
| } | ||||
|
|
r82 | } while (this._scanner.next()); | ||
|
|
r81 | |||
| this.dieUnexpectedEnd("}"); | ||||
|
|
r79 | } | ||
|
|
r80 | |||
|
|
r81 | readColon() { | ||
| if (!this._scanner.next()) | ||||
| this.dieUnexpectedEnd(); | ||||
| if (this._scanner.getTokenType() !== TokeType.Colon) | ||||
| return false; | ||||
| if (!this._scanner.next()) | ||||
| this.dieUnexpectedEnd(); | ||||
| return true; | ||||
|
|
r79 | } | ||
|
|
r80 | |||
|
|
r115 | pushSubst(fieldName: string, filedFormat?: string) { | ||
|
|
r82 | // console.log("pushSubst ", fieldName, filedFormat); | ||
| this._parts.push({ name: fieldName, format: filedFormat }); | ||||
|
|
r79 | } | ||
| pushText(text: string) { | ||||
|
|
r82 | this._parts.push(text); | ||
|
|
r79 | } | ||
|
|
r115 | dieUnexpectedToken(expected?: string): never { | ||
|
|
r81 | throw new Error(isNullOrEmptyString(expected) ? | ||
| `Unexpected token ${this._scanner.getTokenValue()}` : | ||||
| `Unexpected token ${this._scanner.getTokenValue()}, expected ${expected}` | ||||
| ); | ||||
|
|
r79 | } | ||
|
|
r115 | dieUnexpectedEnd(expected?: string): never { | ||
|
|
r81 | throw new Error(isNullOrEmptyString(expected) ? "Unexpected end of the string" : `Unexpected end of the string, expected ${expected}`); | ||
|
|
r79 | } | ||
| } | ||||
