UNPKG

ember-material-icons

Version:

Google Material icons for your ember-cli app

351 lines (280 loc) 8.68 kB
import { assert } from "@glimmer/util"; import { Stack, DictSet } from "@glimmer/util"; import { TemplateMeta, SerializedBlock, SerializedTemplateBlock, SerializedTemplate, SerializedComponent, Core, Statement, Statements, Expression, Expressions, Ops } from '@glimmer/wire-format'; export type str = string; export type Params = Core.Params; export type Hash = Core.Hash; export type Path = Core.Path; export type StackValue = Expression | Params | Hash | str; export class Block { public type = "block"; statements: Statement[] = []; positionals: string[] = []; toJSON(): SerializedBlock { return { statements: this.statements, locals: this.positionals }; } push(statement: Statement) { this.statements.push(statement); } } export class TemplateBlock extends Block { public type = "template"; public yields = new DictSet<string>(); public named = new DictSet<string>(); public blocks: SerializedBlock[] = []; public hasPartials = false; toJSON(): SerializedTemplateBlock { return { statements: this.statements, locals: this.positionals, named: this.named.toArray(), yields: this.yields.toArray(), hasPartials: this.hasPartials }; } } export class ComponentBlock extends Block { public type = "component"; public attributes: Statements.Attribute[] = []; public arguments: Statements.Argument[] = []; private inParams = true; push(statement: Statement) { if (this.inParams) { if (Statements.isFlushElement(statement)) { this.inParams = false; } else if (Statements.isArgument(statement)) { this.arguments.push(statement); } else if (Statements.isAttribute(statement)) { this.attributes.push(statement); } else if (Statements.isModifier(statement)) { throw new Error('Compile Error: Element modifiers are not allowed in components'); } else { throw new Error('Compile Error: only parameters allowed before flush-element'); } } else { this.statements.push(statement); } } toJSON(): SerializedComponent { let args = this.arguments; let keys = args.map(arg => arg[1]); let values = args.map(arg => arg[2]); return { attrs: this.attributes, args: [keys, values], locals: this.positionals, statements: this.statements }; } } export class Template<T extends TemplateMeta> { public block = new TemplateBlock(); constructor(public meta: T) {} toJSON(): SerializedTemplate<T> { return { block: this.block.toJSON(), meta: this.meta }; } } export default class JavaScriptCompiler<T extends TemplateMeta> { static process<T extends TemplateMeta>(opcodes, meta): Template<T> { let compiler = new JavaScriptCompiler<T>(opcodes, meta); return compiler.process(); } private template: Template<T>; private blocks = new Stack<Block>(); private opcodes: any[]; private values: StackValue[] = []; constructor(opcodes, meta: T) { this.opcodes = opcodes; this.template = new Template(meta); } process(): Template<T> { this.opcodes.forEach(([opcode, ...args]) => { if (!this[opcode]) { throw new Error(`unimplemented ${opcode} on JavaScriptCompiler`); } this[opcode](...args); }); return this.template; } /// Nesting startBlock([program]) { let block: Block = new Block(); block.positionals = program.blockParams; this.blocks.push(block); } endBlock() { let { template, blocks } = this; template.block.blocks.push(blocks.pop().toJSON()); } startProgram() { this.blocks.push(this.template.block); } endProgram() { } /// Statements text(content: string) { this.push([Ops.Text, content]); } append(trusted: boolean) { this.push([Ops.Append, this.popValue<Expression>(), trusted]); } comment(value: string) { this.push([Ops.Comment, value]); } modifier(path: Path) { let params = this.popValue<Params>(); let hash = this.popValue<Hash>(); this.push([Ops.Modifier, path, params, hash]); } block(path: Path, template: number, inverse: number) { let params = this.popValue<Params>(); let hash = this.popValue<Hash>(); let blocks = this.template.block.blocks; assert(typeof template !== 'number' || blocks[template] !== null, 'missing block in the compiler'); assert(typeof inverse !== 'number' || blocks[inverse] !== null, 'missing block in the compiler'); this.push([Ops.Block, path, params, hash, blocks[template], blocks[inverse]]); } openElement(tag: str, blockParams: string[]) { if (tag.indexOf('-') !== -1) { this.startComponent(blockParams); } else { this.push([Ops.OpenElement, tag, blockParams]); } } flushElement() { this.push([Ops.FlushElement]); } closeElement(tag: str) { if (tag.indexOf('-') !== -1) { let component = this.endComponent(); this.push([Ops.Component, tag, component]); } else { this.push([Ops.CloseElement]); } } staticAttr(name: str, namespace: str) { let value = this.popValue<Expression>(); this.push([Ops.StaticAttr, name, value, namespace]); } dynamicAttr(name: str, namespace: str) { let value = this.popValue<Expression>(); this.push([Ops.DynamicAttr, name, value, namespace]); } trustingAttr(name: str, namespace: str) { let value = this.popValue<Expression>(); this.push([Ops.TrustingAttr, name, value, namespace]); } staticArg(name: str) { let value = this.popValue<Expression>(); this.push([Ops.StaticArg, name.slice(1), value]); } dynamicArg(name: str) { let value = this.popValue<Expression>(); this.push([Ops.DynamicArg, name.slice(1), value]); } yield(to: string) { let params = this.popValue<Params>(); this.push([Ops.Yield, to, params]); this.template.block.yields.add(to); } debugger() { this.push([Ops.Debugger, null, null]); } hasBlock(name: string) { this.pushValue<Expressions.HasBlock>([Ops.HasBlock, name]); this.template.block.yields.add(name); } hasBlockParams(name: string) { this.pushValue<Expressions.HasBlockParams>([Ops.HasBlockParams, name]); this.template.block.yields.add(name); } partial() { let params = this.popValue<Params>(); this.push([Ops.Partial, params[0]]); this.template.block.hasPartials = true; } /// Expressions literal(value: Expressions.Value | undefined) { if (value === undefined) { this.pushValue<Expressions.Undefined>([Ops.Undefined]); } else { this.pushValue<Expressions.Value>(value); } } unknown(path: string[]) { this.pushValue<Expressions.Unknown>([Ops.Unknown, path]); } arg(path: string[]) { this.template.block.named.add(path[0]); this.pushValue<Expressions.Arg>([Ops.Arg, path]); } get(path: string[]) { this.pushValue<Expressions.Get>([Ops.Get, path]); } concat() { this.pushValue<Expressions.Concat>([Ops.Concat, this.popValue<Params>()]); } helper(path: string[]) { let params = this.popValue<Params>(); let hash = this.popValue<Hash>(); this.pushValue<Expressions.Helper>([Ops.Helper, path, params, hash]); } /// Stack Management Opcodes startComponent(blockParams: string[]) { let component = new ComponentBlock(); component.positionals = blockParams; this.blocks.push(component); } endComponent(): SerializedComponent { let component = this.blocks.pop(); assert(component.type === 'component', "Compiler bug: endComponent() should end a component"); return (component as ComponentBlock).toJSON(); } prepareArray(size: number) { let values = []; for (let i = 0; i < size; i++) { values.push(this.popValue()); } this.pushValue<Params>(values); } prepareObject(size: number) { assert(this.values.length >= size, `Expected ${size} values on the stack, found ${this.values.length}`); let keys: string[] = new Array(size); let values: Expression[] = new Array(size); for (let i = 0; i < size; i++) { keys[i] = this.popValue<str>(); values[i] = this.popValue<Expression>(); } this.pushValue<Hash>([keys, values]); } /// Utilities push(args: Statement) { while (args[args.length - 1] === null) { args.pop(); } this.blocks.current.push(args); } pushValue<S extends Expression | Params | Hash>(val: S) { this.values.push(val); } popValue<T extends StackValue>(): T { assert(this.values.length, "No expression found on stack"); return this.values.pop() as T; } }