diff --git a/.gitignore b/.gitignore index 6dce30af..a58baf71 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ obj/ *.dll *.exe *.runtimeconfig.json +*.received.txt *.user diff --git a/Cesium.Ast/Declarations.cs b/Cesium.Ast/Declarations.cs index 9d685ac1..abc3fb93 100644 --- a/Cesium.Ast/Declarations.cs +++ b/Cesium.Ast/Declarations.cs @@ -23,6 +23,8 @@ public record StructOrUnionSpecifier( string? Identifier, ImmutableArray StructDeclarations) : ITypeSpecifier; +public record NamedTypeSpecifier(string TypeDefName) : ITypeSpecifier; + // 6.7.2.1 Structure and union specifiers public enum ComplexTypeKind { @@ -61,8 +63,13 @@ public interface IDirectDeclarator { IDirectDeclarator? Base { get; } } -public record IdentifierDirectDeclarator(string Identifier) : IDirectDeclarator +public record IdentifierDirectDeclarator(string? Identifier) : IDirectDeclarator { + // HACK: This property is only mutable from CParser.TypeDefNameIdentifierHack. + // This requirement is caused by an issue https://github.com/LanguageDev/Yoakke/issues/138 + // TODO: Eventually, we should get rid of that. + public string? Identifier { get; set; } = Identifier; + public IDirectDeclarator? Base => null; } public record ArrayDirectDeclarator( diff --git a/Cesium.CodeGen.Tests/CodeGenMethodTests.UninitializedVariable.verified.txt b/Cesium.CodeGen.Tests/CodeGenMethodTests.UninitializedVariable.verified.txt new file mode 100644 index 00000000..d615ed5e --- /dev/null +++ b/Cesium.CodeGen.Tests/CodeGenMethodTests.UninitializedVariable.verified.txt @@ -0,0 +1,5 @@ +System.Int32 ::main() + Locals: + System.Int32 V_0 + IL_0000: ldc.i4 0 + IL_0005: ret diff --git a/Cesium.CodeGen.Tests/CodeGenMethodTests.cs b/Cesium.CodeGen.Tests/CodeGenMethodTests.cs index 5e90dad0..b56cab35 100644 --- a/Cesium.CodeGen.Tests/CodeGenMethodTests.cs +++ b/Cesium.CodeGen.Tests/CodeGenMethodTests.cs @@ -80,6 +80,8 @@ public void VarArgMainDoesNotCompile2() => DoesNotCompile( [Fact] public Task MultiDeclaration() => DoTest("int main() { int x = 0, y = 2 + 2; }"); + [Fact] public Task UninitializedVariable() => DoTest("int main() { int x; return 0; }"); + [Fact] public Task Arithmetic() => DoTest(@"int main(void) { diff --git a/Cesium.CodeGen.Tests/CodeGenTypeTests.cs b/Cesium.CodeGen.Tests/CodeGenTypeTests.cs index d4fe69f0..084fe65a 100644 --- a/Cesium.CodeGen.Tests/CodeGenTypeTests.cs +++ b/Cesium.CodeGen.Tests/CodeGenTypeTests.cs @@ -54,4 +54,8 @@ void foo(void) [Fact] public Task SingleFieldStructDefinition() => DoTest("typedef struct { int x; } foo;"); + + [Fact] + public Task TypeDefStructUsage() => DoTest(@"typedef struct { int x; } foo; +int main(void) { foo x; return 0; }"); } diff --git a/Cesium.Parser.Tests/ParserTests/FullParserTests.cs b/Cesium.Parser.Tests/ParserTests/FullParserTests.cs index 84ffb114..bba089cb 100644 --- a/Cesium.Parser.Tests/ParserTests/FullParserTests.cs +++ b/Cesium.Parser.Tests/ParserTests/FullParserTests.cs @@ -124,4 +124,8 @@ public Task PrefixIncrementTest() => DoTest(@"int main() [Fact] public Task DoubleTypeDef() => DoTest(@"typedef struct { int x; } foo, bar;"); + + [Fact] + public Task TypeDefStructUsage() => DoTest(@"typedef struct { int x; } foo; +int main(void) { foo x; return 0; }"); } diff --git a/Cesium.Parser/CParser.cs b/Cesium.Parser/CParser.cs index a95fbc51..c33223b5 100644 --- a/Cesium.Parser/CParser.cs +++ b/Cesium.Parser/CParser.cs @@ -177,7 +177,19 @@ private static Expression MakeAssignmentExpression( private static Declaration MakeDeclaration( DeclarationSpecifiers specifiers, InitDeclaratorList? initDeclarators, - IToken _) => new(specifiers, initDeclarators); + IToken _) + { + var firstInitDeclarator = initDeclarators?.FirstOrDefault(); + if (firstInitDeclarator != null) + { + var firstDeclarator = firstInitDeclarator.Declarator; + (specifiers, firstDeclarator) = TypeDefNameIdentifierHack(specifiers, firstDeclarator); + firstInitDeclarator = firstInitDeclarator with { Declarator = firstDeclarator }; + initDeclarators = initDeclarators!.Value.RemoveAt(0).Insert(0, firstInitDeclarator); + } + + return new(specifiers, initDeclarators); + } [Rule("declaration_specifiers: storage_class_specifier declaration_specifiers?")] [Rule("declaration_specifiers: type_specifier declaration_specifiers?")] @@ -238,7 +250,10 @@ private static ITypeSpecifier MakeComplexTypeSpecifier(StructOrUnionSpecifier st structOrUnionSpecifier; // TODO: [Rule("type_specifier: enum_specifier")] - // TODO: [Rule("type_specifier: typedef_name")] + + [Rule("type_specifier: typedef_name")] + private static ITypeSpecifier MakeNamedTypeSpecifier(IToken typeDefName) => + new NamedTypeSpecifier(typeDefName.Text); // 6.7.2.1 Structure and union specifiers @@ -315,9 +330,9 @@ private static StructDeclaratorList MakeStructDeclaratorList( private static Declarator MakeDeclarator(Pointer? pointer, IDirectDeclarator directDeclarator) => new(pointer, directDeclarator); - [Rule("direct_declarator: Identifier")] - private static IDirectDeclarator MakeDirectDeclarator(ICToken identifier) => - new IdentifierDirectDeclarator(identifier.Text); + [Rule("direct_declarator: Identifier?")] + private static IDirectDeclarator MakeDirectDeclarator(ICToken? identifier) => + new IdentifierDirectDeclarator(identifier?.Text); // TODO: direct_declarator: ( declarator ) @@ -378,7 +393,11 @@ private static ParameterList MakeParameterList(ParameterList prev, ICToken _, Pa [Rule("parameter_declaration: declaration_specifiers declarator")] private static ParameterDeclaration MakeParameterTypeList( DeclarationSpecifiers specifiers, - Declarator declarator) => new(specifiers, declarator); + Declarator declarator) + { + (specifiers, declarator) = TypeDefNameIdentifierHack(specifiers, declarator); + return new(specifiers, declarator); + } [Rule("parameter_declaration: declaration_specifiers abstract_declarator?")] private static ParameterDeclaration MakeParameterTypeList( @@ -442,7 +461,9 @@ private static IDirectAbstractDeclarator MakeDirectAbstractDeclarator( // direct-abstract-declarator? [ * ] // direct-abstract-declarator? ( parameter-type-list? ) - // TODO: 6.7.8 Type definitions + // 6.7.8 Type definitions + [Rule("typedef_name: Identifier")] + private static IToken MakeTypeDefName(IToken identifier) => identifier; // 6.7.9 Initialization @@ -533,7 +554,11 @@ private static FunctionDefinition MakeFunctionDefinition( DeclarationSpecifiers specifiers, Declarator declarator, ImmutableArray? declarationList, - CompoundStatement statement) => new(specifiers, declarator, declarationList, statement); + CompoundStatement statement) + { + (specifiers, declarator) = TypeDefNameIdentifierHack(specifiers, declarator); + return new(specifiers, declarator, declarationList, statement); + } [Rule("declaration_list: declaration")] private static ImmutableArray MakeDeclarationList(Declaration declaration) => @@ -608,4 +633,37 @@ private static ImmutableArray MakeDeclarationList( // TODO: 6.10.7 Null directive // TODO: 6.10.8 Predefined macro names // TODO: 6.10.9 Pragma operator + + // HACK: The existence of this method is caused caused by an issue https://github.com/LanguageDev/Yoakke/issues/138 + // As no simple workaround exist, we have to do ugly manipulations in parser and AST to support this. + // TODO: Eventually, I hope we'll get rid of that. + private static (DeclarationSpecifiers, Declarator) TypeDefNameIdentifierHack( + DeclarationSpecifiers specifiers, + Declarator declarator) + { + var directDeclarator = declarator.DirectDeclarator; + IdentifierDirectDeclarator? identifierDeclarator = null; + while (directDeclarator != null && identifierDeclarator == null) + { + identifierDeclarator = directDeclarator as IdentifierDirectDeclarator; + directDeclarator = directDeclarator.Base; + } + + if (identifierDeclarator is { Identifier: null }) + { + var lastSpecifier = specifiers.LastOrDefault(); + if (lastSpecifier is NamedTypeSpecifier { TypeDefName: var tn }) + { + specifiers = specifiers.RemoveAt(specifiers.Length - 1); + identifierDeclarator.Identifier = tn; + } + } + + if (identifierDeclarator is { Identifier: null }) + throw new NotSupportedException( + "THIS IS A BUG! It is caused by a hack in parsing for the sake of `typedef_name`." + + $" Please report to the Cesium maintainers: [{string.Join(",", specifiers)}] {declarator}."); + + return (specifiers, declarator); + } }