From 9c1728820cea787b17cb502f4f3094228e295ab9 Mon Sep 17 00:00:00 2001 From: Robert Borghese <28355157+SomeRanDev@users.noreply.github.com> Date: Thu, 22 Feb 2024 01:08:23 -0500 Subject: [PATCH] Begin work on C# AST, increment to v0.2 --- haxelib.json | 2 +- src/cscompiler/CSCompiler.hx | 57 ++++++---- src/cscompiler/ast/CSClass.hx | 26 +++++ src/cscompiler/ast/CSConstant.hx | 28 +++++ src/cscompiler/ast/CSEnum.hx | 18 ++++ src/cscompiler/ast/CSExpr.hx | 101 ++++++++++++++++++ src/cscompiler/ast/CSFunction.hx | 13 +++ src/cscompiler/ast/CSStatement.hx | 38 +++++++ src/cscompiler/ast/CSTopLevel.hx | 25 +++++ src/cscompiler/ast/CSType.hx | 20 ++++ src/cscompiler/ast/CSVar.hx | 30 ++++++ .../{CSBase.hx => CSCompiler_Base.hx} | 2 +- .../{CSClass.hx => CSCompiler_Class.hx} | 10 +- .../{CSEnum.hx => CSCompiler_Enum.hx} | 6 +- .../{CSExpression.hx => CSCompiler_Expr.hx} | 11 +- .../{CSType.hx => CSCompiler_Type.hx} | 11 +- 16 files changed, 366 insertions(+), 32 deletions(-) create mode 100644 src/cscompiler/ast/CSClass.hx create mode 100644 src/cscompiler/ast/CSConstant.hx create mode 100644 src/cscompiler/ast/CSEnum.hx create mode 100644 src/cscompiler/ast/CSExpr.hx create mode 100644 src/cscompiler/ast/CSFunction.hx create mode 100644 src/cscompiler/ast/CSStatement.hx create mode 100644 src/cscompiler/ast/CSTopLevel.hx create mode 100644 src/cscompiler/ast/CSType.hx create mode 100644 src/cscompiler/ast/CSVar.hx rename src/cscompiler/components/{CSBase.hx => CSCompiler_Base.hx} (93%) rename src/cscompiler/components/{CSClass.hx => CSCompiler_Class.hx} (97%) rename src/cscompiler/components/{CSEnum.hx => CSCompiler_Enum.hx} (79%) rename src/cscompiler/components/{CSExpression.hx => CSCompiler_Expr.hx} (98%) rename src/cscompiler/components/{CSType.hx => CSCompiler_Type.hx} (95%) diff --git a/haxelib.json b/haxelib.json index 7727bdc..dcab1e2 100644 --- a/haxelib.json +++ b/haxelib.json @@ -1,6 +1,6 @@ { "name": "csharp", - "version": "0.1.0", + "version": "0.2.0", "description": "A remake of the Haxe/C# target written entirely within Haxe using Reflaxe.", "url": "https://github.com/RobertBorghese/reflaxe.CSharp", "license": "MIT", diff --git a/src/cscompiler/CSCompiler.hx b/src/cscompiler/CSCompiler.hx index b6bcfff..5c2c604 100644 --- a/src/cscompiler/CSCompiler.hx +++ b/src/cscompiler/CSCompiler.hx @@ -11,11 +11,13 @@ import haxe.macro.Type; // --- import reflaxe.BaseCompiler; -import reflaxe.DirectToStringCompiler; +import reflaxe.GenericCompiler; import reflaxe.data.ClassVarData; import reflaxe.data.ClassFuncData; import reflaxe.data.EnumOptionData; import reflaxe.helpers.Context; +import reflaxe.output.DataAndFileInfo; +import reflaxe.output.StringOrBytes; using reflaxe.helpers.SyntaxHelper; using reflaxe.helpers.ModuleTypeHelper; @@ -26,6 +28,10 @@ using reflaxe.helpers.TypeHelper; // --- +import cscompiler.ast.CSClass; +import cscompiler.ast.CSEnum; +import cscompiler.ast.CSStatement; +import cscompiler.ast.CSType; import cscompiler.components.*; import cscompiler.config.Define; @@ -34,7 +40,7 @@ import cscompiler.config.Define; Its "impl" functions are called from Reflaxe. **/ -class CSCompiler extends reflaxe.DirectToStringCompiler { +class CSCompiler extends reflaxe.GenericCompiler { /** The namespace used for top-level module types. **/ @@ -43,22 +49,22 @@ class CSCompiler extends reflaxe.DirectToStringCompiler { /** Handles implementation of `compileClassImpl`. **/ - public var classComp(default, null): CSClass; + public var classComp(default, null): CSCompiler_Class; /** Handles implementation of `compileEnumImpl`. **/ - public var enumComp(default, null): CSEnum; + public var enumComp(default, null): CSCompiler_Enum; /** Handles implementation of `compileExprImpl`. **/ - public var exprComp(default, null): CSExpression; + public var exprComp(default, null): CSCompiler_Expr; /** Handles implementation of `compileType`, `compileModuleType`, and `compileClassName`. **/ - public var typeComp(default, null): CSType; + public var typeComp(default, null): CSCompiler_Type; /** Constructor. @@ -77,10 +83,10 @@ class CSCompiler extends reflaxe.DirectToStringCompiler { // Bypass Haxe null-safety not allowing `this` usage. @:nullSafety(Off) var self = this; - classComp = new CSClass(self); - enumComp = new CSEnum(self); - exprComp = new CSExpression(self); - typeComp = new CSType(self); + classComp = new CSCompiler_Class(self); + enumComp = new CSCompiler_Enum(self); + exprComp = new CSCompiler_Expr(self); + typeComp = new CSCompiler_Type(self); } // --- @@ -106,7 +112,11 @@ class CSCompiler extends reflaxe.DirectToStringCompiler { function setupMainFunction() { final mainExpr = getMainExpr(); if(mainExpr != null) { - final csCode = compileExpressionOrError(mainExpr); + final csExpr = compileExpressionOrError(mainExpr); + + // TODO: Convert `csExpr` to `String` using printer + final csCode = ""; + appendToExtraFile(BootFilename, haxeBootContent(csCode)); } } @@ -177,10 +187,21 @@ namespace Haxe { } /** - Required for adding semicolons at the end of each line. Overridden from Reflaxe. + Generate output. + + TODO. **/ - override function formatExpressionLine(expr: String): String { - return expr + ";"; + public function generateOutputIterator(): Iterator> { + // TODO: Print all classes and enums using these vars from `GenericCompiler`: + // var classes: Array + // var enums: Array + + return { + hasNext: () -> false, + next: () -> { + return new DataAndFileInfo(StringOrBytes.fromString(""), @:nullSafety(Off) null, null, null); + } + }; } // --- @@ -188,14 +209,14 @@ namespace Haxe { /** Generate the C# output given the Haxe class information. **/ - public function compileClassImpl(classType: ClassType, varFields: Array, funcFields: Array): Null { + public function compileClassImpl(classType: ClassType, varFields: Array, funcFields: Array): Null { return classComp.compile(classType, varFields, funcFields); } /** Generate the C# output given the Haxe enum information. **/ - public function compileEnumImpl(enumType: EnumType, options: Array): Null { + public function compileEnumImpl(enumType: EnumType, options: Array): Null { return enumComp.compile(enumType, options); } @@ -206,7 +227,7 @@ namespace Haxe { A `Position` is provided so compilation errors can be reported to it. **/ - public function compileType(type: Type, pos: Position): String { + public function compileType(type: Type, pos: Position): CSType { final result = typeComp.compile(type, pos); if(result == null) { throw "Type could not be generated: " + Std.string(type); @@ -253,7 +274,7 @@ namespace Haxe { /** Generate the C# output given the Haxe typed expression (`TypedExpr`). **/ - public function compileExpressionImpl(expr: TypedExpr, topLevel: Bool): Null { + public function compileExpressionImpl(expr: TypedExpr, topLevel: Bool): Null { return exprComp.compile(expr, topLevel); } diff --git a/src/cscompiler/ast/CSClass.hx b/src/cscompiler/ast/CSClass.hx new file mode 100644 index 0000000..a04ff8f --- /dev/null +++ b/src/cscompiler/ast/CSClass.hx @@ -0,0 +1,26 @@ +package cscompiler.ast; + +#if (macro || cs_runtime) + +/** + Represents a class in C#. + + TODO. +**/ +class CSClass { + public var name(default, null): String; + + var superClass: Null; + var superClassTypeParams: Null>; + + public function new(name: String) { + this.name = name; + } + + public function setSuperClass(superClass: CSClass, typeParams: Array) { + this.superClass = superClass; + superClassTypeParams = typeParams; + } +} + +#end diff --git a/src/cscompiler/ast/CSConstant.hx b/src/cscompiler/ast/CSConstant.hx new file mode 100644 index 0000000..6909199 --- /dev/null +++ b/src/cscompiler/ast/CSConstant.hx @@ -0,0 +1,28 @@ +package cscompiler.ast; + +#if (macro || cs_runtime) + +/** + Represents a constant expression. + + TODO: Give each case a better description. +**/ +enum CSConstant { + CSInt(i: Int, suffix: String); + + /** + TODO: + Should there be a `CSDouble`? + Or some better way to handle differen suffixes? + **/ + CSFloat(s: String, suffix: String); + + CSString(s: String); + CSBool(b: Bool); + + CSNull; + CSThis; + CSSuper; +} + +#end diff --git a/src/cscompiler/ast/CSEnum.hx b/src/cscompiler/ast/CSEnum.hx new file mode 100644 index 0000000..3fb7bc1 --- /dev/null +++ b/src/cscompiler/ast/CSEnum.hx @@ -0,0 +1,18 @@ +package cscompiler.ast; + +#if (macro || cs_runtime) + +/** + Represents an enum in C#. + + TODO. +**/ +class CSEnum { + public var name(default, null): String; + + public function new(name: String) { + this.name = name; + } +} + +#end diff --git a/src/cscompiler/ast/CSExpr.hx b/src/cscompiler/ast/CSExpr.hx new file mode 100644 index 0000000..dc8aac2 --- /dev/null +++ b/src/cscompiler/ast/CSExpr.hx @@ -0,0 +1,101 @@ +package cscompiler.ast; + +#if (macro || cs_runtime) + +import haxe.macro.Expr; +import haxe.macro.Type; + +/** + Represents a C# expression. +**/ +class CSExpr { + public var def(default, null): CSExprDef; + public var haxeExpr(default, null): Null; + + public function new(def: CSExprDef, haxeExpr: Null = null) { + this.def = def; + this.haxeExpr = haxeExpr; + } +} + +/** + TODO: Give each case a better description. +**/ +enum CSExprDef { + /** + A constant. + **/ + CSConst(constant: CSConstant); + + /** + Reference to a local variable `varData`. + **/ + CSLocalVar(varData: CSVar); + + /** + Array access `baseExpr[indexExpr]`. + **/ + CSArray(baseExpr: CSExpr, indexExpr: CSExpr); + + /** + Binary operator `leftExpr op rightExpr`. + **/ + CSBinop(op: Binop, leftExpr: CSExpr, rightExpr: CSExpr); + + /** + Field access on `e` of name `fieldName`. + + TODO: + Replace `fieldName: String` with a custom `FieldAccess` type (`fieldAccess: CSFieldAccess`)? + **/ + CSField(e: CSExpr, fieldName: String); + + /** + Reference to a module type `m`. + + TODO: + This is assuming static-access is only possible from a class in C#? + Maybe this should be replaced with a `CSStaticVar(varData: CSVar, cls: CSClass)`. + **/ + CSClassExpr(cls: CSClass); + + /** + Parentheses `(e)`. + **/ + CSParenthesis(e: CSExpr); + + /** + An array declaration `{ expressions }`. + **/ + CSArrayDecl(expressions: Array); + + /** + A call `baseExpr(arguments)`. + **/ + CSCall(baseExpr: CSExpr, typeParams: Array, arguments: Array); + + /** + A constructor call `new cls(arguments)`. + **/ + CSNew(cls: CSClass, typeParams: Array, arguments: Array); + + /** + An unary operator `op` on `baseExpr`. + + TODO: + Is postfix necessary? + **/ + CSUnop(op: Unop, postFix: Bool, baseExpr: CSExpr); + + /** + A function declaration. + **/ + CSFunctionExpr(tfunc: CSFunction); + + /** + An unknown identifier. + **/ + CSIdent(s: String); +} + +#end diff --git a/src/cscompiler/ast/CSFunction.hx b/src/cscompiler/ast/CSFunction.hx new file mode 100644 index 0000000..372c2ca --- /dev/null +++ b/src/cscompiler/ast/CSFunction.hx @@ -0,0 +1,13 @@ +package cscompiler.ast; + +#if (macro || cs_runtime) + +/** + Represents a function in C#. + + TODO. +**/ +class CSFunction { +} + +#end diff --git a/src/cscompiler/ast/CSStatement.hx b/src/cscompiler/ast/CSStatement.hx new file mode 100644 index 0000000..a222e0e --- /dev/null +++ b/src/cscompiler/ast/CSStatement.hx @@ -0,0 +1,38 @@ +package cscompiler.ast; + +#if (macro || cs_runtime) + +import haxe.macro.Type; + +/** + Represents a C# statement. +**/ +class CSStatement { + public var def(default, null): CSStatementDef; + public var haxeExpr(default, null): Null; + + public function new(def: CSStatementDef, haxeExpr: Null = null) { + this.def = def; + this.haxeExpr = haxeExpr; + } +} + +enum CSStatementDef { + CSExprStatement(expression: CSExpr); + + CSBlock(statements: Array); + + /** + TODO: else if + **/ + CSIf(condition: CSExpr, ifContent: Array, elseContent: Null>); + + CSWhile(condition: CSExpr, content: Array, normalWhile: Bool); + + /** + A variable declaration `var varData` or `var varData = expr`. + **/ + CSVar(varData: CSVar, expr: Null); +} + +#end diff --git a/src/cscompiler/ast/CSTopLevel.hx b/src/cscompiler/ast/CSTopLevel.hx new file mode 100644 index 0000000..7a39b7c --- /dev/null +++ b/src/cscompiler/ast/CSTopLevel.hx @@ -0,0 +1,25 @@ +package cscompiler.ast; + +#if (macro || cs_runtime) + +/** + Represents a top-level entry in a C# source file. + + TODO: + Should this be an enum only? + If it's a class, the C# namespace path can be stored separately instead of repeating in every enum case. +**/ +class CSTopLevel { + public var def(default, null): CSTopLevelDef; + + public function new(def: CSTopLevelDef) { + this.def = def; + } +} + +enum CSTopLevelDef { + CSTopLevelClass(c: CSClass); + CSTopLevelEnum(e: CSEnum); +} + +#end diff --git a/src/cscompiler/ast/CSType.hx b/src/cscompiler/ast/CSType.hx new file mode 100644 index 0000000..08e5b89 --- /dev/null +++ b/src/cscompiler/ast/CSType.hx @@ -0,0 +1,20 @@ +package cscompiler.ast; + +#if (macro || cs_runtime) + +/** + Represents a C# type. + + TODO: + This might be the best way to do this? + + A `CSType` should be generated from a Haxe `Type` right? + But a `CSClass` requires parsing an entire `ClassType` to be created (including fields); + do we want to parse extern classes that we don't need to generate? +**/ +enum CSType { + IsCSClass(c: CSClass); + IsCSEnum(e: CSEnum); +} + +#end diff --git a/src/cscompiler/ast/CSVar.hx b/src/cscompiler/ast/CSVar.hx new file mode 100644 index 0000000..36a7716 --- /dev/null +++ b/src/cscompiler/ast/CSVar.hx @@ -0,0 +1,30 @@ +package cscompiler.ast; + +#if (macro || cs_runtime) + +/** + Represents a variable in C#. +**/ +class CSVar { + /** + TODO: + Is an `id` system necessary? + Haxe equality should work since we're not regenerating + objects when obtaining data from OCaml. + **/ + // var id: Int; + + var name: String; + var type: CSType; + + // static var nextId = 0; + + public function new(name: String, type: CSType) { + // id = nextId++; + + this.name = name; + this.type = type; + } +} + +#end diff --git a/src/cscompiler/components/CSBase.hx b/src/cscompiler/components/CSCompiler_Base.hx similarity index 93% rename from src/cscompiler/components/CSBase.hx rename to src/cscompiler/components/CSCompiler_Base.hx index c43bc8a..5b8bb30 100644 --- a/src/cscompiler/components/CSBase.hx +++ b/src/cscompiler/components/CSCompiler_Base.hx @@ -10,7 +10,7 @@ import cscompiler.CSCompiler; Simply stores reference to `CSCompiler` so components can interact with each other. **/ -class CSBase { +class CSCompiler_Base { var compiler: CSCompiler; public function new(compiler: CSCompiler) { diff --git a/src/cscompiler/components/CSClass.hx b/src/cscompiler/components/CSCompiler_Class.hx similarity index 97% rename from src/cscompiler/components/CSClass.hx rename to src/cscompiler/components/CSCompiler_Class.hx index af11cfb..2e8043a 100644 --- a/src/cscompiler/components/CSClass.hx +++ b/src/cscompiler/components/CSCompiler_Class.hx @@ -5,6 +5,7 @@ package cscompiler.components; import haxe.macro.Type; import haxe.display.Display.MetadataTarget; +import cscompiler.ast.CSClass; import reflaxe.BaseCompiler; import reflaxe.data.ClassVarData; import reflaxe.data.ClassFuncData; @@ -17,7 +18,7 @@ using reflaxe.helpers.SyntaxHelper; The component responsible for compiling Haxe classes into C#. **/ -class CSClass extends CSBase { +class CSCompiler_Class extends CSCompiler_Base { /** The list of variables compiled into C# accumulated while compiling the class. **/ @@ -53,7 +54,10 @@ class CSClass extends CSBase { /** Implementation of `CSCompiler.compileClassImpl`. **/ - public function compile(classType: ClassType, varFields: Array, funcFields: Array): Null { + public function compile(classType: ClassType, varFields: Array, funcFields: Array): Null { + // Temp fix for CSType return + return null; + // TODO: Set the output folder for the file this will be generated for // compiler.setOutputFileDir("src"); @@ -89,7 +93,7 @@ class CSClass extends CSBase { } // Let's put everything together to make the C# class! - return { + /* return */ { final content = []; if(variables.length > 0) { diff --git a/src/cscompiler/components/CSEnum.hx b/src/cscompiler/components/CSCompiler_Enum.hx similarity index 79% rename from src/cscompiler/components/CSEnum.hx rename to src/cscompiler/components/CSCompiler_Enum.hx index 80a15c9..4e1c179 100644 --- a/src/cscompiler/components/CSEnum.hx +++ b/src/cscompiler/components/CSCompiler_Enum.hx @@ -8,17 +8,19 @@ import reflaxe.BaseCompiler; import reflaxe.data.EnumOptionData; import reflaxe.helpers.OperatorHelper; +import cscompiler.ast.CSEnum; + /** The component responsible for compiling Haxe enums into C#. **/ -class CSEnum extends CSBase { +class CSCompiler_Enum extends CSCompiler_Base { /** Implementation of `CSCompiler.compileEnumImpl`. TODO. **/ - public function compile(enumType: EnumType, options: Array): Null { + public function compile(enumType: EnumType, options: Array): Null { return null; } } diff --git a/src/cscompiler/components/CSExpression.hx b/src/cscompiler/components/CSCompiler_Expr.hx similarity index 98% rename from src/cscompiler/components/CSExpression.hx rename to src/cscompiler/components/CSCompiler_Expr.hx index f17078e..145254b 100644 --- a/src/cscompiler/components/CSExpression.hx +++ b/src/cscompiler/components/CSCompiler_Expr.hx @@ -12,11 +12,13 @@ using reflaxe.helpers.NameMetaHelper; using reflaxe.helpers.SyntaxHelper; using reflaxe.helpers.TypedExprHelper; +import cscompiler.ast.CSStatement; + /** The component responsible for compiling Haxe typed expressions into C#. **/ -class CSExpression extends CSBase { +class CSCompiler_Expr extends CSCompiler_Base { /** Calls `compiler.compileExpressionOrError`. **/ @@ -27,7 +29,10 @@ class CSExpression extends CSBase { /** Implementation of `CSCompiler.compileExpressionImpl`. **/ - public function compile(expr: TypedExpr, topLevel: Bool): Null { + public function compile(expr: TypedExpr, topLevel: Bool): Null { + // Temp fix for CSStatement return + return null; + var result = ""; switch(expr.expr) { case TConst(constant): { @@ -203,7 +208,7 @@ class CSExpression extends CSBase { // generate the C# code to extract its index. } } - return result; + /* return */ result; } /** diff --git a/src/cscompiler/components/CSType.hx b/src/cscompiler/components/CSCompiler_Type.hx similarity index 95% rename from src/cscompiler/components/CSType.hx rename to src/cscompiler/components/CSCompiler_Type.hx index 478423a..8032dc3 100644 --- a/src/cscompiler/components/CSType.hx +++ b/src/cscompiler/components/CSCompiler_Type.hx @@ -6,10 +6,10 @@ import reflaxe.helpers.Context; // same as haxe.macro.Context import haxe.macro.Expr; import haxe.macro.Type; +import cscompiler.ast.CSType; import cscompiler.config.Define; import cscompiler.config.NamespaceStyle; import cscompiler.config.NamespaceStyle.fromString as NamespaceStyle_fromString; - import cscompiler.helpers.StringTools; using reflaxe.helpers.ModuleTypeHelper; @@ -19,14 +19,17 @@ using reflaxe.helpers.NameMetaHelper; The component responsible for compiling Haxe types into C#. **/ -class CSType extends CSBase { +class CSCompiler_Type extends CSCompiler_Base { /** Generates the C# type code given the Haxe `haxe.macro.Type`. TODO. **/ - public function compile(type: Type, pos: Position): Null { - return switch(type) { + public function compile(type: Type, pos: Position): Null { + // Temp fix for CSType return + return null; + + /* return */ switch(type) { case TMono(refType): { final maybeType = refType.get(); if(maybeType != null) {