UNPKG

plaxtony

Version:

Static code analysis of SC2 Galaxy Script

287 lines (255 loc) 8.78 kB
import * as gt from './types'; import { tokenToString } from './scanner'; import { getKindName, isToken} from './utils'; export class Printer { output: string[]; indent: number; emptyLine: boolean = true; private write(text: string) { if (this.emptyLine) { this.output.push('\t'.repeat(this.indent)); this.emptyLine = false; } this.output.push(text); } private whitespace(text: string = ' ') { if (this.emptyLine) { this.output.push('\t'.repeat(this.indent)); this.emptyLine = false; } if (text === '\n') { this.newLine(); } else { this.output.push(text); } } private newLine() { this.output.push('\n'); this.emptyLine = true; } private increaseIndent() { ++this.indent; } private decreaseIndent() { --this.indent; } private emitNode(node: gt.Node) { switch (node.kind) { case gt.SyntaxKind.FunctionDeclaration: { const func = <gt.FunctionDeclaration>node; if (func.modifiers && func.modifiers.length > 0) { this.emitNodeList(func.modifiers, ' '); this.write(' '); } this.emitNode(func.type); this.write(' '); this.emitNode(func.name); this.write('('); this.emitNodeList(func.parameters, ',', ' '); this.write(')'); if (func.body) { this.newLine(); this.emitNode(func.body); } else { this.write(';'); } this.newLine(); break; } case gt.SyntaxKind.StructDeclaration: { const struct = <gt.StructDeclaration>node; this.write('struct '); this.emitNode(struct.name); this.write(' {'); this.newLine(); this.increaseIndent(); this.emitNodeList(struct.members, '', '\n', true); this.decreaseIndent(); this.write('};'); this.newLine(); break; } case gt.SyntaxKind.VariableDeclaration: case gt.SyntaxKind.PropertyDeclaration: { const variable = <gt.VariableDeclaration>node; if (variable.modifiers && variable.modifiers.length > 0) { this.emitNodeList(variable.modifiers, ' '); this.write(' '); } this.emitNode(variable.type); this.write(' '); this.emitNode(variable.name); if (variable.kind === gt.SyntaxKind.VariableDeclaration && variable.initializer) { this.whitespace(' '); this.write('='); this.whitespace(' '); this.emitNode(variable.initializer); } this.write(';'); break; } case gt.SyntaxKind.ParameterDeclaration: { const param = <gt.ParameterDeclaration>node; if (param.modifiers && param.modifiers.length > 0) { this.emitNodeList(param.modifiers, ' '); this.write(' '); } this.emitNode(param.type); this.write(' '); this.emitNode(param.name); break; } case gt.SyntaxKind.TypedefDeclaration: { const typedef = <gt.TypedefDeclaration>node; this.write('typedef '); this.emitNode(typedef.type); this.write(' '); this.emitNode(typedef.name); this.write(';'); break; } case gt.SyntaxKind.Identifier: { const identifier = <gt.Identifier>node; this.write(identifier.name); break; } case gt.SyntaxKind.NumericLiteral: case gt.SyntaxKind.StringLiteral: { const literal = <gt.Literal>node; this.write(literal.text); break; } case gt.SyntaxKind.ArrayType: { const type = <gt.ArrayTypeNode>node; this.emitNode(type.elementType); this.write('['); this.emitNode(type.size); this.write(']'); break; } case gt.SyntaxKind.MappedType: { const type = <gt.MappedTypeNode>node; this.emitNode(type.returnType); this.write('<'); this.emitNodeList(type.typeArguments, ',', ' '); this.write('>'); break; } case gt.SyntaxKind.BinaryExpression: { const expr = <gt.BinaryExpression>node; this.emitNode(expr.left); this.whitespace(' '); this.emitNode(expr.operatorToken); this.whitespace(' '); this.emitNode(expr.right); break; } case gt.SyntaxKind.PrefixUnaryExpression: { const expr = <gt.PrefixUnaryExpression>node; this.emitNode(expr.operator); this.emitNode(expr.operand); break; } case gt.SyntaxKind.PostfixUnaryExpression: { const expr = <gt.PostfixUnaryExpression>node; this.emitNode(expr.operand); this.emitNode(expr.operator); break; } case gt.SyntaxKind.ParenthesizedExpression: { const expr = <gt.ParenthesizedExpression>node; this.write('('); this.emitNode(expr.expression); this.write(')'); break; } case gt.SyntaxKind.CallExpression: { const expr = <gt.CallExpression>node; this.emitNode(expr.expression); this.write('('); this.emitNodeList(expr.arguments, ',', ' '); this.write(')'); break; } case gt.SyntaxKind.ElementAccessExpression: { const expr = <gt.ElementAccessExpression>node; this.emitNode(expr.expression); this.write('['); this.emitNode(expr.argumentExpression); this.write(']'); break; } case gt.SyntaxKind.PropertyAccessExpression: { const expr = <gt.PropertyAccessExpression>node; this.emitNode(expr.expression); this.write('.'); this.emitNode(expr.name); break; } case gt.SyntaxKind.Unknown: { // TODO: throw exception? break; } default: { if (isToken(node)) { this.write(tokenToString(node.kind)); break; } throw new Error(`unhandled node '${getKindName(node.kind)}'`); } } } private emitNodeList(nodesList: ReadonlyArray<gt.Node>, textSeparator: string = undefined, whitespaceSeparator: string = undefined, includeSeparatorAtEnd: boolean = false) { nodesList.forEach((node: gt.Node, index: number) => { this.emitNode(node); if (includeSeparatorAtEnd || nodesList.length !== (index + 1)) { if (textSeparator) { this.write(textSeparator); } if (whitespaceSeparator) { this.whitespace(whitespaceSeparator); } } }); } private reset() { this.output = []; this.indent = 0; } private flush(): string { const text = this.output.join(''); this.reset(); return text; } public printNode(node: gt.Node): string { this.emitNode(node); return this.flush(); } public printFile(sourceFile: gt.SourceFile): string { this.newLine(); return this.flush(); } constructor() { this.reset(); } }