2023-05-06 21:43:40 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-05-08 14:43:50 +00:00
|
|
|
export interface FunctionArgument {
|
|
|
|
type: string,
|
2023-05-14 20:19:47 +00:00
|
|
|
name: string,
|
|
|
|
description?: string
|
2023-05-08 14:43:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export abstract class GenericCodeGenerator<T extends CodeGenerator> {
|
2023-05-06 21:43:40 +00:00
|
|
|
private children: CodeGenerator[] = []
|
|
|
|
private text: string[] = []
|
|
|
|
private tokens: Token[] = []
|
|
|
|
|
2023-05-08 14:43:50 +00:00
|
|
|
private tags: {[key:string]:any} = {}
|
|
|
|
|
|
|
|
getTag(key: string){
|
|
|
|
return this.tags[key]
|
|
|
|
}
|
|
|
|
|
|
|
|
setTag(key: string, value: any){
|
|
|
|
this.tags[key] = value
|
|
|
|
}
|
|
|
|
|
2023-05-06 21:43:40 +00:00
|
|
|
iterateTokens(){
|
|
|
|
return this.tokens[Symbol.iterator]()
|
|
|
|
}
|
|
|
|
|
|
|
|
iterateText(){
|
|
|
|
return this.text[Symbol.iterator]()
|
|
|
|
}
|
|
|
|
|
|
|
|
iterateChildren(){
|
|
|
|
return this.children[Symbol.iterator]()
|
|
|
|
}
|
2023-05-08 14:43:50 +00:00
|
|
|
|
|
|
|
abstract createGenerator(): T;
|
2023-05-06 21:43:40 +00:00
|
|
|
|
|
|
|
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)
|
2023-05-08 14:43:50 +00:00
|
|
|
this.statement("")
|
2023-05-06 21:43:40 +00:00
|
|
|
}
|
|
|
|
|
2023-05-08 14:43:50 +00:00
|
|
|
child(sub?: T){
|
|
|
|
if(!sub) sub = this.createGenerator()
|
2023-05-06 21:43:40 +00:00
|
|
|
this.tokens.push(Token.GOSUB)
|
|
|
|
this.children.push(sub)
|
2023-05-08 14:43:50 +00:00
|
|
|
return sub
|
2023-05-06 21:43:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2023-05-08 16:05:03 +00:00
|
|
|
public function(name: string, returnType: string, args: FunctionArgument[], isStatic: boolean, func?: (gen: T) => void): T {
|
2023-05-08 14:43:50 +00:00
|
|
|
const sub = this.createGenerator();
|
|
|
|
sub.setTag("_type", "function-body")
|
|
|
|
sub.setTag("_name", name)
|
|
|
|
sub.setTag("_isStatic", isStatic)
|
|
|
|
sub.setTag("_returnType", returnType)
|
2023-05-06 21:43:40 +00:00
|
|
|
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()
|
2023-05-08 14:43:50 +00:00
|
|
|
this.child(sub)
|
|
|
|
this.unindent()
|
|
|
|
this.line("}")
|
|
|
|
this.breakLine()
|
|
|
|
if(func) func(sub)
|
|
|
|
return sub
|
|
|
|
}
|
|
|
|
|
|
|
|
public if(condition: string, funIf?: (gen: T) => void){
|
|
|
|
this.line("if("+condition+") {")
|
|
|
|
this.indent()
|
|
|
|
const sub = this.createGenerator()
|
|
|
|
sub.setTag("_type", "if-body")
|
|
|
|
sub.setTag("_condition", condition)
|
|
|
|
this.child(sub)
|
|
|
|
this.unindent()
|
|
|
|
this.line("}")
|
|
|
|
if(funIf) funIf(sub)
|
|
|
|
return sub
|
|
|
|
}
|
|
|
|
|
|
|
|
public else(funElse?: (gen: T) => void){
|
|
|
|
this.line("else {")
|
|
|
|
this.indent()
|
|
|
|
const sub = this.createGenerator()
|
|
|
|
sub.setTag("_type", "else-body")
|
|
|
|
this.child(sub)
|
2023-05-06 21:43:40 +00:00
|
|
|
this.unindent()
|
|
|
|
this.line("}")
|
2023-05-08 14:43:50 +00:00
|
|
|
if(funElse) funElse(sub)
|
|
|
|
return sub
|
|
|
|
}
|
|
|
|
|
|
|
|
public returnExp(exp: string){
|
|
|
|
this.statement("return "+exp)
|
|
|
|
}
|
|
|
|
|
|
|
|
public include(name: string){
|
|
|
|
this.line("#include <" + name + ">")
|
2023-05-06 21:43:40 +00:00
|
|
|
}
|
2023-05-08 14:43:50 +00:00
|
|
|
|
2023-06-02 06:03:36 +00:00
|
|
|
public for(indexVar: string, lengthVar: string){
|
|
|
|
this.line(`for(int ${indexVar}; i < ${lengthVar}; i++){`)
|
|
|
|
this.indent()
|
|
|
|
const child = this.child()
|
|
|
|
this.unindent()
|
|
|
|
this.line("}")
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
|
2023-05-08 14:43:50 +00:00
|
|
|
public header(guard: string, fun?: (gen: T) => void){
|
|
|
|
this.line("#ifndef " + guard)
|
|
|
|
this.line("#define " + guard)
|
|
|
|
this.breakLine()
|
|
|
|
const sub = this.child()
|
|
|
|
sub.setTag("_type", "header-body")
|
|
|
|
sub.setTag("_guardName", guard)
|
|
|
|
this.line("#endif // " + guard)
|
|
|
|
if(fun) fun(sub)
|
|
|
|
return sub
|
|
|
|
}
|
2023-05-10 21:26:53 +00:00
|
|
|
|
|
|
|
public declareStruct(structName: string, varName: string, values: string[], isStatic: boolean = false){
|
|
|
|
if(isStatic) this.inline("static ")
|
|
|
|
this.statement(`${structName} ${varName} = { ${values.join(', ')} }`)
|
|
|
|
}
|
2023-05-16 06:28:30 +00:00
|
|
|
|
|
|
|
public switch(switchVar: string){
|
|
|
|
this.line(`switch(${switchVar}) {`)
|
|
|
|
this.indent()
|
|
|
|
const body = this.child()
|
|
|
|
this.unindent()
|
|
|
|
this.line("}")
|
|
|
|
return body
|
|
|
|
}
|
|
|
|
|
|
|
|
public case(value: string){
|
|
|
|
this.line(`case ${value}:`)
|
|
|
|
}
|
|
|
|
|
|
|
|
public defaultBreak(){
|
|
|
|
this.line("default:")
|
|
|
|
this.line("{")
|
|
|
|
this.indent()
|
|
|
|
const body = this.child()
|
|
|
|
this.statement("break")
|
|
|
|
this.unindent()
|
|
|
|
this.line("}")
|
|
|
|
return body
|
|
|
|
}
|
|
|
|
|
|
|
|
public caseBreak(value: string){
|
|
|
|
this.case(value)
|
|
|
|
this.line("{")
|
|
|
|
this.indent()
|
|
|
|
const body = this.child()
|
|
|
|
this.statement("break")
|
|
|
|
this.unindent()
|
|
|
|
this.line("}")
|
|
|
|
return body
|
|
|
|
}
|
2023-05-06 21:43:40 +00:00
|
|
|
}
|
|
|
|
|
2023-05-08 14:43:50 +00:00
|
|
|
export class CodeGenerator extends GenericCodeGenerator<CodeGenerator>{
|
|
|
|
createGenerator(): CodeGenerator {
|
|
|
|
return new CodeGenerator()
|
2023-05-06 21:43:40 +00:00
|
|
|
}
|
2023-05-08 14:43:50 +00:00
|
|
|
|
2023-05-06 21:43:40 +00:00
|
|
|
}
|