diff --git a/README.md b/README.md index c81a705..0dfcf5c 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,12 @@ This is a repository for CPSC 410: Advanced Software Engineering. The TS-DSL sho * yarn (>= v1.17.3) * tsc (>= 3.6.3) +## Demo + +1. Navigate to `ts-dsl/ui` +2. Run `tsc TypeScript.ts` +3. Run `node TypeScript.js simpleProgramClass.txt` + ## Working with ts-dsl * To build: `yarn build` diff --git a/src/ast/ClassDecl.ts b/src/ast/ClassDecl.ts index 12346fd..94e88a8 100644 --- a/src/ast/ClassDecl.ts +++ b/src/ast/ClassDecl.ts @@ -5,6 +5,10 @@ import {ImplementsDecl} from "./ImplementsDecl"; import CommentDecl from "./CommentDecl"; import FuncDecl from "./FuncDecl"; import {Tokenizer} from "../util/Tokenizer"; +import StaticDecl from "./StaticDecl"; +import AsyncDecl from "./AsyncDecl"; +import {VarList} from "./VarList"; +import Func = Mocha.Func; /** * Represents a Class a TypeScript project may have. @@ -48,6 +52,17 @@ export class ClassDecl extends Content { while(context.getCurrentLineTabLevel() > indentLevel && context.checkToken("fields")) { let field: FieldDecl = new FieldDecl(); field.parse(context); + if (field.generateGetter) { + field.fields.nameTypeMap.forEach((type: string, name: string) => { + this.functions.push(this.createGetter(name, type)); + }); + + } + if (field.generateSetter) { + field.fields.nameTypeMap.forEach((type: string, name: string) => { + this.functions.push(this.createSetter(name, type)); + }); + } this.fields.push(field); } @@ -79,4 +94,30 @@ export class ClassDecl extends Content { public getAbsolutePath(): string { return `${this.parentPath}/${this.className}.ts`; } + + private createGetter(name: string, type: string): FuncDecl { + let funcGetter: FuncDecl = new FuncDecl(); + funcGetter.name = "get" + name.charAt(0).toUpperCase() + name.slice(1); + funcGetter.returnDecl.returnType = type; + funcGetter.modifier = "public"; + funcGetter.maybeStatic = new StaticDecl(); // not static + funcGetter.maybeAsync = new AsyncDecl(); // not async + funcGetter.params = new VarList(); + funcGetter.comments = new CommentDecl(); + return funcGetter; + } + + private createSetter(name: string, type: string): FuncDecl { + let funcSetter: FuncDecl = new FuncDecl(); + // setName + funcSetter.name = "set" + name.charAt(0).toUpperCase() + name.slice(1); + funcSetter.returnDecl.returnType = "void"; + funcSetter.modifier = "public"; + funcSetter.maybeStatic = new StaticDecl(); // not static + funcSetter.maybeAsync = new AsyncDecl(); // not async + funcSetter.params = new VarList(); + funcSetter.params.addPair(name, type); + funcSetter.comments = new CommentDecl(); + return funcSetter; + } } diff --git a/src/ast/FuncDecl.ts b/src/ast/FuncDecl.ts index 0eb9ee3..898e8c9 100644 --- a/src/ast/FuncDecl.ts +++ b/src/ast/FuncDecl.ts @@ -22,6 +22,7 @@ export default class FuncDecl extends AstNode { name: string; params: VarList; comments: CommentDecl; + body: string = null; // only used in getter/setter returnDecl: ReturnDecl = new ReturnDecl(); public parse(context: Tokenizer): any { diff --git a/src/ast/InterfaceDecl.ts b/src/ast/InterfaceDecl.ts index 3e91979..0ef3190 100644 --- a/src/ast/InterfaceDecl.ts +++ b/src/ast/InterfaceDecl.ts @@ -4,6 +4,9 @@ import {FieldDecl} from "./FieldDecl"; import CommentDecl from "./CommentDecl"; import FuncDecl from "./FuncDecl"; import {Tokenizer} from "../util/Tokenizer"; +import StaticDecl from "./StaticDecl"; +import AsyncDecl from "./AsyncDecl"; +import {VarList} from "./VarList"; /** * Represents an Interface a TypeScript project may have. @@ -36,6 +39,19 @@ export class InterfaceDecl extends Content { this.fieldDecl = new FieldDecl(); this.fieldDecl.isInterfaceField = true; this.fieldDecl.parse(context); + + // handle getter/setter functions + if (this.fieldDecl.generateGetter) { + this.fieldDecl.fields.nameTypeMap.forEach((name: string, type: string) => { + this.functions.push(this.createGetter(name, type)); + }); + + } + if (this.fieldDecl.generateSetter) { + this.fieldDecl.fields.nameTypeMap.forEach((name: string, type: string) => { + this.functions.push(this.createSetter(name, type)); + }); + } } while(context.getCurrentLineTabLevel() > indentLevel && context.checkToken("function")) { @@ -72,4 +88,33 @@ export class InterfaceDecl extends Content { public getAbsolutePath(): string { return this.parentPath + "/" + this.interfaceName + ".ts"; } + + private createGetter(name: string, type: string): FuncDecl { + let funcGetter: FuncDecl = new FuncDecl(); + // getName + funcGetter.name = "get" + name.charAt(0).toUpperCase() + name.slice(1); + funcGetter.returnDecl.returnType = type; + funcGetter.modifier = "public"; + funcGetter.maybeStatic = new StaticDecl(); // not static + funcGetter.maybeAsync = new AsyncDecl(); // not async + // no params + funcGetter.params = new VarList(); + funcGetter.comments = new CommentDecl(); + return funcGetter; + } + + private createSetter(name: string, type: string): FuncDecl { + let funcSetter: FuncDecl = new FuncDecl(); + // setName + funcSetter.name = "set" + name.charAt(0).toUpperCase() + name.slice(1); + funcSetter.returnDecl.returnType = "void"; + funcSetter.modifier = "public"; + funcSetter.maybeStatic = new StaticDecl(); // not static + funcSetter.maybeAsync = new AsyncDecl(); // not async + // one param + funcSetter.params = new VarList(); + funcSetter.params.addPair(name, type); + funcSetter.comments = new CommentDecl(); + return funcSetter; + } } diff --git a/src/codegen/TypeScriptEngine.ts b/src/codegen/TypeScriptEngine.ts index f64a89b..0509ff5 100644 --- a/src/codegen/TypeScriptEngine.ts +++ b/src/codegen/TypeScriptEngine.ts @@ -7,7 +7,9 @@ import {FunctionDeclaration, ClassDeclaration, InterfaceDeclaration, ClassElement, - TypeElement} from "typescript"; + TypeElement, + Block, + Statement} from "typescript"; import {VarList} from "../ast/VarList"; import {TypeTable} from "../ast/symbols/TypeTable"; import FuncDecl from "../ast/FuncDecl"; @@ -122,12 +124,34 @@ export default class TypeScriptEngine { undefined, tsParams, tsReturnType, - ts.createBlock(retStatement, false) + this.makeBody(funcDecl, retStatement) ); this.addLeadingComment(methodSig, funcDecl.comments); return methodSig; } + private makeBody(funcDecl: FuncDecl, defaultRetStatement: Statement[]): Block { + const funcName: string = funcDecl.name; + if (funcName.includes("get")) { + const nameOfField: string = funcName.split("get")[1]; + const fieldAsLowercase: string = nameOfField.charAt(0).toLowerCase() + nameOfField.slice(1); + const statements: Statement[] = [ts.createReturn(ts.createIdentifier(`this.${fieldAsLowercase}`))]; + return ts.createBlock(statements, false); + } else if (funcName.includes("set")) { + const nameOfField: string = funcName.split("set")[1]; + const fieldAsLowercase: string = nameOfField.charAt(0).toLowerCase() + nameOfField.slice(1); + const setterBody: Statement[] = [ts.createExpressionStatement(ts.createBinary( + ts.createIdentifier(`this.${fieldAsLowercase}`), + ts.SyntaxKind.EqualsToken, + ts.createIdentifier(fieldAsLowercase)) + )]; + return ts.createBlock(setterBody, false); + } else { + return ts.createBlock(defaultRetStatement, false); + } + + } + private makeMethodModifiers(funcDecl: FuncDecl): Modifier[] { if (funcDecl.modifier) { return this.makeModifierNodes([funcDecl.modifier]); diff --git a/test/testFiles/simpleProgramClass.txt b/test/testFiles/simpleProgramClass.txt index c27f613..f3c9c13 100644 --- a/test/testFiles/simpleProgramClass.txt +++ b/test/testFiles/simpleProgramClass.txt @@ -7,8 +7,6 @@ project TestProject generate setters fields public [ number date ] generate getters - function private getDate + function private makeDate params [ string id, string content, string kind ] returns string - - diff --git a/ui/simpleProgramClass.txt b/ui/simpleProgramClass.txt new file mode 100644 index 0000000..8b95a61 --- /dev/null +++ b/ui/simpleProgramClass.txt @@ -0,0 +1,13 @@ +project TestProject + modules [ ] + dir src/model + class Time + fields private [ number dayOfWeek, number hour, number minute ] + generate getters + generate setters + fields public [ number date ] + generate getters + function private formatDate + params [ string id, string content, string kind ] + returns string +