rayjs/bindings/src/generation.ts

193 lines
5.1 KiB
TypeScript

export class StringWriter {
private buffer = ''
write(value: string): void {
this.buffer += value;
}
writeLine(value: string = ''): void {
this.buffer += value + '\n';
}
toString(): string {
return this.buffer;
}
}
export class CodeWriter extends StringWriter {
private indent = 0
private needsIndent = true
writeGenerator(generator: CodeGenerator){
const tokens = generator.iterateTokens()
const text = generator.iterateText()
const children = generator.iterateChildren()
let result = tokens.next();
while (!result.done) {
switch (result.value) {
case Token.STRING:
const str = text.next().value
if(this.needsIndent){
this.write(" ".repeat(this.indent))
this.needsIndent = false
}
this.write(str)
break
case Token.GOSUB:
const sub = children.next().value
this.writeGenerator(sub)
break
case Token.INDENT:
this.indent++
break
case Token.UNINDENT:
this.indent = this.indent > 0 ? this.indent-1 : 0
break
case Token.NEWLINE:
this.write("\n")
this.needsIndent = true
break
default:
break
}
result = tokens.next()
}
}
}
enum Token {
STRING = 0,
NEWLINE = 1,
INDENT = 2,
UNINDENT = 3,
GOSUB = 4
}
export class CodeGenerator {
private children: CodeGenerator[] = []
private text: string[] = []
private tokens: Token[] = []
iterateTokens(){
return this.tokens[Symbol.iterator]()
}
iterateText(){
return this.text[Symbol.iterator]()
}
iterateChildren(){
return this.children[Symbol.iterator]()
}
line(text: string){
this.tokens.push(Token.STRING, Token.NEWLINE)
this.text.push(text)
}
comment(text: string){
this.line("// " + text)
}
call(name: string, params: string[], returnVal: FunctionArgument | null = null){
if(returnVal) this.inline(`${returnVal.type} ${returnVal.name} = `)
this.inline(name + "(")
this.inline(params.join(", "))
this.statement(")")
}
declare(name: string, type: string, isStatic = false, initValue: string | null = null){
if(isStatic) this.inline("static ")
this.inline(type + " " + name)
if(initValue) this.inline(" = " + initValue)
this.statement("")
}
child(sub: CodeGenerator){
this.tokens.push(Token.GOSUB)
this.children.push(sub)
}
childFunc(sub: CodeGenerator, func: (gen: CodeGenerator) => void){
this.child(sub)
func(sub)
}
childFuncBody(sub: FunctionGenerator | ConditionGenerator, func: (gen: CodeGenerator) => void): void {
this.child(sub)
func(sub.body)
}
inline(str: string){
this.tokens.push(Token.STRING)
this.text.push(str)
}
statement(str: string){
this.line(str+";")
}
breakLine(){
this.tokens.push(Token.NEWLINE)
}
public indent(){
this.tokens.push(Token.INDENT)
}
public unindent(){
this.tokens.push(Token.UNINDENT)
}
}
export class ConditionGenerator extends CodeGenerator {
body = new CodeGenerator()
constructor(condition: string){
super()
this.line("if("+condition+") {")
this.indent()
this.child(this.body)
this.unindent()
this.line("}")
}
}
export class HeaderGenerator extends CodeGenerator {
guardStart(name: string){
this.line("#ifndef " + name)
this.line("#define " + name)
}
guardEnd(name: string){
this.line("#endif // " + name)
}
include(name: string){
this.line("#include <" + name + ">")
}
}
export interface FunctionArgument {
type: string,
name: string
}
export class CustomFunctionGenerator<T extends CodeGenerator> extends CodeGenerator {
constructor(public readonly name: string, returnType: string, args: FunctionArgument[], public readonly body: T, isStatic = false){
super()
if(isStatic) this.inline("static ")
this.inline(returnType + " " + name + "(")
this.inline(args.map(x => x.type + " " + x.name).join(", "))
this.inline(") {")
this.breakLine()
this.indent()
this.child(body)
this.unindent()
this.line("}")
}
}
export class FunctionGenerator extends CustomFunctionGenerator<CodeGenerator> {
constructor(name: string, returnType: string, args: FunctionArgument[], isStatic = false, body: CodeGenerator = new CodeGenerator()){
super(name, returnType, args, body, isStatic)
}
}