diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7fee41af1..6fdb432fd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,9 @@ name: CI Build on: push: - branches: [ master ] + branches: [ astPrinterRelease ] pull_request: - branches: [ master ] + branches: [ astPrinterRelease ] jobs: build: diff --git a/.gitignore b/.gitignore index 14c4f38e2..495762897 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ CMakeLists.txt.user CMakeUserPresets.json .ccls _version.py +*.sw[k-p] +.nfs* diff --git a/CMakePresets.json b/CMakePresets.json index 291081a36..b2d5060a3 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,256 +1,258 @@ { - "version": 3, - "configurePresets": [ - { - "name": "windows-flags", - "hidden": true, - "cacheVariables": { - "SLANG_WARN_FLAGS": "/W4;/WX;/external:anglebrackets;/external:W0" - } - }, - { - "name": "windows-base", - "hidden": true, - "inherits": "windows-flags", - "generator": "Ninja", - "binaryDir": "${sourceDir}/build/${presetName}", - "installDir": "${sourceDir}/install/${presetName}", - "cacheVariables": { - "CMAKE_CXX_COMPILER": "cl.exe", - "CMAKE_CXX_FLAGS": "/nologo /MP /volatile:iso /Zc:inline /Zc:preprocessor /EHsc /Zc:__cplusplus /Zc:externConstexpr /Zc:throwingNew" + "version": 3, + "configurePresets": [ + { + "name": "windows-flags", + "hidden": true, + "cacheVariables": { + "SLANG_WARN_FLAGS": "/W4;/WX;/external:anglebrackets;/external:W0" + } }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" - } - }, - { - "name": "windows-debug", - "hidden": true, - "inherits": "windows-base", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_CXX_FLAGS_DEBUG": "/Zi /Ob0 /Od /RTC1", - "CMAKE_EXE_LINKER_FLAGS_DEBUG": "/nologo /DEBUG:FASTLINK", - "CMAKE_SHARED_LINKER_FLAGS_DEBUG": "/nologo /DEBUG:FASTLINK", - "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDebugDLL" - } - }, - { - "name": "windows-release", - "hidden": true, - "inherits": "windows-base", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "CMAKE_CXX_FLAGS_RELEASE": "/O2 /Ob3 /GS- /DNDEBUG", - "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL" - } - }, - { - "name": "win64-debug", - "displayName": "Win64 Debug", - "inherits": "windows-debug", - "architecture": { - "value": "x64", - "strategy": "external" - } - }, - { - "name": "win64-release", - "displayName": "Win64 Release", - "inherits": "windows-release", - "architecture": { - "value": "x64", - "strategy": "external" - } - }, - { - "name": "win64-debug-shared", - "displayName": "Win64 Debug Shared", - "inherits": [ - "win64-debug" - ], - "cacheVariables": { - "BUILD_SHARED_LIBS": "ON", - "SLANG_USE_MIMALLOC": "OFF" - } - }, - { - "name": "win64-debug-noexcept", - "displayName": "Win64 Debug No Exceptions", - "inherits": [ - "win64-debug" - ], - "cacheVariables": { - "CMAKE_CXX_FLAGS": "/nologo /MP /GR- /volatile:iso /Zc:inline /Zc:preprocessor /Zc:__cplusplus /Zc:externConstexpr /Zc:throwingNew /D_HAS_EXCEPTIONS=0" - } - }, - { - "name": "gcc-flags", - "hidden": true, - "cacheVariables": { - "SLANG_WARN_FLAGS": "-Wall;-Wextra;-Werror;-Wunused-value;-Wformat-security;-Wimplicit-fallthrough=5;-Walloc-zero;-Wlogical-op;-Wlogical-not-parentheses;-Wvla;-Wduplicated-cond;-Wtype-limits;-Wno-maybe-uninitialized;-Wno-alloc-size-larger-than;-Wno-dangling-reference;-Wno-stringop-overflow" - } - }, - { - "name": "clang-flags", - "hidden": true, - "cacheVariables": { - "SLANG_WARN_FLAGS": "-Wall;-Wextra;-Werror;-Warray-bounds-pointer-arithmetic;-Wassign-enum;-Wbad-function-cast;-Wcast-qual;-Wchar-subscripts;-Wcomma;-Wconditional-uninitialized;-Wconversion;-Wdelete-non-virtual-dtor;-Wdeprecated;-Wduplicate-enum;-Wduplicate-method-arg;-Wembedded-directive;-Wfor-loop-analysis;-Wformat-pedantic;-Widiomatic-parentheses;-Wimplicit-fallthrough;-Wpedantic;-Wrange-loop-analysis;-Wredundant-parens;-Wreserved-id-macro;-Wshadow;-Wundefined-reinterpret-cast;-Wunreachable-code-aggressive;-Wno-missing-braces;-Wno-deprecated-literal-operator", - "CMAKE_CXX_FLAGS": "-Wno-deprecated-declarations" - } - }, - { - "name": "linux-base", - "hidden": true, - "generator": "Unix Makefiles", - "binaryDir": "${sourceDir}/build/${presetName}", - "installDir": "${sourceDir}/install/${presetName}", - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Linux" - } - }, - { - "name": "clang-debug", - "displayName": "Clang Debug", - "inherits": [ - "linux-base", - "clang-flags" - ], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_CXX_COMPILER": "clang++-18" - } - }, - { - "name": "clang-release", - "displayName": "Clang Release", - "inherits": [ - "linux-base", - "clang-flags" - ], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "CMAKE_CXX_COMPILER": "clang++-18" - } - }, - { - "name": "clang-sanitize", - "displayName": "Clang Sanitize", - "description": "clang analysis with sanitizers enabled", - "inherits": [ - "linux-base", - "clang-flags" - ], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "RelWithDebInfo", - "CMAKE_CXX_COMPILER": "clang++-18", - "CMAKE_CXX_FLAGS": "-DSLANG_ASSERT_ENABLED -fsanitize=undefined,address -fno-sanitize-recover=undefined -fno-omit-frame-pointer -fno-common -Wno-deprecated-declarations", - "CMAKE_EXE_LINKER_FLAGS": "-fsanitize=undefined,address -fno-sanitize-recover=undefined", - "CMAKE_SHARED_LINKER_FLAGS": "-fsanitize=undefined,address -fno-sanitize-recover=undefined", - "SLANG_CLANG_TIDY": "clang-tidy-18" - } - }, - { - "name": "gcc-debug-shared", - "displayName": "GCC Debug Shared", - "inherits": [ - "linux-base", - "gcc-flags" - ], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_CXX_COMPILER": "g++-13", - "BUILD_SHARED_LIBS": "ON" - } - }, - { - "name": "gcc-debug-noexcept", - "displayName": "GCC Debug No Exceptions", - "inherits": [ - "linux-base", - "gcc-flags" - ], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_CXX_FLAGS": "-fno-exceptions -fno-rtti", - "CMAKE_CXX_COMPILER": "g++-13" - } - }, - { - "name": "gcc-release", - "displayName": "GCC Release", - "inherits": [ - "linux-base", - "gcc-flags" - ], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "CMAKE_CXX_COMPILER": "g++-13" - } - }, - { - "name": "gcc-11-release", - "displayName": "GCC Release", - "inherits": [ - "linux-base", - "gcc-flags" - ], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "CMAKE_CXX_COMPILER": "g++-11" - } - }, - { - "name": "gcc-coverage", - "displayName": "GCC Coverage", - "inherits": [ - "linux-base" - ], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Coverage", - "CMAKE_CXX_COMPILER": "g++-13", - "CMAKE_CXX_FLAGS_COVERAGE": "-Og -g --coverage -DSLANG_ASSERT_ENABLED=0 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-inline -fno-inline-small-functions -fno-default-inline", - "CMAKE_EXE_LINKER_FLAGS_COVERAGE": "--coverage", - "CMAKE_SHARED_LINKER_FLAGS_COVERAGE": "--coverage", - "SLANG_INCLUDE_COVERAGE": "ON", - "COVERAGE_GCOV_TOOL": "gcov-13" - } - }, - { - "name": "ci-coverage", - "displayName": "CI Coverage", - "inherits": [ - "gcc-coverage" - ], - "cacheVariables": { - "COVERAGE_HTML_COMMAND": "" - } - }, - { - "name": "macos-base", - "hidden": true, - "generator": "Unix Makefiles", - "binaryDir": "${sourceDir}/build/${presetName}", - "installDir": "${sourceDir}/install/${presetName}", - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Darwin" - } - }, - { - "name": "macos-release", - "displayName": "macOS Release", - "inherits": [ - "macos-base" - ], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release" + { + "name": "windows-base", + "hidden": true, + "inherits": "windows-flags", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/${presetName}", + "installDir": "${sourceDir}/install/${presetName}", + "cacheVariables": { + "CMAKE_CXX_COMPILER": "cl.exe", + "CMAKE_CXX_FLAGS": "/nologo /MP /volatile:iso /Zc:inline /Zc:preprocessor /EHsc /Zc:__cplusplus /Zc:externConstexpr /Zc:throwingNew" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "windows-debug", + "hidden": true, + "inherits": "windows-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_FLAGS_DEBUG": "/Zi /Ob0 /Od /RTC1", + "CMAKE_EXE_LINKER_FLAGS_DEBUG": "/nologo /DEBUG:FASTLINK", + "CMAKE_SHARED_LINKER_FLAGS_DEBUG": "/nologo /DEBUG:FASTLINK", + "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDebugDLL" + } + }, + { + "name": "windows-release", + "hidden": true, + "inherits": "windows-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_FLAGS_RELEASE": "/O2 /Ob3 /GS- /DNDEBUG", + "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL" + } + }, + { + "name": "win64-debug", + "displayName": "Win64 Debug", + "inherits": "windows-debug", + "architecture": { + "value": "x64", + "strategy": "external" + } + }, + { + "name": "win64-release", + "displayName": "Win64 Release", + "inherits": "windows-release", + "architecture": { + "value": "x64", + "strategy": "external" + } + }, + { + "name": "win64-debug-shared", + "displayName": "Win64 Debug Shared", + "inherits": [ + "win64-debug" + ], + "cacheVariables": { + "BUILD_SHARED_LIBS": "ON", + "SLANG_USE_MIMALLOC": "OFF" + } + }, + { + "name": "win64-debug-noexcept", + "displayName": "Win64 Debug No Exceptions", + "inherits": [ + "win64-debug" + ], + "cacheVariables": { + "CMAKE_CXX_FLAGS": "/nologo /MP /GR- /volatile:iso /Zc:inline /Zc:preprocessor /Zc:__cplusplus /Zc:externConstexpr /Zc:throwingNew /D_HAS_EXCEPTIONS=0" + } + }, + { + "name": "gcc-flags", + "hidden": true, + "cacheVariables": { + "SLANG_WARN_FLAGS": "-Wall;-Wextra;-Werror;-Wunused-value;-Wformat-security;-Wimplicit-fallthrough=5;-Walloc-zero;-Wlogical-op;-Wlogical-not-parentheses;-Wvla;-Wduplicated-cond;-Wtype-limits;-Wno-maybe-uninitialized;-Wno-alloc-size-larger-than;-Wno-dangling-reference;-Wno-stringop-overflow" + } + }, + { + "name": "clang-flags", + "hidden": true, + "cacheVariables": { + "SLANG_WARN_FLAGS": "-Wall;-Wextra;-Werror;-Warray-bounds-pointer-arithmetic;-Wassign-enum;-Wbad-function-cast;-Wcast-qual;-Wchar-subscripts;-Wcomma;-Wconditional-uninitialized;-Wconversion;-Wdelete-non-virtual-dtor;-Wdeprecated;-Wduplicate-enum;-Wduplicate-method-arg;-Wembedded-directive;-Wfor-loop-analysis;-Wformat-pedantic;-Widiomatic-parentheses;-Wimplicit-fallthrough;-Wpedantic;-Wrange-loop-analysis;-Wredundant-parens;-Wreserved-id-macro;-Wshadow;-Wundefined-reinterpret-cast;-Wunreachable-code-aggressive;-Wno-missing-braces;-Wno-deprecated-literal-operator", + "CMAKE_CXX_FLAGS": "-Wno-deprecated-declarations" + } + }, + { + "name": "linux-base", + "hidden": true, + "generator": "Unix Makefiles", + "binaryDir": "${sourceDir}/build/${presetName}", + "installDir": "${sourceDir}/install/${presetName}", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "clang-debug", + "displayName": "Clang Debug", + "inherits": [ + "linux-base", + "clang-flags" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_COMPILER": "clang++-18" + } + }, + { + "name": "clang-release", + "displayName": "Clang Release", + "inherits": [ + "linux-base", + "clang-flags" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_COMPILER": "clang++-18" + } + }, + { + "name": "clang-sanitize", + "displayName": "Clang Sanitize", + "description": "clang analysis with sanitizers enabled", + "inherits": [ + "linux-base", + "clang-flags" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_CXX_COMPILER": "clang++-18", + "CMAKE_CXX_FLAGS": "-DSLANG_ASSERT_ENABLED -fsanitize=undefined,address -fno-sanitize-recover=undefined -fno-omit-frame-pointer -fno-common -Wno-deprecated-declarations", + "CMAKE_EXE_LINKER_FLAGS": "-fsanitize=undefined,address -fno-sanitize-recover=undefined", + "CMAKE_SHARED_LINKER_FLAGS": "-fsanitize=undefined,address -fno-sanitize-recover=undefined", + "SLANG_CLANG_TIDY": "clang-tidy-18" + } + }, + { + "name": "gcc-debug-shared", + "displayName": "GCC Debug Shared", + "inherits": [ + "linux-base", + "gcc-flags" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_COMPILER": "g++-13", + "BUILD_SHARED_LIBS": "ON" + } + }, + { + "name": "gcc-debug-noexcept", + "displayName": "GCC Debug No Exceptions", + "inherits": [ + "linux-base", + "gcc-flags" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_FLAGS": "-fno-exceptions -fno-rtti", + "CMAKE_CXX_COMPILER": "g++-13" + } + }, + { + "name": "gcc-release", + "displayName": "GCC Release", + "inherits": [ + "linux-base", + "gcc-flags" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_COMPILER": "g++-13" + } + }, + { + "name": "gcc-11-release", + "displayName": "GCC Release", + "inherits": [ + "linux-base", + "gcc-flags" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_COMPILER": "g++-11" + } + }, + { + "name": "gcc-coverage", + "displayName": "GCC Coverage", + "inherits": [ + "linux-base" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Coverage", + "CMAKE_CXX_COMPILER": "g++-13", + "CMAKE_CXX_FLAGS_COVERAGE": "-Og -g --coverage -DSLANG_ASSERT_ENABLED=0 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-inline -fno-inline-small-functions -fno-default-inline", + "CMAKE_EXE_LINKER_FLAGS_COVERAGE": "--coverage", + "CMAKE_SHARED_LINKER_FLAGS_COVERAGE": "--coverage", + "SLANG_INCLUDE_COVERAGE": "ON", + "COVERAGE_GCOV_TOOL": "gcov-13" + } + }, + { + "name": "ci-coverage", + "displayName": "CI Coverage", + "inherits": [ + "gcc-coverage" + ], + "cacheVariables": { + "COVERAGE_HTML_COMMAND": "" + } + }, + { + "name": "macos-base", + "hidden": true, + "generator": "Unix Makefiles", + "binaryDir": "${sourceDir}/build/${presetName}", + "installDir": "${sourceDir}/install/${presetName}", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + } + }, + { + "name": "macos-release", + "displayName": "macOS Release", + "inherits": [ + "macos-base" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } } - } - ] -} + ] + } + + \ No newline at end of file diff --git a/include/slang/ast/ASTSerializer.h b/include/slang/ast/ASTSerializer.h index 6d0d476a6..bb234e6ab 100644 --- a/include/slang/ast/ASTSerializer.h +++ b/include/slang/ast/ASTSerializer.h @@ -38,6 +38,13 @@ class SLANG_EXPORT ASTSerializer { /// should be included in the JSON output. void setIncludeAddresses(bool set) { includeAddrs = set; } + /// Sets a flag that ensures only the information needed to validate the astPrinter is given. + /// the printing of types, targets is disabled + void enableMinimalInfo(bool set) { minimalInfo = set; } + + bool getMinimalInfoEnabled() { return minimalInfo; } + + /// Sets a flag that indicates whether source line and file /// information should be included in the JSON output. void setIncludeSourceInfo(bool set) { includeSourceInfo = set; } @@ -173,6 +180,7 @@ class SLANG_EXPORT ASTSerializer { Compilation& compilation; JsonWriter& writer; bool includeAddrs = true; + bool minimalInfo = false; bool includeSourceInfo = false; }; diff --git a/include/slang/ast/printer/defaultAstPrinter.h b/include/slang/ast/printer/defaultAstPrinter.h new file mode 100644 index 000000000..82eb7cda1 --- /dev/null +++ b/include/slang/ast/printer/defaultAstPrinter.h @@ -0,0 +1,1096 @@ +//------------------------------------------------------------------------------ +//! @file SyntaxPrinter.h +//! @brief Support for printing syntax nodes and tokens +// +// SPDX-FileCopyrightText: Michael Popoloski, Easics +// SPDX-License-Identifier: MIT +//------------------------------------------------------------------------------ +#pragma once + +#include "slang/ast/ASTVisitor.h" +#include "slang/ast/HierarchicalReference.h" +#include "slang/ast/SemanticFacts.h" +#include "slang/ast/expressions/LiteralExpressions.h" +#include "slang/ast/expressions/SelectExpressions.h" +#include "slang/ast/symbols/BlockSymbols.h" +#include "slang/ast/symbols/ParameterSymbols.h" +#include "slang/ast/symbols/PortSymbols.h" +#include "slang/ast/symbols/VariableSymbols.h" +#include "slang/ast/types/AllTypes.h" +#include "slang/ast/types/NetType.h" +#include "slang/ast/types/Type.h" +#include "slang/util/LanguageVersion.h" +#include "slang/util/Util.h" +#include "slang/ast/types/TypePrinter.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + + +namespace slang::ast { + +template +concept IsFunc = requires(T t) { + // selects SubroutineSymbol, MethodPrototypeSymbol + t.subroutineKind; +}; + +/// Provides support for printing a ast back to source code. +class SLANG_EXPORT AstPrinter : public ASTVisitor { +public: + AstPrinter(slang::ast::Compilation& compilation) : compilation(compilation){}; + + /// @return a view of the internal text buffer. + std::string_view str() const { return buffer; } + + void handle(const InvalidStatement& t); + + // wait_statement ::= wait ( expression ) statement_or_null + void handle(const WaitStatement& t); + + // wait_statement ::= wait fork; + void handle(const WaitForkStatement& t); + + // wait_statement ::= wait_order ( hierarchical_identifier { , hierarchical_identifier } ) action_block + void handle(const WaitOrderStatement& t); + + void handle(const InvalidAssertionExpr& t); + + // hierarchical_identifier ::= [ $root . ] { identifier constant_bit_select . } identifier + void handle(const HierarchicalValueExpression& t); + + // net_lvalue ::={ net_lvalue { , net_lvalue } } (this is used in other instances asswel) + void handle(const ConcatenationExpression& t); + + void handle(const NewArrayExpression& t); + + // mintypmax_expression ::= expression | expression : expression : expression + void handle(const MinTypMaxExpression& t); + + // value_range ::= expression | [ expression : expression ] + // TODO uitzoeken waarvoor die valuerange kind dient + void handle(const ValueRangeExpression& t); + + // void handle(const BinaryAssertionExpr& t); + // blocking_assignment ::= variable_lvalue = delay_or_event_control expression | + // variable_lvalue assignment_operator expression + // nonblocking_assignment ::= variable_lvalue <= [ delay_or_event_control ] expression + void handle(const AssignmentExpression& t); + + void handle(const UnaryExpression& t); + + void handle(const BinaryExpression& t); + + // subroutine_call_statement ::=subroutine_call ; + // subroutine_call ::= tf_call | system_tf_call | method_call | [ std:: ] randomize_call + // ps_or_hierarchical_tf_identifier { attribute_instance } [ ( list_of_arguments ) ] + // system_tf_call ::= system_tf_identifier [ ( list_of_arguments ) ] + void handle(const CallExpression& t); + + void handle(const NamedValueExpression& t); + + void handle(const ArbitrarySymbolExpression& t); + + void handle(const UnbasedUnsizedIntegerLiteral& t); + + void handle(const UnboundedLiteral& t); + + void handle(const IntegerLiteral& t); + + void handle(const ElementSelectExpression& t); + + void handle(const DistExpression& t); + + // inside_expression ::= expression inside { open_range_list } + void handle(const InsideExpression& t); + + // value_range ::=expression| [ expression : expression ] + void handle(const RangeSelectExpression& t); + + // class_new ::=[ class_scope ] new [ ( list_of_arguments ) ] + void handle(const NewClassExpression& t); + + // class_new ::=[ class_scope ] new [ ( list_of_arguments ) ] + void handle(const MemberAccessExpression& t); + + void handle(const SimpleAssignmentPatternExpression& t); + + void handle(const BinSelectWithFilterExpr& t); + + void handle(const BinaryBinsSelectExpr& t); + + // select_condition ::= binsof ( bins_expression ) [ intersect { covergroup_range_list } ] + void handle(const ConditionBinsSelectExpr& t); + + void handle(const UnaryBinsSelectExpr& t); + + // void handle(const AssertionInstanceExpression& t); + + // void handle(const SimpleAssertionExpr& t); + + void handle(const StringLiteral& t); + + void handle(const RealLiteral& t); + + // event_control::= @ ( event_expression ) + // event_expression ::=[ edge_identifier ] expression [ iff expression ] + void handle(const SignalEventControl& t); + + void handle(const ImplicitEventControl& t); + + // type_declaration ::= typedef data_type type_identifier { variable_dimension } ; + // TODO: formating type_str + void handle(const TypeAliasType& t); + + void handle(const ClassType& t); + + // covergroup_declaration ::= covergroup covergroup_identifier [ ( [ tf_port_list ] ) ] [ + // coverage_event ] ;{ coverage_spec_or_option } endgroup [ : covergroup_identifier ] + void handle(const CovergroupType& t); + + void handle(const CoverageOptionSetter& t); + + // + void handle(const EmptyStatement& t); + + // + void handle(const StatementList& t); + + // + void handle(const VariableDeclStatement& t); + + // disable_statement ::= disable fork ; + void handle(const DisableForkStatement& t); + + // disable_statement ::= disable hierarchical_block_identifier ; + void handle(const DisableStatement& t); + + // jump_statement ::=break ; + void handle(const BreakStatement& t); + + // jump_statement ::=continue ; + void handle(const ContinueStatement& t); + + void handle(const ExpressionStatement& t); + + // loop_statement ::= repeat ( expression ) statement_or_null + // statement_or_null ::=statement| { attribute_instance } ; + // statement ::= [ block_identifier : ] { attribute_instance } statement_item + void handle(const RepeatLoopStatement& t); + + // loop_statement ::= forever statement_or_null + void handle(const ForeverLoopStatement& t); + + // loop_statement ::= foreach ( ps_or_hierarchical_array_identifier [ loop_variables ] ) + // statement + void handle(const ForeachLoopStatement& t); + + // loop_statement ::= while ( expression ) statement_or_null + void handle(const WhileLoopStatement& t); + + // for ( [ for_initialization ] ; [ expression ] ; [ for_step ] ) statement_or_null + void handle(const ForLoopStatement& t); + + // conditional_statement ::= [ unique_priority ] if ( cond_predicate ) statement_or_null {else + // if ( cond_predicate ) statement_or_null } [ else statement_or_null ] + void handle(const ConditionalStatement& t); + + // case_statement ::= [ unique_priority ] case_keyword ( case_expression ) case_item { + // case_item} endcase + // | [ unique_priority ] case ( case_expression ) inside case_inside_item { + // case_inside_item } endcase + void handle(const CaseStatement& t); + + void handle(const BlockStatement& t); + + // immediate_assertion_statement ::= simple_immediate_assertion_statement | + // deferred_immediate_assertion_statement simple_immediate_assertion_statement ::= + // simple_immediate_assert_statement simple_immediate_assert_statement ::= assert ( + // expression ) action_block action_block ::=statement_or_null | [ statement ] else + // statement_or_null + void handle(const ImmediateAssertionStatement& t); + + // concurrent_assertion_statement ::=assert_property_statement| assume_property_statement + void handle(const ConcurrentAssertionStatement& t); + + // case_statement ::= | [ unique_priority ] case_keyword (case_expression )matches + // case_pattern_item { case_pattern_item } endcase + void handle(const PatternCaseStatement& t); + + // pattern ::= tagged member_identifier [ pattern ] + void handle(const TaggedPattern& t); + + // pattern ::=. variable_identifier + void handle(const VariablePattern& t); + // pattern ::= .* + void handle(const WildcardPattern& t); + + // assignment_pattern ::= '{ expression { , expression } } + void handle(const StructurePattern& t); + + void handle(const GenvarSymbol& t); + + void handle(const GenerateBlockSymbol& t); + + // loop_generate_construct ::= for ( genvar_initialization ; genvar_expression ; + // genvar_iteration ) generate_block + void handle(const GenerateBlockArraySymbol& t); + + void handle(const AttributeSymbol& t); + + void handle(const StatementBlockSymbol& t); + + // property_declaration ::= property property_identifier [ ( [ property_port_list ] ) ] ;{ + // assertion_variable_declaration }property_spec [ ; ] endproperty + void handle(const PropertySymbol& t); + + // property_port_item ::={ attribute_instance } [ local [ property_lvar_port_direction ] ] + // property_formal_typeformal_port_identifier {variable_dimension} [ = property_actual_arg ] + void handle(const AssertionPortSymbol& t); + + + /* + package_declaration ::= + { attribute_instance } package [ lifetime ] package_identifier ; + [ timeunits_declaration ] { { attribute_instance } package_item } + endpackage [ : package_identifier ] + */ + void handle(const PackageSymbol& t); + + // anonymous_program ::= program ; { anonymous_program_item } endprogram + void handle(const AnonymousProgramSymbol& t); + + // ding zoals initial + void handle(const ProceduralBlockSymbol& t); + + // continuous_assign ::= assign [ drive_strength ] [ delay3 ] list_of_net_assignments ; + // | assign [ delay_control ] list_of_variable_assignments ; + void handle(const ContinuousAssignSymbol& t); + + // delay_control ::= # delay_value | # ( mintypmax_expression ) + void handle(const DelayControl& t); + + void handle(const Delay3Control& t); + + void handle(const EventListControl& t); + + /// module_declaration ::= module_ansi_header [ timeunits_declaration ] { + /// non_port_module_item } endmodule [ : module_identifier ] interface_declaration ::= + /// interface_ansi_header [ timeunits_declaration ] { non_port_interface_item } endinterface [ : + /// interface_identifier ] program_declaration ::= program_ansi_header [ + /// timeunits_declaration ] { non_port_program_item } endprogram [ : program_identifier ] + + /// module_ansi_header ::= { attribute_instance } module_keyword [ lifetime ] + /// module_identifier <{ package_import_declaration } [ parameter_port_list ] [ + /// list_of_port_declarations ];> interface_ansi_header ::= { attribute_instance } interface [ + /// lifetime ] interface_identifier <{ package_import_declaration } [ parameter_port_list ] [ + /// list_of_port_declarations ];> program_ansi_header ::= { attribute_instance } program [ + /// lifetime ] program_identifier <{ package_import_declaration } [ parameter_port_list ] [ + /// list_of_port_declarations ] ;> + /// <> is handeld in InstanceBodySymbol + void handle(const slang::ast::InstanceSymbol& t); + + /// ansi_port_declaration ::=[ net_port_header ] port_identifier { unpacked_dimension } [ = + /// constant_expression ] + /// | [ variable_port_header ] port_identifier { variable_dimension } [ + /// = constant_expression ] + void handle(const slang::ast::PortSymbol& t); + + ///(non ansi) port ::=[ port_expression ] | . port_identifier ( [ port_expression ] ) + /// port_reference ::= port_identifier constant_select + void handleNonAnsiPort(const slang::ast::PortSymbol& t); + + /// ansi_port_declaration ::=[ interface_port_header ] port_identifier { unpacked_dimension } [ + /// = constant_expression ] + void handle(const slang::ast::InterfacePortSymbol& t); + + /// net_port_type ::= [ net_type ] data_type_or_implicit + void handle(const slang::ast::NetSymbol& t); + + void handle(const slang::ast::ScalarType& t); + + /// variable_port_type ::= var_data_type + /// var_data_type ::= data_type | var data_type_or_implicit + // data_declaration10 ::= [ var ] [ lifetime ] data_type_or_implicit + void handle(const slang::ast::VariableSymbol& t); + + void handle(const slang::ast::MultiPortSymbol& t); + + /// parameter_declaration ::= parameter data_type_or_implicit list_of_param_assignments + /// local_parameter_declaration ::= localparam data_type_or_implicit list_of_param_assignments + /// list_of_param_assignments ::= param_assignment { , param_assignment } always with lenght 1 + /// ?? param_assignment ::= parameter_identifier { unpacked_dimension } [ = + /// constant_param_expression ] + void handle(const slang::ast::ParameterSymbol& t); + + // Represents a module, interface, or program definition + void handle(const DefinitionSymbol& t); + + /// package_import_item ::= package_identifier :: identifier + void handle(const ExplicitImportSymbol& t); + + // package_import_item ::= package_identifier :: * + void handle(const WildcardImportSymbol& t); + + // modport_declaration ::= modport modport_item { , modport_item } ; + // modport declartion with multiple items get automaticly splitted in multiple separete modport + // declartions + void handle(const ModportSymbol& t); + + // net_alias ::= alias net_lvalue = net_lvalue { = net_lvalue } ; + void handle(const NetAliasSymbol& t); + + // modport_ports_declaration ::= { attribute_instance } modport_simple_ports_declaration + // modport_simple_ports_declaration ::= port_direction modport_simple_port { + // ,modport_simple_port} + void handle(const ModportPortSymbol& t); + + // udp_output_declaration ::= { attribute_instance } output port_identifier + // udp_input_declaration ::= { attribute_instance } input list_of_udp_port_identifiers + void handle(const PrimitivePortSymbol& t); + + // udp_declaration ::= udp_ansi_declaration udp_body endprimitive [ : udp_identifier ] + void handle(const PrimitiveSymbol& t); + + // config_declaration ::= config config_identifier ; { local_parameter_declaration ; + // }design_statement { config_rule_statement } endconfig [ : config_identifier ] + void handle(const ConfigBlockSymbol& t); + + // specify_block ::= specify { specify_item } endspecify + void handle(const SpecifyBlockSymbol& t); + + // specparam_declaration ::= specparam [ packed_dimension ] list_of_specparam_assignments ; + void handle(const SpecparamSymbol& t); + + // path_declaration ::=simple_path_declaration ;| edge_sensitive_path_declaration ; | + // state_dependent_path_declaration; + void handle(const TimingPathSymbol& t); + + // dient voor SubroutineSymbol, MethodPrototypeSymbol + // method_prototype ::= task_prototype | function_prototype + // function_prototype ::= function data_type_or_void function_identifier [ ( [ tf_port_list ] ) + // ] task_prototype ::= task task_identifier [ ( [ tf_port_list ] ) ] + template + void handle(const T& t); + + void handle(const FormalArgumentSymbol& t); + + void handle(const UninstantiatedDefSymbol& t); + + void handle(const CompilationUnitSymbol& t); + + // class_property ::= { property_qualifier } data_declaration + void handle(const ClassPropertySymbol& t); + + // checker_declaration ::= checker checker_identifier [ ( [ checker_port_list ] ) ] ; { { + // attribute_instance } checker_or_generate_item } endchecker [ : checker_identifier ] + void handle(const CheckerSymbol& t); + + void handle(const CheckerInstanceSymbol& t); + + // checker_declaration { { attribute_instance } checker_or_generate_item } these values get inserted in CheckerSymbol + void handle(const CheckerInstanceBodySymbol& t); + + // clocking_declaration ::= [ default ] clocking [ clocking_identifier ] clocking_event ;{ + // clocking_item }endclocking [ : clocking_identifier ] + void handle(const ClockingBlockSymbol& t); + + /// { package_import_declaration } [ parameter_port_list ] [ list_of_port_declarations ]; + void handle(const InstanceBodySymbol& t); + + void handle(const GenericClassDefSymbol& t); + + // constraint_declaration ::= [ static ] constraint constraint_identifier constraint_block + void handle(const ConstraintBlockSymbol& t); + + // production ::= [ data_type_or_void ] production_identifier [ ( tf_port_list ) ] : rs_rule { | + // rs_rule } ; + void handle(const RandSeqProductionSymbol& t); + + void handle(const RandSequenceStatement& t); + + void handle(const RandSeqProductionSymbol::ProdBase& t); + + void handle(const RandSeqProductionSymbol::CaseItem& t); + + void handle(std::span t); + + void handle(const ConstraintList& t); + + void handle(const CoverpointSymbol& t); + + void handle(const CovergroupBodySymbol& t); + + // cover_cross ::=[ cross_identifier : ] cross list_of_cross_items [ iff ( expression ) ] + // cross_body + void handle(const CoverCrossSymbol& t); + + // + void handle(const CoverageBinSymbol& t); + + // cross_body ::= { { cross_body_item ; } } + void handle(const CoverCrossBodySymbol& t); + + //severity_system_task ::= $fatal [ ( finish_number [, list_of_arguments ] ) ] ; + // | $error [ ( [ list_of_arguments ] ) ] ; + // | $warning [ ( [ list_of_arguments ] ) ] ; + // | $info [ ( [ list_of_arguments ] ) ] ; + void handle(const ElabSystemTaskSymbol& t); + + void visitTransList(std::span set); + + void visitTransSet(std::span list); + + void handle(const ExpressionConstraint& t); + + void handle(const ImplicationConstraint& t); + + void handle(const SolveBeforeConstraint& t); + + void handle(const ConditionalConstraint& t); + + void handle(const ForeachConstraint& t); + + void handle(const DisableSoftConstraint& t); + +private: + std::string buffer; + std::string* tempBuffer; + std::list writeNextBuffer; + + // used to make sure the internalSymbol of ansi ports aren't written as a member of a + // instanceBody, to make sure the direction of ansi ports is known. + std::map internalSymbols; + + + // the type in the ast is not the type defined by the type alias, this map is used to convert + // the type back to the type alias type + std::map typeConversions; + + // buffer for code in a statementblock that needs to be appended in the next proceduralBlock + std::string blockBuffer; + + Compilation& compilation; + + bool useTempBuffer = false; + bool inEventList = false; + bool isFrontEventList = false; + bool isBackEventList = false; + + // used to get parent symbol while you are in a expression + const Symbol* currSymbol; + + // used to detect if a visit has changed the buffer + int changedBuffer = 0; + + // the amount of spaces after a newline is depth*depth_multplier + std::size_t indentation_level = 0; + const int indentation_multiplier = 3; + + // converts the type to a type defined by a type alias if a conversion is available + std::string convertType(std::string type) { + // check if type in type conversions + + + std::size_t dot_loc = type.rfind("."); + if (typeConversions.count(type.substr(0, dot_loc))) + return typeConversions[type.substr(0, dot_loc)]; + return type; + } + + // this function is used when visiting the members of a scope, in this case not every element has to be ended with the divider. + // To prevent this behavouir element which already contain a newline are skipped if newline is true + void visitMembers(const Symbol* member, const std::string& div = ",", bool newline = true) { + std::string endChar = newline?"\n":std::string(div); + while (member) { + int currentBuffer = changedBuffer; + member->visit(*this); + std::string* writeBuffer = (useTempBuffer) ? (tempBuffer) : (&this->buffer); + + bool needsDivider = endChar != (*writeBuffer).substr((*writeBuffer).length() - endChar.length(), (*writeBuffer).length() - endChar.length()); + if (*writeBuffer != "" && needsDivider && (changedBuffer != currentBuffer)) { + write(std::string(div) + (newline?"\n":""),false); + } + member = member->getNextSibling(); + } + } + + template + void visitMembers(std::span t, const std::string& div = ",", + bool newline = false) { + for (auto item : t) { + int currentBuffer = changedBuffer; + item->visit(*this); + if (item != t.back() && changedBuffer != currentBuffer) + write(div, false); + std::string* writeBuffer = (useTempBuffer) ? (tempBuffer) : (&this->buffer); + if (*writeBuffer != "" && newline &&("\n" != (*writeBuffer).substr((*writeBuffer).length() - 1, (*writeBuffer).length() - 1))) + write("\n", false); + } + } + + template + void visitMembers(std::span t, const std::string& divider = ",", + bool newline = false) { + for (auto item : t) { + int currentBuffer = changedBuffer; + handle(item); + + if (&item != &t.back() && changedBuffer != currentBuffer) + write(divider, false); + + std::string* writeBuffer = (useTempBuffer) ? (tempBuffer) : (&this->buffer); + if (*writeBuffer != "" && newline &&("\n" != (*writeBuffer).substr((*writeBuffer).length() - 1, (*writeBuffer).length() - 1))) + write("\n", false); + } + } + + void write(std::string_view string, bool add_spacer = true, bool use_dollar = false) { + if (string != "") + changedBuffer++; + // check if there is a $ sign in the string and add its content to the writeNext buffer + // the $ is generated by the typewriter and represents a jump ex: int$[] identifier -> int + // identifier[] + std::size_t dollarLocation = string.find("$"); + std::string* writeBuffer = (useTempBuffer) ? (tempBuffer) : (&this->buffer); + + bool writeNextIsEmpty = writeNextBuffer.empty(); + if (dollarLocation != std::string::npos && use_dollar) { + std::string nextStr = std::string(string.substr(dollarLocation + 1)); + writeNextBuffer.push_back(nextStr); + string = string.substr(0, dollarLocation); + } + + if ((*writeBuffer).back() == '\n') { + // solves the indentation in new lines + std::string depth_string = std::string(indentation_level * indentation_multiplier, ' '); + (*writeBuffer).append(depth_string); + } + + // buffer == "" is added to ensure the first char of the program does not have a space + // infront of it + else if (add_spacer && (*writeBuffer) != "") { + (*writeBuffer).append(" "); + } + (*writeBuffer).append(std::string(string)); + + if (!writeNextIsEmpty) { + std::string element = writeNextBuffer.front(); + writeNextBuffer.pop_front(); + write(element, false); + } + } + + void writeAttributes(std::span attributes) { + if (!attributes.empty()) { + write("(*"); + for (auto attrib : attributes) { + attrib->visit(*this); + if (attrib != attributes.back()) + write(",", false); + write("*) "); + } + } + } + + void writeAttributeInstances(const Symbol& t) { + auto attributes = compilation.getAttributes(t); + writeAttributes(attributes); + } + + void writeAttributeInstances(const Statement& t) { + auto attributes = compilation.getAttributes(t); + writeAttributes(attributes); + } + + void writeAttributeInstances(const Expression& t) { + auto attributes = compilation.getAttributes(t); + writeAttributes(attributes); + } + + void writeTimeUnitsDeclaration(const std::optional t) { + if (t.has_value()) { + write("timeunit"); + write(t.value().toString()); + write(";"); + } + } + + + void write(NetType::NetKind kind) { + switch (kind) { + case (NetType::NetKind::Wire): + write("wire"); + break; + case (NetType::NetKind::WAnd): + write("wand"); + break; + case (NetType::NetKind::WOr): + write("wor"); + break; + case (NetType::NetKind::Tri): + write("tri"); + break; + case (NetType::NetKind::TriAnd): + write("triAnd"); + break; + case (NetType::NetKind::TriOr): + write("trior"); + break; + case (NetType::NetKind::Tri0): + write("tri0"); + break; + case (NetType::NetKind::Tri1): + write("tri1"); + break; + case (NetType::NetKind::TriReg): + write("trireg"); + break; + case (NetType::NetKind::Supply0): + write("supply0"); + break; + case (NetType::NetKind::Supply1): + write("supply1"); + break; + case (NetType::NetKind::UWire): + write("uwire"); + break; + case (NetType::NetKind::Interconnect): + write("interconnect"); + break; + case (NetType::NetKind::Unknown): + write("// unkown net type"); + break; + case (NetType::NetKind::UserDefined): + // Is handeld in the handler + break; + } + } + + // TODO finish this list + void write(BinaryOperator op) { + switch (op) { + case (BinaryOperator::Add): + write("+", false); + break; + case (BinaryOperator::Subtract): + write("-", false); + break; + case (BinaryOperator::Multiply): + write("*", false); + break; + case (BinaryOperator::Divide): + write("/", false); + break; + case (BinaryOperator::Mod): + write("%", false); + break; + case (BinaryOperator::BinaryAnd): + write("&", false); + break; + case (BinaryOperator::BinaryOr): + write("|", false); + break; + case (BinaryOperator::BinaryXor): + write("^", false); + break; + case (BinaryOperator::BinaryXnor): + write("^~", false); + break; + case (BinaryOperator::Equality): + write("==", false); + break; + case (BinaryOperator::Inequality): + write("!=", false); + break; + case (BinaryOperator::CaseEquality): + write("===", false); + break; + case (BinaryOperator::CaseInequality): + write("!==", false); + break; + case (BinaryOperator::GreaterThanEqual): + write(">=", false); + break; + case (BinaryOperator::GreaterThan): + write(">", false); + break; + case (BinaryOperator::LessThanEqual): + write("<=", false); + break; + case (BinaryOperator::LessThan): + write("<", false); + break; + case (BinaryOperator::LogicalAnd): + write("&&", false); + break; + case (BinaryOperator::LogicalOr): + write("||", false); + break; + case (BinaryOperator::LogicalEquivalence): + write("||", false); + break; + case (BinaryOperator::ArithmeticShiftLeft): + write("<<<", false); + break; + case (BinaryOperator::ArithmeticShiftRight): + write(">>>", false); + break; + case (BinaryOperator::LogicalShiftLeft): + write(">>", false); + break; + case (BinaryOperator::LogicalShiftRight): + write("<<", false); + break; + case (BinaryOperator::Power): + write("**", false); + break; + case (BinaryOperator::WildcardEquality): + write("==", false); + break; + case (BinaryOperator::WildcardInequality): + write("!=?", false); + break; + default: + SLANG_UNREACHABLE; + } + } + + // TODO finish this list + void write(UnaryOperator op) { + switch (op) { + case (UnaryOperator::Preincrement): + write("++"); + break; + case (UnaryOperator::Postincrement): + write("$++", false, true); + break; + case (UnaryOperator::Predecrement): + write("--"); + break; + case (UnaryOperator::Postdecrement): + write("$--", false, true); + break; + case (UnaryOperator::LogicalNot): + write("!", false, true); + break; + case (UnaryOperator::BitwiseAnd): + write("&", false, true); + break; + case (UnaryOperator::BitwiseNand): + write("~&", false, true); + break; + case (UnaryOperator::BitwiseOr): + write("|", false, true); + break; + case (UnaryOperator::BitwiseNor): + write("~|", false, true); + break; + case (UnaryOperator::BitwiseNot): + write("~", false, true); + break; + case (UnaryOperator::BitwiseXor): + write("^", false, true); + break; + case (UnaryOperator::BitwiseXnor): + write("^", false, true); + break; + case (UnaryOperator::Minus): + write("-", false, true); + break; + case (UnaryOperator::Plus): + write("+", false, true); + break; + default: + SLANG_UNREACHABLE; + } + } + + void write(ArgumentDirection direction) { + switch (direction) { + case (ArgumentDirection::In): + write("input", false); + break; + case (ArgumentDirection::Out): + write("output", false); + break; + case (ArgumentDirection::InOut): + write("inout", false); + break; + case (ArgumentDirection::Ref): + write("ref", false); + break; + default: + SLANG_UNREACHABLE; + } + } + + void write(PrimitivePortDirection direction) { + switch (direction) { + case (PrimitivePortDirection::In): + write("input", false); + break; + case (PrimitivePortDirection::Out): + write("output", false); + break; + case (PrimitivePortDirection::OutReg): + write("output reg", false); + break; + default: + SLANG_UNREACHABLE; + } + } + + void write(AssertionKind assertion) { + switch (assertion) { + case (AssertionKind::Assert): + case (AssertionKind::Assume): + write(lowerFirstLetter(toString(assertion))); + break; + case (AssertionKind::CoverProperty): + case (AssertionKind::CoverSequence): + write("cover"); + break; + default: + SLANG_UNREACHABLE; + } + } + + void write(CaseStatementCondition caseName) { + switch (caseName) { + case (CaseStatementCondition::Inside): + case (CaseStatementCondition::Normal): + write("case"); + break; + case (CaseStatementCondition::WildcardXOrZ): + write("casex"); + break; + case (CaseStatementCondition::WildcardJustZ): + write("casez"); + break; + + default: + SLANG_UNREACHABLE; + } + } + + void write(ProceduralBlockKind procedure) { + switch (procedure) { + case (ProceduralBlockKind::AlwaysComb): + write("always_comb"); + break; + case (ProceduralBlockKind::AlwaysLatch): + write("always_latch"); + break; + case (ProceduralBlockKind::AlwaysFF): + write("always_ff"); + break; + default: + write(lowerFirstLetter(toString(procedure))); + } + } + + void write(BinaryAssertionOperator op) { + switch (op) { + case (BinaryAssertionOperator::And): + write("and", false); + break; + case (BinaryAssertionOperator::Iff): + write("iff", false); + break; + case (BinaryAssertionOperator::Implies): + write("implies", false); + break; + case (BinaryAssertionOperator::Intersect): + write("intersect", false); + break; + case (BinaryAssertionOperator::OverlappedImplication): + write("|->"); + break; + case (BinaryAssertionOperator::OverlappedFollowedBy): + write("#-#", false); + break; + case (BinaryAssertionOperator::Or): + write("or", false); + break; + case (BinaryAssertionOperator::NonOverlappedFollowedBy): + write("#=#", false); + break; + case (BinaryAssertionOperator::NonOverlappedImplication): + write("|=>"); + break; + case (BinaryAssertionOperator::SUntil): + write("s_until"); + break; + case (BinaryAssertionOperator::SUntilWith): + write("s_until_with"); + break; + case (BinaryAssertionOperator::Throughout): + write("throughout"); + break; + case (BinaryAssertionOperator::Until): + write("until"); + break; + case (BinaryAssertionOperator::UntilWith): + write("until_with"); + break; + case (BinaryAssertionOperator::Within): + write("within"); + break; + default: + SLANG_UNREACHABLE; + } + } + + void write(CoverageBinSymbol::BinKind kind) { + switch (kind) { + case (CoverageBinSymbol::BinKind::Bins): + write("bins"); + break; + case (CoverageBinSymbol::BinKind::IllegalBins): + write("illegal_bins"); + break; + case (CoverageBinSymbol::BinKind::IgnoreBins): + write("ignore_bins"); + break; + default: + SLANG_UNREACHABLE; + } + } + + void write(CoverageBinSymbol::TransRangeList::RepeatKind kind) { + switch (kind) { + case (CoverageBinSymbol::TransRangeList::RepeatKind::None): + break; + case (CoverageBinSymbol::TransRangeList::RepeatKind::Consecutive): + write("*"); + break; + case (CoverageBinSymbol::TransRangeList::RepeatKind::Nonconsecutive): + write("="); + break; + case (CoverageBinSymbol::TransRangeList::RepeatKind::GoTo): + write("->"); + break; + default: + SLANG_UNREACHABLE; + } + } + + void write(ValueRangeKind kind) { + switch (kind) { + case (ValueRangeKind::Simple): + write(":"); + break; + case (ValueRangeKind::AbsoluteTolerance): + write("+/-"); + break; + case (ValueRangeKind::RelativeTolerance): + write("+%-"); + break; + default: + SLANG_UNREACHABLE; + } + } + + + void writeName(const Symbol& t, bool add_spacer = true) { + write(getRealName(t, currSymbol), add_spacer); + } + + // attempt to get the real hierachical name of an object ex:e.a instead of $root.m.e.a + // It does this by trying to find the scope in which both the caller and the actual item are visible + std::string getRealName(const Symbol& item, const Symbol* caller) { + // caller is often this.currSymnol which can be null + if (!caller) + return std::string(item.name); + + // loop through the parents of the item until the scope of the parent contains the other + // symbol + auto parent = item.getParentScope(); + while (!Lookup::isVisibleFrom(*caller, *parent)) { + auto& parent_symbol = parent->asSymbol(); + parent = parent_symbol.getParentScope(); + } + + if (parent) { + auto& parent_symbol = parent->asSymbol(); + + std::string parent_path_name = ""; + std::string item_path_name = ""; + auto grandparent = parent_symbol.getParentScope(); + + if (grandparent) { + grandparent->asSymbol().getHierarchicalPath(parent_path_name); + parent_path_name += "."; + item.getHierarchicalPath(item_path_name); + + if (parent_path_name.length() itemClassPair= Lookup::getContainingClass(*item.getParentScope()); + //check if the caller is a member of the same class + std::pair callerClassPair= Lookup::getContainingClass((*(*caller).getParentScope())); + + if(itemClassPair.first && callerClassPair.first){ + // if they are the same class there is no problem + if (itemClassPair.first == callerClassPair.first) + return std::string(item.name); + + if (callerClassPair.first->getBaseClass() == itemClassPair.first ) + return "super." + std::string(item.name); + else{ + return std::string(itemClassPair.first->name) +"::" + std::string(item.name); + } + } + } + return std::string(item.name); + } + + + std::string lowerFirstLetter(std::string_view string) { + if (string == "") + return ""; + std::string new_string = std::string(string); + new_string[0] = (char)tolower(new_string[0]); + return new_string; + } + + // lowers all letters of a string + std::string lower(std::string_view string) { + if (string == "") + return ""; + // TODO: een beter manier vinden om dit te doen + std::string new_string = std::string(string); + for (auto& x : new_string) { + x = (char)tolower(x); + } + return new_string; + } + + std::string getTypeStr(const Type& t){ + TypePrintingOptions options; + options.elideScopeNames = true; + options.skipScopedTypeNames = true; + TypePrinter printer; + printer.options = options; + printer.append(t); + + return printer.toString(); + } +}; + +} // namespace slang::ast diff --git a/include/slang/ast/symbols/CoverSymbols.h b/include/slang/ast/symbols/CoverSymbols.h index c37dfef71..5d4a45fa7 100644 --- a/include/slang/ast/symbols/CoverSymbols.h +++ b/include/slang/ast/symbols/CoverSymbols.h @@ -63,6 +63,8 @@ class SLANG_EXPORT CovergroupType : public Type, public Scope { public: using ArgList = std::span; + std::string_view name; + CovergroupType(Compilation& compilation, std::string_view name, SourceLocation loc, const CovergroupBodySymbol& body); diff --git a/include/slang/ast/symbols/VariableSymbols.h b/include/slang/ast/symbols/VariableSymbols.h index 635147144..cc70b63df 100644 --- a/include/slang/ast/symbols/VariableSymbols.h +++ b/include/slang/ast/symbols/VariableSymbols.h @@ -41,7 +41,11 @@ enum class SLANG_EXPORT VariableFlags : uint16_t { CheckerFreeVariable = 1 << 4, /// The variable is a function port with direction 'ref static'. - RefStatic = 1 << 5 + RefStatic = 1 << 5, + + /// This variable contains no new information and should be ingored + /// when printing the ast back to source code + isDuplicate = 1 << 6 }; SLANG_BITMASK(VariableFlags, RefStatic) diff --git a/scripts/__pycache__/syntaxfactory_gen.cpython-310.pyc b/scripts/__pycache__/syntaxfactory_gen.cpython-310.pyc new file mode 100644 index 000000000..46cd5a059 Binary files /dev/null and b/scripts/__pycache__/syntaxfactory_gen.cpython-310.pyc differ diff --git a/scripts/syntax_gen.py b/scripts/syntax_gen.py index 154fd64cc..1c5352dce 100755 --- a/scripts/syntax_gen.py +++ b/scripts/syntax_gen.py @@ -3,7 +3,6 @@ # # SPDX-FileCopyrightText: Michael Popoloski # SPDX-License-Identifier: MIT - import argparse import math import os diff --git a/source/ast/ASTSerializer.cpp b/source/ast/ASTSerializer.cpp index 6f2425664..8bf582608 100644 --- a/source/ast/ASTSerializer.cpp +++ b/source/ast/ASTSerializer.cpp @@ -195,7 +195,8 @@ void ASTSerializer::visit(const T& elem, bool inMembersArray) { } if constexpr (std::is_base_of_v) { write("kind", toString(elem.kind)); - write("type", *elem.type); + if (!minimalInfo) + write("type", *elem.type); auto attributes = compilation.getAttributes(elem); if (!attributes.empty()) { startArray("attributes"); @@ -284,7 +285,8 @@ void ASTSerializer::visit(const T& elem, bool inMembersArray) { } if constexpr (std::is_base_of_v) { - write("type", elem.getType()); + if (!minimalInfo) + write("type", elem.getType()); if (auto init = elem.getInitializer()) write("initializer", *init); diff --git a/source/ast/CMakeLists.txt b/source/ast/CMakeLists.txt index 21f4aeefe..3cbd37d74 100644 --- a/source/ast/CMakeLists.txt +++ b/source/ast/CMakeLists.txt @@ -55,6 +55,11 @@ target_sources( HierarchicalReference.cpp Lookup.cpp LValue.cpp + printer/StatementPrinter.cpp + printer/ExpressionPrinter.cpp + printer/PatternPrinter.cpp + printer/SymbolPrinter.cpp + printer/EtcPrinter.cpp OpaqueInstancePath.cpp Patterns.cpp Scope.cpp diff --git a/source/ast/expressions/AssignmentExpressions.cpp b/source/ast/expressions/AssignmentExpressions.cpp index dae181471..eb066bae7 100644 --- a/source/ast/expressions/AssignmentExpressions.cpp +++ b/source/ast/expressions/AssignmentExpressions.cpp @@ -2127,7 +2127,8 @@ void StructuredAssignmentPatternExpression::serializeTo(ASTSerializer& serialize serializer.startArray("typeSetters"); for (auto& setter : typeSetters) { serializer.startObject(); - serializer.write("type", *setter.type); + if (!serializer.getMinimalInfoEnabled()) + serializer.write("type", *setter.type); serializer.write("expr", *setter.expr); serializer.endObject(); } diff --git a/source/ast/printer/EtcPrinter.cpp b/source/ast/printer/EtcPrinter.cpp new file mode 100644 index 000000000..3bac429ac --- /dev/null +++ b/source/ast/printer/EtcPrinter.cpp @@ -0,0 +1,271 @@ +//------------------------------------------------------------------------------ +//! @file EtcPrinter.cpp +//! @brief adds Support for printing TimingControls, Types,... from the ast +// +// SPDX-FileCopyrightText: Michael Popoloski +// SPDX-License-Identifier: MIT +//------------------------------------------------------------------------------ +#include "slang/ast/printer/defaultAstPrinter.h" + +#include +#include "slang/ast/Constraints.h" +#include "slang/util/Util.h" +#include "slang/ast/types/TypePrinter.h" + +namespace slang::ast { + +void AstPrinter::handle(const EventListControl& t) { + // These are only used to make this syntax possible @(A,B,....) + this->inEventList = true; + this->isFrontEventList = true; + for (auto event : t.events) { + if (event == t.events.back()) + this->isBackEventList = true; + int currentBuffer = changedBuffer; + event->visit(*this); + this->isFrontEventList = false; + + if (event != t.events.back()&& changedBuffer != currentBuffer) + write(",",false); + } + this->inEventList = false; + this->isBackEventList = false; + this->inEventList = false; + + +} + +// delay_control ::= # delay_value | # ( mintypmax_expression ) +void AstPrinter::handle(const DelayControl& t) { + write("#"); + if (t.expr.kind == ExpressionKind::MinTypMax) { + write("(", false); + t.expr.visit(*this); + write(")", false); + } + else { + t.expr.visit(*this); + } +} + +void AstPrinter::handle(const Delay3Control& t) { + write("#"); + if (t.expr1.kind == ExpressionKind::MinTypMax) { + // delay3 ::= # ( mintypmax_expression [ , mintypmax_expression [ , + // mintypmax_expression ] ] ) + write("(", false); + t.expr1.visit(*this); + if (t.expr2) { + write(",", false); + (*t.expr2).visit(*this); + + if (t.expr3) { + write(",", false); + (*t.expr3).visit(*this); + } + } + write(")", false); + } + else { + // delay3 ::= # delay_value + t.expr1.visit(*this); + } +} + + +// event_control::= @ ( event_expression ) +// event_expression ::=[ edge_identifier ] expression [ iff expression ] +void AstPrinter::handle(const SignalEventControl& t) { + if (isFrontEventList || !inEventList) + write("@("); + + if (t.edge == EdgeKind::BothEdges) { + write("edge"); + } + else if (t.edge!=EdgeKind::None){ + write(lower(toString(t.edge))); + } + + t.expr.visit(*this); + + if (t.iffCondition) { + write("iff"); + (*t.iffCondition).visit(*this); + } + if (isBackEventList || !inEventList) + write(")"); +} + +void AstPrinter::handle([[maybe_unused]] const ImplicitEventControl& t) { + write("@*"); +} + +// type_declaration ::= typedef data_type type_identifier { variable_dimension } ; +// TODO: formating type_str +void AstPrinter::handle(const TypeAliasType& t) { + blockBuffer.append("typedef "); + // ex: union tagged{void Invalid;int Valid;}m3.u$1 shoulden't have the m3.u$1 + TypePrintingOptions options; + options.elideScopeNames = true; + options.skipScopedTypeNames = true; + TypePrinter printer; + printer.options = options; + printer.append(t); + + std::string type_str = printer.toString(); + std::size_t bracket_loc = type_str.rfind("}"); + blockBuffer.append(type_str.substr(0, bracket_loc + 1)); + blockBuffer.append(t.name); + blockBuffer.append(";\n"); + + // remove the name of the typealias to make it possible to compare them to getType() types + std::size_t dot_loc = type_str.rfind("."); + typeConversions.insert({type_str.substr(0, dot_loc), std::string(t.name)}); +} +/* +class_declaration ::= +[ virtual ] class [ lifetime ] class_identifier [ parameter_port_list ] +[ extends class_type [ ( list_of_arguments ) ] ] +[ implements interface_class_type { , interface_class_type } ] ; +{ class_item } +endclass [ : class_identifier]*/ +void AstPrinter::handle(const ClassType& t) { + if (t.isAbstract) + write("virtual"); + write("class"); + + if (t.thisVar){ + // [ lifetime ] class_identifier + write(t.thisVar->lifetime == VariableLifetime::Static ? "static" : "automatic"); + const Type& data_type = t.thisVar->getDeclaredType().get()->getType(); + write(convertType(getTypeStr(data_type)), true, true); + } + + if (t.getBaseClass() != nullptr){ + write("extends"); + write(t.getBaseClass()->name); + } + + if (!t.getDeclaredInterfaces().empty()){ + write("implements"); + for (auto interface: t.getDeclaredInterfaces()){ + interface->visit(*this); + } + } + write(";\n"); + + indentation_level++; + visitMembers(t.getFirstMember() ,";"); + indentation_level--; + + write("endclass\n"); +} + +//covergroup_declaration ::=covergroup covergroup_identifier [ ( [ tf_port_list ] ) ] [ coverage_event ] ;{ coverage_spec_or_option } endgroup [ : covergroup_identifier ] +void AstPrinter::handle(const CovergroupType& t) { + write("covergroup"); + write(t.name); + // ( [ tf_port_list ] ) + if(!t.getArguments().empty()){ + write("("); + visitMembers(t.getArguments()); + write(")"); + } + + if (t.getCoverageEvent()) + t.getCoverageEvent()->visit(*this); + + write(";\n"); + + visitMembers(t.getArguments().back()->getNextSibling(),";"); + + write("endgroup\n"); +} + +// coverage_option ::= option.member_identifier = expression +// type_option.member_identifier = constant_expression +void AstPrinter::handle(const CoverageOptionSetter& t){ + t.getExpression().visit(*this); +} + + +void AstPrinter::handle(const ConstraintList& t) { + visitMembers(t.list, ";",true); +} + +void AstPrinter::handle(const ExpressionConstraint& t) { + if(t.isSoft) + write("soft"); + t.expr.visit(*this); +} + +//constraint_expression ::=[ soft ] expression –> constraint_set ; +void AstPrinter::handle(const ImplicationConstraint& t) { + //t.expr.visit(*this); + t.predicate.visit(*this); + write("->"); + write("{"); + t.body.visit(*this); + write(";"); + write("}\n"); + +} + +//constraint_block_item ::= solve solve_before_list before solve_before_list ; +void AstPrinter::handle(const SolveBeforeConstraint& t) { + write("solve"); + visitMembers<>(t.solve); + write("before"); + visitMembers<>(t.after); + + //t.expr.visit(*this); +} + +// +void AstPrinter::handle(const ConditionalConstraint& t) { + write("if("); + t.predicate.visit(*this); + write("){\n"); + + indentation_level++; + t.ifBody.visit(*this); + indentation_level--; + + write("}\n"); + if (t.elseBody){ + write("else{\n"); + t.elseBody->visit(*this); + } + write("}\n"); + } + +void AstPrinter::handle(const DisableSoftConstraint& t) { + write("disable soft"); + t.target.visit(*this); +} +// t.expr.visit(*this); + +//constraint_expression ::= foreach ( ps_or_hierarchical_array_identifier [ loop_variables ] ) constraint_set +void AstPrinter::handle(const slang::ast::ForeachConstraint& t) { + write("foreach(" ); + t.arrayRef.visit(*this); + if (!t.loopDims.empty()){ + write("[",false); + for(auto LoopDim:t.loopDims ){ + writeName(*LoopDim.loopVar); + if (LoopDim.range.has_value()) + write(LoopDim.range.value().toString(),false); + + } + write("]",false); + } + write(")",false ); + write("{\n",false); + indentation_level++; + t.body.visit(*this); + indentation_level--; + write("}\n",false); +} + +} + // namespace slang::ast \ No newline at end of file diff --git a/source/ast/printer/ExpressionPrinter.cpp b/source/ast/printer/ExpressionPrinter.cpp new file mode 100644 index 000000000..62507ac87 --- /dev/null +++ b/source/ast/printer/ExpressionPrinter.cpp @@ -0,0 +1,278 @@ +//------------------------------------------------------------------------------ +//! @file ExpressionPrinter.cpp +//! @brief adds Support for printing expressions from the ast +// +// SPDX-FileCopyrightText: Michael Popoloski +// SPDX-License-Identifier: MIT +//------------------------------------------------------------------------------ +#include "slang/ast/printer/defaultAstPrinter.h" +#include "slang/ast/HierarchicalReference.h" +#include "slang/ast/expressions/MiscExpressions.h" + +#include + +namespace slang::ast { + +void AstPrinter::handle(const InvalidAssertionExpr& t) { + // wrap the invalid part of the code in a comment + // print instance + if (t.child) { + write("/* invalid code:"); + t.child->visit(*this); + write("*/"); + } +} + +// hierarchical_identifier ::= [ $root . ] { identifier constant_bit_select . } identifier +void AstPrinter::handle(const HierarchicalValueExpression& t) { + // get path + std::string path_name = ""; + t.symbol.getHierarchicalPath(path_name); + write(path_name); +} + +// net_lvalue ::={ net_lvalue { , net_lvalue } } (this is used in other instances asswel) +void AstPrinter::handle(const ConcatenationExpression& t) { + write("{"); + visitMembers<>(t.operands()); + write("}"); +} + +void AstPrinter::handle(const NewArrayExpression& t) { + write("new"); + write("["); + if (t.initExpr()) { + (*t.initExpr()).visit(*this); + } + else { + t.sizeExpr().visit(*this); + } + write("]"); +} + +// mintypmax_expression ::= expression | expression : expression : expression +void AstPrinter::handle(const MinTypMaxExpression& t) { + t.min().visit(*this); + write(":", false); + t.typ().visit(*this); + write(":", false); + t.max().visit(*this); +} + +// value_range ::= expression | [ expression : expression ] +void AstPrinter::handle(const ValueRangeExpression& t) { + write("["); + t.left().visit(*this); + write(t.rangeKind); + t.right().visit(*this); + write("]"); +} + +// blocking_assignment ::= variable_lvalue = delay_or_event_control expression | +// variable_lvalue assignment_operator expression +// nonblocking_assignment ::= variable_lvalue <= [ delay_or_event_control ] expression +void AstPrinter::handle(const AssignmentExpression& t) { + t.left().visit(*this); + + if (t.isCompound()) + write(t.op.value()); + + if (t.isNonBlocking()) { + write("<=", false); + } + else { + write("=", false); + } + + if (t.timingControl) { + t.timingControl->visit(*this); + } + + t.right().visit(*this); +} + +void AstPrinter::handle(const UnaryExpression& t) { + write(t.op); + visitDefault(t); +} + +void AstPrinter::handle(const BinaryExpression& t) { + t.left().visit(*this); + // ensures that compound operators work ex: += would be +=+ without this + if (t.left().kind != ExpressionKind::LValueReference) { + write(t.op); + } + t.right().visit(*this); +} + +// subroutine_call_statement ::=subroutine_call ; +// subroutine_call ::= tf_call | system_tf_call | method_call | [ std:: ] randomize_call +// ps_or_hierarchical_tf_identifier { attribute_instance } [ ( list_of_arguments ) ] +// system_tf_call ::= system_tf_identifier [ ( list_of_arguments ) ] +void AstPrinter::handle(const CallExpression& t){ + bool hasThisClass =t.thisClass()!= nullptr ; + if(hasThisClass){ + t.thisClass()->visit(*this); + write(".",false); + } + + if(std::holds_alternative(t.subroutine)){ + auto symbol =std::get(t.subroutine) ; + if (symbol) + writeName(*symbol, !hasThisClass); + else + write(t.getSubroutineName(), !hasThisClass); + + } + else { + write(t.getSubroutineName(), !hasThisClass); + } + writeAttributeInstances(t); + + write("(", false); + visitMembers<>(t.arguments()); + write(")", false); +} + +void AstPrinter::handle(const NamedValueExpression& t) { + writeName(t.symbol); +} + +void AstPrinter::handle(const UnbasedUnsizedIntegerLiteral& t) { + if (t.getLiteralValue().isUnknown()) + write("'x"); + else if (t.getLiteralValue().value == slang::logic_t::Z_VALUE) + write("'z"); + else { + write("'"); + write(std::to_string(t.getLiteralValue().value)); + } +} +void AstPrinter::handle([[maybe_unused]] const UnboundedLiteral& t) { + write("$"); +} + +void AstPrinter::handle(const IntegerLiteral& t) { + write(t.getValue().toString()); +} + +void AstPrinter::handle(const StringLiteral& t) { + write("\""); + write(t.getValue(), false); + write("\"", false); +} + +void AstPrinter::handle(const RealLiteral& t) { + write(std::to_string(t.getValue())); +} + +void AstPrinter::handle(const ElementSelectExpression& t) { + t.value().visit(*this); + write("[", false); + t.selector().visit(*this); + write("]", false); +} + +void AstPrinter::handle(const ArbitrarySymbolExpression& t) { + writeName(*t.symbol); +} +// expression_or_dist ::= expression [ dist { dist_list } ] +// dist_item ::= value_range [ dist_weight ] +// dist_weight ::=:= expression| :/ expression +void AstPrinter::handle(const DistExpression& t) { + t.left().visit(*this); + write("dist"); + write("{"); + for (auto dist : t.items()) { + int currentBuffer = changedBuffer; + dist.value.visit(*this); + if (dist.weight.has_value()) { + if (dist.weight.value().kind == DistExpression::DistWeight::PerValue) + write(":="); + else + write(":/"); + dist.weight->expr->visit(*this); + } + if (&dist.value != &(t.items().back().value) && changedBuffer != currentBuffer) + write(",", false); + } + write("}"); +} +// inside_expression ::= expression inside { open_range_list } +void AstPrinter::handle(const InsideExpression& t) { + t.left().visit(*this); + write("inside"); + write("{"); + visitMembers(t.rangeList()); + write("}"); +} + +// value_range ::=expression| [ expression : expression ] +void AstPrinter::handle(const RangeSelectExpression& t) { + t.value().visit(*this); + write("[", false); + t.left().visit(*this); + write(":", false); + t.right().visit(*this); + write("]", false); +} + +//class_new ::=[ class_scope ] new [ ( list_of_arguments ) ] +void AstPrinter::handle(const NewClassExpression& t) { + write(t.type->toString()); + write("::new",false); +} + +// +void AstPrinter::handle(const MemberAccessExpression& t) { + t.value().visit(*this); + write(".",false); + writeName(t.member,false); +} + +void AstPrinter::handle(const SimpleAssignmentPatternExpression& t){ + write("'{"); + visitMembers(t.elements()); + write("}"); + +} + +void AstPrinter::handle(const BinSelectWithFilterExpr& t){ + write("("); + t.expr.visit(*this); + if(t.matchesExpr) + t.matchesExpr->visit(*this); + write(")"); + write("with"); + write("("); + t.filter.visit(*this); + write(")"); + +} + //select_condition ::= binsof ( bins_expression ) [ intersect { covergroup_range_list } ] + +void AstPrinter::handle(const BinaryBinsSelectExpr& t){ + t.left.visit(*this); + if(t.op==BinaryBinsSelectExpr::And){ + write("&&"); + }else{ + write("||"); + } + t.right.visit(*this); +} +void AstPrinter::handle(const UnaryBinsSelectExpr& t){ + write("!"); + t.expr.visit(*this); +} +//select_condition ::= binsof ( bins_expression ) [ intersect { covergroup_range_list } ] +void AstPrinter::handle(const ConditionBinsSelectExpr& t){ + write("binsof("); + writeName(t.target); + write(")"); + if(!t.intersects.empty()){ + write("intersect{"); + visitMembers(t.intersects); + write("}"); + } +} +} // namespace slang::ast \ No newline at end of file diff --git a/source/ast/printer/PatternPrinter.cpp b/source/ast/printer/PatternPrinter.cpp new file mode 100644 index 000000000..6ee68e2bf --- /dev/null +++ b/source/ast/printer/PatternPrinter.cpp @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +//! @file Pattern.h +//! @brief Support for printing patterns from the ast +// +// SPDX-FileCopyrightText: Michael Popoloski +// SPDX-License-Identifier: MIT +//------------------------------------------------------------------------------ + +#include "slang/ast/printer/defaultAstPrinter.h" + + +namespace slang::ast { + +// case_statement ::= | [ unique_priority ] case_keyword (case_expression )matches +// case_pattern_item { case_pattern_item } endcase +void AstPrinter::handle(const PatternCaseStatement& t) { + if (t.check != UniquePriorityCheck::None) { + std::string_view priority = toString(t.check); + write(lowerFirstLetter(priority)); + } + + // case_keyword + write(t.condition); + write("("); + t.expr.visit(*this); + write(") matches\n"); + indentation_level++; + + // case_pattern_item ::= pattern [ &&& expression ] : statement_or_null + for (auto item : t.items) { + item.pattern.get()->visit(*this); + if (item.filter) { + write("&&&"); + item.filter->visit(*this); + } + write(":"); + item.stmt->visit(*this); + write("\n"); + } + + // case_item ::= | default [ : ] statement_or_null + if (t.defaultCase) { + write("default :"); + (*t.defaultCase).visit(*this); + write("\n"); + } + + indentation_level--; + write("endcase \n"); +} + +// pattern ::= tagged member_identifier [ pattern ] +void AstPrinter::handle(const TaggedPattern& t) { + write("tagged"); + writeName(t.member); + t.valuePattern->visit(*this); +} + +// pattern ::=. variable_identifier +void AstPrinter::handle(const VariablePattern& t) { + write("."); + + writeName(t.variable, false); +} +// pattern ::= .* +void AstPrinter::handle([[maybe_unused]] const WildcardPattern& t) { + write(".*"); +} + +// assignment_pattern ::= '{ expression { , expression } } +void AstPrinter::handle(const StructurePattern& t) { + write("'{"); + for (auto field_pattern : t.patterns) { + int currentBuffer = changedBuffer; + field_pattern.pattern->visit(*this); + if (field_pattern.pattern != t.patterns.back().pattern && changedBuffer != currentBuffer) + write(","); + } + write("}"); +} +} \ No newline at end of file diff --git a/source/ast/printer/StatementPrinter.cpp b/source/ast/printer/StatementPrinter.cpp new file mode 100644 index 000000000..18687d5e6 --- /dev/null +++ b/source/ast/printer/StatementPrinter.cpp @@ -0,0 +1,351 @@ +//------------------------------------------------------------------------------ +//! @file StatemenPrinter. +//! @brief Support for printing statements from the ast +// +// SPDX-FileCopyrightText: Michael Popoloski +// SPDX-License-Identifier: MIT +//------------------------------------------------------------------------------ + +#include "slang/ast/printer/defaultAstPrinter.h" + + +namespace slang::ast { + +void AstPrinter::handle(const InvalidStatement& t) { + // wrap the invalid part of the code in a comment + write("// InvalidStatement removed\n"); + visitDefault(t); +} + +// wait_statement ::= wait ( expression ) statement_or_null +void AstPrinter::handle(const WaitStatement& t) { + write("wait ("); + t.cond.visit(*this); + write(")"); + t.stmt.visit(*this); + write("\n"); +} + +// wait_statement ::= wait fork; +void AstPrinter::handle([[maybe_unused]] const WaitForkStatement& t) { + write("wait fork;\n"); +} + +// wait_statement ::= wait_order ( hierarchical_identifier { , hierarchical_identifier } ) +// action_block +void AstPrinter::handle(const WaitOrderStatement& t) { + write("wait_order ("); + visitMembers<>(t.events); + write(")"); + + // action_block ::=statement _or_null | [ statement ] else statement + t.ifTrue->visit(*this); + if (t.ifFalse) { + write("else"); + t.ifFalse->visit(*this); + } + write("\n"); +} + +// #test schrijven +void AstPrinter::handle(const EmptyStatement& t) { + // Represents an empty statement, used as a placeholder or an anchor for attributes. + writeAttributeInstances(t); + write(";"); + // visitDefault(t); +} + +// #test schrijven +void AstPrinter::handle(const StatementList& t) { + // Represents a list of statements. + visitDefault(t); +} + +void AstPrinter::handle(const VariableDeclStatement& t) { + t.symbol.visit(*this); + write(";\n"); +} + +// disable_statement ::= disable fork ; +void AstPrinter::handle([[maybe_unused]] const DisableForkStatement& t) { + write("disable fork ;\n"); +} + +// disable_statement ::= disable hierarchical_block_identifier ; +void AstPrinter::handle(const DisableStatement& t) { + write("disable"); + t.target.visit(*this); + write(";\n"); +} +// jump_statement ::=break ; +void AstPrinter::handle([[maybe_unused]] const BreakStatement& t) { + write("break;"); +} + +// jump_statement ::=continue ; +void AstPrinter::handle([[maybe_unused]] const ContinueStatement& t) { + write("continue;"); +} + +void AstPrinter::handle(const ExpressionStatement& t) { + visitDefault(t); + write(";\n"); +} +// loop_statement ::= repeat ( expression ) statement_or_null +// statement_or_null ::=statement| { attribute_instance } ; +// statement ::= [ block_identifier : ] { attribute_instance } statement_item +void AstPrinter::handle(const RepeatLoopStatement& t) { + write("repeat ("); + t.count.visit(*this); + write(")"); + indentation_level++; + t.body.visit(*this); + indentation_level--; + write("\n"); +} +// loop_statement ::= forever statement_or_null +void AstPrinter::handle(const ForeverLoopStatement& t) { + write("forever"); + indentation_level++; + t.body.visit(*this); + indentation_level--; + write("\n"); +} + +// loop_statement ::= foreach ( ps_or_hierarchical_array_identifier [ loop_variables ] ) +// statement +void AstPrinter::handle(const ForeachLoopStatement& t) { + write("foreach("); + t.arrayRef.visit(*this); + write("["); + for (auto var : t.loopDims) { + if (var.loopVar) { + writeName(*var.loopVar); + if (var.loopVar != t.loopDims.back().loopVar) + write(",",false); + } + } + write("]"); + write(")"); + + indentation_level++; + t.body.visit(*this); + indentation_level--; + write("\n"); +} +// loop_statement ::= while ( expression ) statement_or_null +void AstPrinter::handle(const WhileLoopStatement& t) { + write("while ("); + t.cond.visit(*this); + write(")"); + t.body.visit(*this); + write("\n"); +} +// for ( [ for_initialization ] ; [ expression ] ; [ for_step ] ) statement_or_null +void AstPrinter::handle(const ForLoopStatement& t) { + write("for ("); + + // for_initialization ::= list_of_variable_assignments + visitMembers<>(t.initializers); + write(";"); + + // stop expression + t.stopExpr->visit(*this); + write(";"); + + // for_step_assignment ::=operator_assignment| inc_or_dec_expression | + // function_subroutine_call + visitMembers<>(t.steps); + write(")\n"); + indentation_level++; + t.body.visit(*this); + indentation_level--; + write("\n"); +} +// conditional_statement ::= [ unique_priority ] if ( cond_predicate ) statement_or_null {else +// if ( cond_predicate ) statement_or_null } [ else statement_or_null ] +void AstPrinter::handle(const ConditionalStatement& t) { + + if (t.check != UniquePriorityCheck::None) { + std::string_view priority = toString(t.check); + write(lowerFirstLetter(priority)); + } + + write("if("); + // cond_predicate ::= expression_or_cond_pattern { &&& expression_or_cond_pattern } + for (auto condition : t.conditions) { + condition.expr.get()->visit(*this); + // cond_pattern ::= expression matches pattern + if (condition.pattern) { + write("matches "); + condition.pattern->visit(*this); + } + if (condition.expr.get() != t.conditions.back().expr.get()) { + write("&&&"); + } + } + write(")\n"); + + indentation_level++; + t.ifTrue.visit(*this); + indentation_level--; + + if (t.ifFalse) { + write("else\n"); + indentation_level++; + t.ifFalse->visit(*this); + indentation_level--; + } +} + +// case_statement ::= [ unique_priority ] case_keyword ( case_expression ) case_item { case_item} +// endcase +// | [ unique_priority ] case ( case_expression ) inside case_inside_item { +// case_inside_item } endcase +void AstPrinter::handle(const CaseStatement& t) { + if (t.check != UniquePriorityCheck::None) { + std::string_view priority = toString(t.check); + write(lowerFirstLetter(priority)); + } + + // case_keyword + write(t.condition); + write("(", false); + t.expr.visit(*this); + write(")", false); + + if (t.condition == CaseStatementCondition::Inside) { + write("inside"); + } + + write("\n", false); + + indentation_level++; + + for (auto item : t.items) { + // case_item :: case_item_expression { , case_item_expression } : statement_or_null + visitMembers<>(item.expressions); + write(":"); + item.stmt->visit(*this); + write("\n"); + } + + // case_item ::= | default [ : ] statement_or_null + if (t.defaultCase) { + write("default :"); + (*t.defaultCase).visit(*this); + write("\n"); + } + indentation_level--; + + write("endcase \n"); +} + + + +void AstPrinter::handle(const BlockStatement& t) { + // foreach creates a Blockstatement automaticly which causes a duplicate block statement + // when trying to print the ast ( the same happens with RandSequence ) + if (t.body.kind == StatementKind::ForeachLoop || t.body.kind == StatementKind::RandSequence) { + t.body.visit(*this); + return; + } + + // Represents a sequential or parallel block statement. + if (t.blockKind == StatementBlockKind::Sequential) { + write("begin"); + } + else { + write("fork"); + } + + if (t.blockSymbol) { + write(":", false); + writeName((*t.blockSymbol)); + write("\n"); + } + else { + write("\n"); + } + + indentation_level += 1; + + // first write the information from the statementBlock + write(blockBuffer); + blockBuffer = ""; + + t.body.visit(*this); + indentation_level -= 1; + + if (t.blockKind == StatementBlockKind::JoinAll) { + write("join\n"); + } + else if (t.blockKind == StatementBlockKind::JoinAny) { + write("join_any\n"); + } + else if (t.blockKind == StatementBlockKind::JoinNone) { + write("join_none\n"); + } + else { + write("end\n"); + } +} + +//immediate_assertion_statement ::= simple_immediate_assertion_statement | deferred_immediate_assertion_statement +//simple_immediate_assertion_statement ::= simple_immediate_assert_statement +//simple_immediate_assert_statement ::= assert ( expression ) action_block +//action_block ::= statement_or_null | [ statement ] else statement_or_null +void AstPrinter::handle(const ImmediateAssertionStatement& t){ + write(t.assertionKind); + if(t.isDeferred) + write(t.isFinal?"final":"#0"); + write("("); + t.cond.visit(*this); + write(")"); + if (t.ifTrue) + t.ifTrue->visit(*this); + + if (t.ifFalse){ + write("else"); + t.ifFalse->visit(*this); + } +} + +// concurrent_assertion_statement ::=assert_property_statement| assume_property_statement +// assert_property_statement ::=assert property ( property_spec ) action_block +void AstPrinter::handle(const ConcurrentAssertionStatement& t){ + write(t.assertionKind); + write("property"); + write("("); + // property_spec ::=[clocking_event ] [ disable iff ( expression_or_dist ) ] property_expr + t.propertySpec.visit(*this); + write(")"); + // action_block ::= statement_or_null | [ statement ] else statement_or_null + if (t.ifTrue){ + indentation_level++; + t.ifTrue->visit(*this); + indentation_level--; + } + + if (t.ifFalse){ + write("else"); + indentation_level++; + t.ifFalse->visit(*this); + indentation_level--; + } + write("\n"); +} +//randsequence_statement ::= randsequence ( [ production_identifier ] ) production { production } endsequence +void AstPrinter::handle(const RandSequenceStatement& t){ + // + write("randsequence("); + write(t.firstProduction->name); + write(")\n"); + indentation_level++; + t.firstProduction->visit(*this); + indentation_level--; + write("endsequence"); +} + + +} // namespace slang::ast diff --git a/source/ast/printer/SymbolPrinter.cpp b/source/ast/printer/SymbolPrinter.cpp new file mode 100644 index 000000000..77641d558 --- /dev/null +++ b/source/ast/printer/SymbolPrinter.cpp @@ -0,0 +1,1285 @@ +//------------------------------------------------------------------------------ +//! @file ExpressionPrinter.cpp +//! @brief adds Support for printing expressions from the ast +// +// SPDX-FileCopyrightText: Michael Popoloski +// SPDX-License-Identifier: MIT +//------------------------------------------------------------------------------ + +#include "slang/ast/Symbol.h" +#include "slang/ast/printer/defaultAstPrinter.h" +#include "slang/ast/symbols/CoverSymbols.h" +#include "slang/ast/symbols/MemberSymbols.h" +#include "slang/ast/symbols/SubroutineSymbols.h" +#include "slang/util/Util.h" +#include "slang/syntax/AllSyntax.h" + +namespace slang::ast { + +// genvar_initialization ::= [ genvar ] genvar_identifier = constant_expression +void AstPrinter::handle(const GenvarSymbol& t) { + currSymbol = &t; + write("genvar"); + write(t.name); +} + +// attr_spec ::= attr_name [ = constant_expression ] +// attr_name ::= identifier +void AstPrinter::handle(const AttributeSymbol& t) { + currSymbol = &t; + write(t.name); + if (auto value = t.getValue(); value) { + write("="); + write(value.toString()); + } +} + + + +/* +package_declaration ::= + { attribute_instance } package [ lifetime ] package_identifier ; + [ timeunits_declaration ] { { attribute_instance } package_item } + endpackage [ : package_identifier ] +*/ +void AstPrinter::handle(const PackageSymbol& t) { + currSymbol = &t; + // attribute_instance ::= (* attr_spec { , attr_spec } *) + writeAttributeInstances(t); + + write("package"); + + write(t.defaultLifetime == VariableLifetime::Static ? "static" : "automatic"); + + write(t.name); + write(";\n", false); + + // I chose one type, info about the used declaration is not availble + // timeunit time_literal [ / time_literal ] ; + writeTimeUnitsDeclaration(t.timeScale); + + visitDefault(t); + + write("endpackage"); +} + +// anonymous_program ::= program ; { anonymous_program_item } endprogram +void AstPrinter::handle(const AnonymousProgramSymbol& t) { + currSymbol = &t; + write("program;\n"); + visitDefault(t); + write("endprogram"); +} + +// ding zoals initial +void AstPrinter::handle(const ProceduralBlockSymbol& t) { + currSymbol = &t; + write(t.procedureKind); + + t.getBody().visit(*this); +} + +// continuous_assign ::= assign [ drive_strength ] [ delay3 ] list_of_net_assignments ; +// | assign [ delay_control ] list_of_variable_assignments ; +void AstPrinter::handle(const ContinuousAssignSymbol& t) { + currSymbol = &t; + write("assign"); + // drive_strength ::= ( strength0 , strength1 ) + bool driveStrengthExists = t.getDriveStrength().first.has_value() && + t.getDriveStrength().second.has_value(); + if (driveStrengthExists) { + write("("); + write(lower(toString(t.getDriveStrength().first.value())), false); + write("0", false); + write(",", false); + write(lower(toString(t.getDriveStrength().second.value()))); + write("1", false); + write(")", false); + } + + // delay3 | delay_control + if (t.getDelay()) + t.getDelay()->visit(*this); + + // list_of_net_assignments | list_of_variable_assignments + t.getAssignment().visit(*this); +} + +/// module_declaration ::= module_ansi_header [ timeunits_declaration ] { +/// non_port_module_item } endmodule [ : module_identifier ] interface_declaration ::= +/// interface_ansi_header [ timeunits_declaration ] { non_port_interface_item } endinterface [ : +/// interface_identifier ] program_declaration ::= program_ansi_header [ +/// timeunits_declaration ] { non_port_program_item } endprogram [ : program_identifier ] + +/// module_ansi_header ::= { attribute_instance } module_keyword [ lifetime ] +/// module_identifier <{ package_import_declaration } [ parameter_port_list ] [ +/// list_of_port_declarations ];> interface_ansi_header ::= { attribute_instance } interface [ +/// lifetime ] interface_identifier <{ package_import_declaration } [ parameter_port_list ] [ +/// list_of_port_declarations ];> program_ansi_header ::= { attribute_instance } program [ +/// lifetime ] program_identifier <{ package_import_declaration } [ parameter_port_list ] [ +/// list_of_port_declarations ] ;> +/// <> is handeld in InstanceBodySymbol +void AstPrinter::handle(const slang::ast::InstanceSymbol& t) { + currSymbol = &t; + writeAttributeInstances(t); + + // print instance + std::string_view instanceSymbol = toString(t.getDefinition().definitionKind); + std::string newSymbol = lowerFirstLetter(instanceSymbol); + write(newSymbol); + + // write [ lifetime ] x_identifier + t.getDefinition().visit(*this); + + // visit content instance <> + indentation_level += 1; + t.body.visit(*this); + indentation_level -= 1; + + // print endinstance + write("end" + newSymbol + "\n"); + + // check if there are connections that need to be made + if (!t.getPortConnections().empty()) { + // module_instantiation ::= module_identifier [ parameter_value_assignment ] hierarchical_instance { , hierarchical_instance } ; + writeName(t.body); + // TODO parameter_value_assignment + + // hierarchical_instance ::= name_of_instance ( [ list_of_port_connections ] ) | named_port_connection { , named_port_connection } + write(t.name); + write("(", false); + // list_of_port_connections ::= named_port_connection { , named_port_connection } or named equivalent + for (auto named_port : t.getPortConnections()) { + // named_port_connection ::= { attribute_instance } . port_identifier [ ( [expression ] ) ] + writeAttributeInstances(named_port->port); + write("."); + writeName(named_port->port, false); + write("(", false); + auto expression = named_port->getExpression(); + if (expression) + expression->visit(*this); + write(")", false); + + if (named_port != t.getPortConnections().back()) + write(",", false); + } + write(");\n", false); + } +} + +// ansi_port_declaration ::= [ net_port_header ] port_identifier { unpacked_dimension } [=constant_expression ] +// | [ variable_port_header ] port_identifier { variable_dimension } [= constant_expression ] +void AstPrinter::handle(const slang::ast::PortSymbol& t) { + currSymbol = &t; + // net_port_header ::= [ port_direction ] net_port_type + // variable_port_header ::= [ port_direction ] variable_port_type + + internalSymbols.insert({t.internalSymbol, t.direction}); + + if (!t.isAnsiPort) { + return handleNonAnsiPort(t); + } + + if (t.internalSymbol) { + // direction is handeld via the internalSymbols map + t.internalSymbol->visit(*this); + } + else { + write(t.direction); + if (t.getType().toString() == "void") { + write(".$()", true, true); + write(t.name, false); + } + else { + write(convertType(getTypeStr(t.getType())), true, true); + } + } + + if (t.getInternalExpr()) + t.getInternalExpr()->visit(*this); + + if (t.getInitializer()) { + write("=", false); + t.getInitializer()->visit(*this); + } +} + +///(non ansi) port ::=[ port_expression ] | . port_identifier ( [ port_expression ] ) +/// port_reference ::= port_identifier constant_select +void AstPrinter::handleNonAnsiPort(const slang::ast::PortSymbol& t) { + currSymbol = &t; + write(t.name); + if (t.getInternalExpr()) + t.getInternalExpr()->visit(*this); +} + +/// ansi_port_declaration ::=[ interface_port_header ] port_identifier { unpacked_dimension } [ +/// = constant_expression ] +void AstPrinter::handle(const slang::ast::InterfacePortSymbol& t) { + currSymbol = &t; + // interface_port_header ::= interface_identifier [ . modport_identifier] + if (t.interfaceDef) { + writeName(*t.interfaceDef); + } + else { + write("interface"); + } + + if (t.modport != "") { + write(".", false); + write(t.modport, false); + } + + // write port_identifier + write(t.name); +} + +/// net_port_type ::= [ net_type ] data_type_or_implicit +void AstPrinter::handle(const slang::ast::NetSymbol& t) { + if (t.isImplicit){ + return; + } + currSymbol = &t; + // add the direction if this symbol is part of the port declaration + if (internalSymbols.count(&t) != 0) + write(internalSymbols[&t]); + if(t.netType.netKind != NetType::NetKind::UserDefined){ + write(t.netType.netKind); + write(convertType(getTypeStr(t.getType())), true, true); + } + write(t.name); + + auto initializer = t.getInitializer(); + if (initializer) { + write("="); + initializer->visit(*this); + } +} + +void AstPrinter::handle(const slang::ast::ScalarType& t) { + currSymbol = &t; + write(t.name); +} + +/// variable_port_type ::= var_data_type +/// var_data_type ::= data_type | var data_type_or_implicit +// data_declaration10 ::= [ var ] [ lifetime ] data_type_or_implicit +void AstPrinter::handle(const slang::ast::VariableSymbol& t) { + currSymbol = &t; + // add the direction if this symbol is part of the port declaration + if (t.flags.has(VariableFlags::CompilerGenerated) | t.flags.has(VariableFlags::isDuplicate)) + return; + + bool isPort = internalSymbols.count(&t) != 0; + if (isPort) + write(internalSymbols[&t]); + + write("var"); + // without this the module ast doesn't contain the name of the variable for no apparent reason + //( after processing the new source code) + if (!t.flags.has(VariableFlags::RefStatic) && !isPort) + write(t.lifetime == VariableLifetime::Static ? "static" : "automatic"); + + if (t.flags.has(VariableFlags::CheckerFreeVariable)) + write("rand"); + + const Type& data_type = t.getDeclaredType().get()->getType(); + bitmask flags = t.getDeclaredType().get()->getFlags(); + + if ((flags & DeclaredTypeFlags::InferImplicit) != DeclaredTypeFlags::InferImplicit) { + write(convertType(getTypeStr(data_type)), true, true); + } + + writeAttributeInstances(t); + + write(t.name); + + auto initializer = t.getInitializer(); + if (initializer) { + write("="); + initializer->visit(*this); + } +} +void AstPrinter::handle(const slang::ast::ClassPropertySymbol& t) { + currSymbol = &t; + if (t.randMode != RandMode::None) { + write(lower(toString(t.randMode))); + } + const VariableSymbol& u = t; + this->handle(u); +} + +void AstPrinter::handle(const slang::ast::MultiPortSymbol& t) { + currSymbol = &t; + if (t.isNullPort) { + return visitDefault(t); + } + + // write(t.direction); + + // write(convertType(t.getType().toString()), true, true); + write("."); + write(t.name, false); + write("({", false); + + visitMembers<>(t.ports); + + write("})"); +} + +/// parameter_declaration ::= parameter data_type_or_implicit list_of_param_assignments +/// local_parameter_declaration ::= localparam data_type_or_implicit list_of_param_assignments +/// list_of_param_assignments ::= param_assignment { , param_assignment } always has a lenght 1 +/// ?? param_assignment ::= parameter_identifier { unpacked_dimension } [ = +/// constant_param_expression ] +void AstPrinter::handle(const slang::ast::ParameterSymbol& t) { + currSymbol = &t; + // parameter|localparam + if (t.isLocalParam()) { + write(std::string_view("localparam")); + } + else { + write(std::string_view("parameter")); + } + // data_type_or_implicit + write(lowerFirstLetter(convertType(getTypeStr(t.getType())))); + + // list_of_param_assignments->param_assignment->parameter_identifier + write(t.name); + + // TODO:unpacked_dimension + if (t.getInitializer()) + write("=", false); + visitDefault(t); +} + +// Represents a module, interface, or program definition +void AstPrinter::handle(const DefinitionSymbol& t) { + currSymbol = &t; + write(t.defaultLifetime == VariableLifetime::Static ? "static" : "automatic"); + write(t.name); +} + +/// package_import_item ::= package_identifier :: identifier +void AstPrinter::handle(const ExplicitImportSymbol& t) { + currSymbol = &t; + write(t.packageName); + write("::", false); + write(t.importName); +} + +// package_import_item ::= package_identifier :: * +void AstPrinter::handle(const WildcardImportSymbol& t) { + currSymbol = &t; + write(t.packageName); + write("::", false); + write("*", false); +} + +// modport_declaration ::= modport modport_item { , modport_item } ; +// modport declartion with multiple items get automaticly splitted in multiple separete modport +// declartions +void AstPrinter::handle(const ModportSymbol& t) { + currSymbol = &t; + write("modport"); + // modport_item ::= modport_identifier ( modport_ports_declaration { , + // modport_ports_declaration } ) + write(t.name); + write("("); + auto member = t.getFirstMember(); + visitMembers(member, ",",false); + write(")"); +} + +// net_alias ::= alias net_lvalue = net_lvalue { = net_lvalue } ; +void AstPrinter::handle(const NetAliasSymbol& t) { + write("alias"); + visitMembers<>(t.getNetReferences(), "="); +} +// property_declaration ::=property property_identifier [ ( [ property_port_list ] ) ] ;{ +// assertion_variable_declaration }property_spec [ ; ]endproperty +void AstPrinter::handle(const PropertySymbol& t) { + currSymbol = &t; + write("property"); + write(t.name); + auto member = t.getFirstMember(); + + if (!t.ports.empty()) { + write("("); + visitMembers<>(t.ports); + auto symbol = ((PortSymbol*)t.ports.back())->internalSymbol; + member = symbol ? symbol->getNextSibling() : t.ports.back(); + write(")"); + } + + write(";\n"); + indentation_level++; + + visitMembers(member, ";"); + + indentation_level--; + write("endproperty\n"); +} + +// property_port_item ::={ attribute_instance } [ local [ property_lvar_port_direction ] ] +// property_formal_type formal_port_identifier {variable_dimension} [ = property_actual_arg ] +void AstPrinter::handle(const AssertionPortSymbol& t) { + currSymbol = &t; + writeAttributeInstances(t); + + if (t.isLocalVar()) { + write("local input"); + } + + write(getTypeStr(t.declaredType.getType())); + + write(t.name); +} + +// modport_ports_declaration ::= { attribute_instance } modport_simple_ports_declaration +// modport_simple_ports_declaration ::= port_direction modport_simple_port { ,modport_simple_port} +void AstPrinter::handle(const ModportPortSymbol& t) { + currSymbol = &t; + writeAttributeInstances(t); + write(t.direction); + write(t.name); +} + +/// { package_import_declaration } [ parameter_port_list ] [ list_of_port_declarations ]; +void AstPrinter::handle(const InstanceBodySymbol& t) { + currSymbol = &t; + + auto remainingMember = t.getFirstMember(); + + // package_import_declaration ::= import package_import_item { , package_import_item } ; + auto wildCard = t.getWildcardImportData(); + if (wildCard) { + write("import"); + for (auto imports : wildCard->wildcardImports) { + + int currentBuffer = changedBuffer; + + imports->visit(*this); + if (changedBuffer != currentBuffer && imports != wildCard->wildcardImports.back()) + write(",", false); + } + write(";", false); + } + + // parameter_port_list ::= # ( list_of_param_assignments { , parameter_port_declaration } ) + if (!t.getParameters().empty()) { + write("#(", false); + for (auto param : t.getParameters()) { + if (param->isBodyParam()) + continue; + int currentBuffer = changedBuffer; + + param->symbol.visit(*this); + + if (changedBuffer != currentBuffer && param != t.getParameters().back()) + write(",", false); + + // implemented like this to prevent body parameters shifting the last parameters + remainingMember = param->symbol.getNextSibling(); + } + + write(")"); + } + + // list_of_port_declarations2 ::=( [ { attribute_instance} ansi_port_declaration { , { + // attribute_instance} ansi_port_declaration } ] ) + if (!t.getPortList().empty()) { + + write(std::string_view("("), false); + bool isAnsi = false; + + for (auto port : t.getPortList()) { + + if (!port) + continue; + writeAttributeInstances(*port); + + port->visit(*this); + + if (!isAnsi && port->kind == SymbolKind::Port) { + isAnsi = ((slang::ast::PortSymbol*)port)->isAnsiPort; + } + + if (port != t.getPortList().back()) + write(",", false); + } + + // the remainingMember shouden't be one of the internal symbols -> skip these + remainingMember = t.getPortList().back()->getNextSibling(); + while (isAnsi && internalSymbols.count(remainingMember) != 0 && remainingMember) + remainingMember = remainingMember->getNextSibling(); + + write(")"); + } + + write(";\n", false); + + // return if there are no remaining members + visitMembers(remainingMember, ";"); +} + +void AstPrinter::handle(const StatementBlockSymbol& t) { + currSymbol = &t; + // extra block where variables, .. are defined that are used in the corresponding instance, + // contains mostly redundant except TypeAliasTypes + t.visit(makeVisitor([&](auto& visitor, const TypeAliasType& TypeAliasType) { + AstPrinter::handle(TypeAliasType); + visitor.visitDefault(TypeAliasType); + })); +} + +// loop_generate_construct ::= for ( genvar_initialization ; genvar_expression ; genvar_iteration ) +// generate_block +void AstPrinter::handle(const GenerateBlockArraySymbol& t) { + currSymbol = &t; + // als je kijkt naar de ast van een gen block is het ( denk ik) onmgelijk om de originele source + // te herconstrueren + // _> deze worden via de originele source code geprint + write(t.getSyntax()->toString()); +} + +// TODO: implementent this without using the getSyntax function +void AstPrinter::handle(const GenerateBlockSymbol& t) { + currSymbol = &t; + write("generate"); + write(t.getSyntax()->toString()); + write("endgenerate\n"); +} + +// udp_output_declaration ::= { attribute_instance } output port_identifier +// udp_input_declaration ::= { attribute_instance } input list_of_udp_port_identifiers +void AstPrinter::handle(const PrimitivePortSymbol& t) { + currSymbol = &t; + writeAttributeInstances(t); + write(t.direction); + write(t.name); +} + +// udp_declaration ::= udp_ansi_declaration udp_body endprimitive [ : udp_identifier ] +// udp_ansi_declaration ::= {attribute_instance} primitive udp_identifier ( +// udp_declaration_port_list ) ; +void AstPrinter::handle(const PrimitiveSymbol& t) { + currSymbol = &t; + writeAttributeInstances(t); + write("primitive"); + write(t.name); + + // udp_declaration_port_list ::= udp_output_declaration , udp_input_declaration { , + // udp_input_declaration } + write("("); + visitMembers<>(t.ports); + write(")\n"); + indentation_level++; + + // udp_body ::= combinational_body | sequential_body + if (t.isSequential) { + // sequential_body ::= [ udp_initial_statement ] table sequential_entry { + // sequential_entry } endtable udp_initial_statement ::= initial output_port_identifier = + // init_val ; + if (t.initVal) { + write("intial"); + writeName(*t.ports.front()); + write("="); + write(t.initVal->toString()); + } + + write("table\n"); + // sequential_entry ::= seq_input_list : current_state : next_state ; + for (auto TableEntry : t.table) { + std::string entry = std::string(TableEntry.inputs); + entry.append(1, ':'); + entry.append(1, TableEntry.state); + entry.append(1, ':'); + entry.append(1, TableEntry.output); + write(entry + ";\n"); + } + write("endtable\n"); + } + else { + // combinational_body ::= table combinational_entry { combinational_entry } endtable + write("table\n"); + for (auto TableEntry : t.table) { + // combinational_entry ::= level_input_list : output_symbol ; + std::string entry = std::string(TableEntry.inputs); + entry.append(1, ':'); + entry.append(1, TableEntry.output); + write(entry + ";\n"); + } + write("endtable\n"); + } + indentation_level--; + + write("endprimitive"); +} + +// config_declaration ::= config config_identifier ; { local_parameter_declaration ; +// }design_statement { config_rule_statement } endconfig [ : config_identifier ] +void AstPrinter::handle(const ConfigBlockSymbol& t) { + currSymbol = &t; + write("config"); + write(t.name); + write(";\n"); + indentation_level++; + + auto member = t.getFirstMember(); + visitMembers(member, ";"); + + indentation_level--; + write("endconfig\n"); +} + +// specify_block ::= specify { specify_item } endspecify +void AstPrinter::handle(const SpecifyBlockSymbol& t) { + currSymbol = &t; + write("specify"); + + indentation_level++; + auto member = t.getFirstMember(); + visitMembers(member); + + indentation_level--; + + write("endspecify\n"); +} + +// specparam_declaration ::= specparam [ packed_dimension ] list_of_specparam_assignments ; +// specparam_assignment ::= specparam_identifier = constant_mintypmax_expression +void AstPrinter::handle(const SpecparamSymbol& t) { + currSymbol = &t; + write("specparam"); + write(t.name); + write("="); + if (t.getInitializer()) + t.getInitializer()->visit(*this); +} + +// path_declaration ::=simple_path_declaration ; +// | edge_sensitive_path_declaration ; +// | state_dependent_path_declaration; +// simple_path_declaration ::=parallel_path_description = path_delay_value +// edge_sensitive_path_declaration ::= parallel_edge_sensitive_path_description = path_delay_value +// state_dependent_path_declaration ::= if ( module_path_expression ) simple_path_declaration +void AstPrinter::handle(const TimingPathSymbol& t) { + currSymbol = &t; + if (t.isStateDependent) { + write("if ("); + if (t.getConditionExpr()) + t.getConditionExpr()->visit(*this); + write(")"); + } + write("("); + // full_path_description ::=( list_of_path_inputs [ polarity_operator ] *> list_of_path_outputs + // ) specify_input_terminal_descriptor ::=input_identifier [ [ constant_range_expression ] ] + visitMembers<>(t.getInputs()); + + if (t.polarity == TimingPathSymbol::Polarity::Positive) { + write("+", false); + } + else if (t.polarity == TimingPathSymbol::Polarity::Negative) { + write("-", false); + } + + if (t.connectionKind == TimingPathSymbol::ConnectionKind::Full) { + write("*>"); + } + else { + write("=>"); + } + visitMembers<>(t.getOutputs()); + + write(")=("); + visitMembers<>(t.getDelays()); + write(")"); +} + +// method_prototype ::= task_prototype | function_prototype +// function_prototype ::= function data_type_or_void function_identifier [ ( [ tf_port_list ] ) ] +// task_prototype ::= task task_identifier [ ( [ tf_port_list ] ) ] +template +void AstPrinter::handle(const T& t) { + currSymbol = &t; + // Ignore built-in methods on class types. + if (t.flags.has(MethodFlags::BuiltIn | MethodFlags::Randomize)) + return; + + if (t.flags.has(MethodFlags::Virtual)) + write("virtual"); + + if (t.flags.has(MethodFlags::Pure)) + write("pure virtual"); + + if (((t.flags & MethodFlags::Static) == MethodFlags::Static)) { + write("static"); + } + + if (t.flags.has(MethodFlags::InterfaceExtern)) + // extern_tf_declaration ::=extern method_prototype; + // extern forkjoin task_prototype ; + write("extern"); + + if (t.flags.has(MethodFlags::Constructor)) + write("new"); + + if (t.flags.has(MethodFlags::DPIImport)) + write(R"(import "DPI")"); + + if (t.flags.has(MethodFlags::DPIContext)) + write(R"(import "DPI" context)"); + + if (t.flags.has(MethodFlags::Initial)) + write(R"(initial)"); + + if (t.flags.has(MethodFlags::Extends)) + write("extends"); + + if (t.flags.has(MethodFlags::Final)) + write("final"); + + if ((t.flags & MethodFlags::ForkJoin) == MethodFlags::ForkJoin) + write("forkjoin"); + + if (((t.flags & MethodFlags::ModportExport) == MethodFlags::ModportExport)) { + write("export"); + } + + if (((t.flags & MethodFlags::ModportImport) == MethodFlags::ModportImport)) { + write("import"); + } + + write(lowerFirstLetter(toString(t.subroutineKind))); + + if (t.subroutineKind == SubroutineKind::Function) { + write(getTypeStr(t.declaredReturnType.getType())); + } + + // function_identifier | task_identifier + write(t.name); + + // tf_port_list + write("(", false); + for (auto arg : t.getArguments()) { + arg->visit(*this); + if (arg != t.getArguments().back()) + write(",", false); + } + write(")", false); + + if (t.isKind(SymbolKind::Subroutine)) { + write(";\n", false); + ((const SubroutineSymbol&)t).getBody().visit(*this); + write("end"); + write(lowerFirstLetter(toString(t.subroutineKind)), false); + write("\n", false); + } +} + +void AstPrinter::handle(const FormalArgumentSymbol& t) { + currSymbol = &t; + write(getTypeStr(t.getType())); + write(t.name); + if (t.getDefaultValue()) { + write("="); + t.getDefaultValue()->visit(*this); + } +} + +void AstPrinter::handle(const UninstantiatedDefSymbol& t) { + currSymbol = &t; + // module_instantiation ::= module_identifier [ parameter_value_assignment ] + // hierarchical_instance { , hierarchical_instance } ; + write(t.definitionName); + write(t.name); + + write("(", false); + + for (auto port : t.getPortConnections()) { + port->visit(*this); + + if (port != t.getPortConnections().back()) + write(",", false); + } + write(");\n", false); +} + +void AstPrinter::handle(const CompilationUnitSymbol& t) { + currSymbol = &t; + visitDefault(t); + // TypeAliases can also be attached to a compilationUnit + write(blockBuffer); + blockBuffer = ""; +} +// checker_declaration ::= checker checker_identifier [ ( [ checker_port_list ] ) ] ; { { +// attribute_instance } checker_or_generate_item } endchecker [ : checker_identifier ] +void AstPrinter::handle(const CheckerSymbol& t) { + currSymbol = &t; + write("checker"); + write(t.name); + write("("); + + for (auto port : t.ports) { + indentation_level++; + port->visit(*this); + + // default value is only availible as a syntax node -> print its string representation + if (port->defaultValueSyntax){ + write("="); + write(port->defaultValueSyntax->toString()); + } + + if (port != t.ports.back()) + write(",\n", false); + indentation_level--; + } + + write(");\n"); + // { { attribute_instance } checker_or_generate_item } + + // if a CheckerInstanceBodySymbol is in the ast it will find this comment and replace it with + // the content of the body + std::string identifier = "//" + std::to_string((unsigned long long)(void*)&t); + write(identifier); + + write("endchecker\n"); +} + +void AstPrinter::handle(const CheckerInstanceSymbol& t) { + currSymbol = &t; + t.body.visit(*this); + // checker_instantiation ::= ps_checker_identifier name_of_instance ( + // [list_of_checker_port_connections] ) ; + if (!t.getPortConnections().empty()) { + write(t.body.name); + write(t.name); + write("("); + // list_of_checker_port_connections + for (auto conn : t.getPortConnections()) { + // named_checker_port_connection ::= { attribute_instance } . formal_port_identifier [ ( + // [ property_actual_arg ] ) ] + std::visit( + [&](auto&& arg) { + if (arg) + arg->visit(*this); + }, + conn.actual); + + if (&conn.formal != &t.getPortConnections().back().formal) + write(",", false); + } + write(")"); + } +} + +// the body needs to be added to the checker symbol, there is no pointer from there to here so +// when the symbol is visited it wil leave a comment with its memory adress this function will make +// a string containing the body and inserting it in the correct location +void AstPrinter::handle(const CheckerInstanceBodySymbol& t) { + currSymbol = &t; + auto remainingMember = t.getFirstMember(); + + // remove the ports + while (remainingMember && remainingMember->kind == SymbolKind::AssertionPort) + remainingMember = remainingMember->getNextSibling(); + + std::string programBuffer; + this->tempBuffer = &programBuffer; + + this->useTempBuffer = true; + visitMembers(remainingMember, ";"); + this->useTempBuffer = false; + + // while elaborating the name of the port is replaced with the name of the argument + std::string checker_identifier = R"(\/\/)" + + std::to_string((unsigned long long)(void*)&t.checker); + std::regex reg(checker_identifier); + + this->buffer = std::regex_replace(this->buffer, reg, programBuffer); +} + +/* +clocking_declaration ::= [ default ] clocking [ clocking_identifier ] clocking_event ;{ +clocking_item }endclocking [ : clocking_identifier ] | global clocking [ clocking_identifier ] +clocking_event ; endclocking [ : clocking_identifier] +*/ +void AstPrinter::handle(const ClockingBlockSymbol& t) { + currSymbol = &t; + write("default clocking"); + write(t.name); + t.getEvent().visit(*this); + write(";\n"); + + visitMembers(t.getFirstMember(), ";"); + + write("endclocking\n"); +} + +// TODO: Adding the right information to the ast to make this possible +void AstPrinter::handle(const GenericClassDefSymbol& t) { + currSymbol = &t; + write(t.getSyntax()->toString()); + write("endclass \n"); +} + +// constraint_declaration ::= [ static ] constraint constraint_identifier constraint_block +void AstPrinter::handle(const ConstraintBlockSymbol& t) { + currSymbol = &t; + if (t.flags.has(ConstraintBlockFlags::Static)) + write("static"); + write("constraint"); + write(t.name); + // constraint_block ::= { { constraint_block_item } } + write("{\n"); + indentation_level++; + + t.getConstraints().visit(*this); + indentation_level--; + + write("}"); +} + +//severity_system_task ::= $fatal [ ( finish_number [, list_of_arguments ] ) ] ; +// | $error [ ( [ list_of_arguments ] ) ] ; +// | $warning [ ( [ list_of_arguments ] ) ] ; +// | $info [ ( [ list_of_arguments ] ) ] ; +void AstPrinter::handle(const ElabSystemTaskSymbol& t) { + if(t.taskKind !=ElabSystemTaskKind::StaticAssert){ + write("$"); + write(lowerFirstLetter(toString(t.taskKind)),false); + if(t.getMessage().has_value()){ + write("(\"",false); + write(t.getMessage().value()); + write("\")"); + } + } + +} + +// production ::= [ data_type_or_void ] production_identifier [ ( tf_port_list ) ] : rs_rule { | +// rs_rule } ; +void AstPrinter::handle(const RandSeqProductionSymbol& t) { + currSymbol = &t; + write(t.name, false); + if (!t.arguments.empty()) { + write("("); + visitMembers(t.arguments); + write(")"); + } + // tf_port_list apears to be unimplemented + write(":"); + handle(t.getRules()); + write(";\n"); + if (t.getNextSibling() != nullptr && + t.getNextSibling()->kind == SymbolKind::RandSeqProduction) { + t.getNextSibling()->visit(*this); + } +} + +// rs_case_item ::=case_item_expression { , case_item_expression } : production_item ; +void AstPrinter::handle(const RandSeqProductionSymbol::CaseItem& t) { + visitMembers(t.expressions, ","); + write(":", false); + handle(t.item); +} + +void AstPrinter::handle(const RandSeqProductionSymbol::ProdBase& t) { + switch (t.kind) { + // production_item ::= production_identifier [ ( list_of_arguments ) ] + case (RandSeqProductionSymbol::ProdKind::Item): { + auto prodItem = ((const RandSeqProductionSymbol::ProdItem&)t); + write(prodItem.target->name); + if (!prodItem.args.empty()) { + write("(", false); + visitMembers(prodItem.args); + write(")", false); + } + break; + } + // rs_code_block ::= { { data_declaration } { statement_or_null } } + case (RandSeqProductionSymbol::ProdKind::CodeBlock): { + write("{"); + auto codeBlock = (const RandSeqProductionSymbol::CodeBlockProd&)t; + codeBlock.block->visit(*this); + write("}"); + break; + } + // rs_if_else ::= if ( expression ) production_item [ else production_item ] + case (RandSeqProductionSymbol::ProdKind::IfElse): { + write("if("); + auto ifElseItem = (const RandSeqProductionSymbol::IfElseProd&)t; + ifElseItem.expr->visit(*this); + write(")"); + ifElseItem.ifItem.visit(*this); + if (ifElseItem.elseItem.has_value()) { + write("else"); + handle(ifElseItem.elseItem.value()); + } + break; + } + // rs_repeat ::= repeat ( expression ) production_item + case (RandSeqProductionSymbol::ProdKind::Repeat): { + write("repeat("); + auto repeatItem = (const RandSeqProductionSymbol::RepeatProd&)t; + repeatItem.expr->visit(*this); + write(")"); + handle(repeatItem.item); + break; + } + + // rs_case ::= case ( case_expression ) rs_case_item { rs_case_item } endcase + case (RandSeqProductionSymbol::ProdKind::Case): { + write("case("); + auto caseItem = (const RandSeqProductionSymbol::CaseProd&)t; + caseItem.expr->visit(*this); + write(")\n"); + indentation_level ++; + visitMembers(caseItem.items, ";",true); + // rs_case_item:= default [ : ] production_item ; + if (caseItem.defaultItem.has_value()) { + write("default :"); + handle(caseItem.defaultItem.value()); + write(";\n"); + } + indentation_level--; + write("endcase"); + + break; + } + } +} + +// rs_rule ::= rs_production_list [ := weight_specification [ rs_code_block ] ] +void AstPrinter::handle(std::span t) { + + // rs_rule ::= rs_production_list [ := weight_specification [ rs_code_block ] ] + for (long unsigned int i = 0; i < t.size(); i++) { + auto rule = t[i]; + // rs_production_list ::= rs_prod { rs_prod } + // | rand join [ ( expression ) ] production_item production_item { + // production_item } + if (rule.isRandJoin) { + write("rand join"); + + if (rule.randJoinExpr){ + write("(", false); + rule.randJoinExpr->visit(*this); + write(")", false); + } + } + visitMembers(rule.prods," "); + if (rule.weightExpr != nullptr) { + write(":=("); + rule.weightExpr->visit(*this); + write(")"); + if (rule.codeBlock.has_value()) + handle(rule.codeBlock.value()); + } + + if (i != t.size() - 1) { + write("|"); + } + } +} + +// cover_point ::=[ [ data_type_or_implicit ] cover_point_identifier : ] coverpoint expression [ iff +// ( expression ) ] bins_or_empty +void AstPrinter::handle(const CoverpointSymbol& t) { + currSymbol = &t; + if (t.name != "") { + write(getTypeStr(t.declaredType.getType())); + write(t.name); + write(":"); + } + + write("coverpoint"); + + t.getCoverageExpr().visit(*this); + if (t.getIffExpr()) { + write("iff"); + write("("); + t.getIffExpr()->visit(*this); + write(")"); + } + + write("{"); + indentation_level++; + + if (!t.options.empty()) { + write("\n", false); + visitMembers(t.options, ";", true); + } + + for (auto& member : t.members()) { + // BinsSelectExpr. + if (member.kind == SymbolKind::CoverageBin) { + int currentBuffer = changedBuffer; + member.visit(*this); + if (currentBuffer != changedBuffer) + write(";\n"); + } + } + + indentation_level--; + write("}"); +} + +// cover_cross ::=[ cross_identifier : ] cross list_of_cross_items [ iff ( expression ) ] cross_body +void AstPrinter::handle(const CoverCrossSymbol& t) { + currSymbol = &t; + if (t.name != "") { + write(t.name); + write(":"); + } + + write("cross"); + + for (auto& target : t.targets) { + write(target->name); + if (target != t.targets.back()) { + write(","); + } + } + + if (t.getIffExpr()) { + write("iff"); + write("("); + t.getIffExpr(); + write(")"); + } + + write("{"); + indentation_level++; + + if (!t.options.empty()) { + write("\n", false); + visitMembers(t.options, ";", true); + } + + for (auto& member : t.members()) { + // BinsSelectExpr. + if (member.kind == SymbolKind::CoverCrossBody) { + member.visit(*this); + } + } + + indentation_level--; + write("}"); +} + +// cross_body ::= { { cross_body_item ; } } +void AstPrinter::handle(const CoverCrossBodySymbol& t) { + currSymbol = &t; + for (auto& member : t.members()) { + // BinsSelectExpr. + // cross_body_item ::=function_declaraton| bins_selection_or_option + if (member.kind != SymbolKind::TypeAlias) { + int currentBuffer = changedBuffer; + member.visit(*this); + if (currentBuffer != changedBuffer) + write(";\n"); + } + } +} + +// bins_or_options ::=[ wildcard ] bins_keyword bin_identifier [ [ [ covergroup_expression ] ] ] = { +// covergroup_range_list } [ with ( with_covergroup_expression ) ] [ iff ( expression ) ] +// bins_or_options ::= bins_keyword bin_identifier [ [ [ covergroup_expression ] ] ] = default [ iff +// ( expression ) ] +void AstPrinter::handle(const CoverageBinSymbol& t) { + currSymbol = &t; + if (t.isWildcard) { + write("wildcard"); + } + write(t.binsKind); + + write(t.name); + if (!t.isDefaultSequence) { + if (t.isArray) { + write("[", false); + if (t.getSetCoverageExpr()) + t.getSetCoverageExpr()->visit(*this); + write("]=", false); + } + + if (!t.getValues().empty() && !t.isDefault) { + write("{"); + visitMembers(t.getValues()); + write("};"); + } + else if (!t.getTransList().empty() && !t.isDefault) { + // getTransList returns a list of transSets which is made up tramsRangeLitst= + visitTransSet(t.getTransList()); + write("default"); + } + else if (t.getCrossSelectExpr() != nullptr && !t.isDefault) + t.getCrossSelectExpr()->visit(*this); + else if (t.isDefault) + write("default"); + else + SLANG_UNREACHABLE; + } + else { + write("= default sequence"); + } +} + +void AstPrinter::visitTransList(std::span set) { + for (auto& list : set) { + // trans_set ::= trans_range_list { => trans_range_list } + // trans_range_list ::=trans_item| trans_item [* repeat_range ]| trans_item [–> repeat_range + // ]| trans_item [= repeat_range ] + visitMembers(list.items); + if (list.repeatKind != CoverageBinSymbol::TransRangeList::RepeatKind::None) { + write("["); + write(list.repeatKind); + if (list.repeatFrom) { + list.repeatFrom->visit(*this); + if (list.repeatTo) { + write(":"); + list.repeatTo->visit(*this); + } + } + write("]"); + } + if (&set.back() != &list) { + write("=>"); + } + } +} + +// trans_list ::= ( trans_set ) { , ( trans_set ) } +void AstPrinter::visitTransSet(std::span list) { + + for (auto& set : list) { + write("("); + // trans_set ::= trans_range_list { => trans_range_list } + // trans_range_list ::=trans_item| trans_item [* repeat_range ]| trans_item [–> repeat_range + // ]| trans_item [= repeat_range ] + visitTransList(set); + + write(")"); + if (&list.back() != &set) + write(","); + } +} + +void AstPrinter::handle(const CovergroupBodySymbol& t) { + currSymbol = &t; + // visit everything except for the class propertys + for (auto& member : t.members()) { + if (member.kind == SymbolKind::ClassProperty) + continue; + else { + int currentBuffer = changedBuffer; + member.visit(*this); + if (changedBuffer != currentBuffer) + write(";\n"); + } + } + visitMembers(t.options); +} + +} // namespace slang::ast \ No newline at end of file diff --git a/source/ast/symbols/CoverSymbols.cpp b/source/ast/symbols/CoverSymbols.cpp index ff19fdc3b..661a43c7e 100644 --- a/source/ast/symbols/CoverSymbols.cpp +++ b/source/ast/symbols/CoverSymbols.cpp @@ -217,13 +217,13 @@ const CovergroupType& CovergroupType::fromSyntax(const Scope& scope, // is used to implicitly declare a property of the covergroup type. bool inClass = scope.asSymbol().kind == SymbolKind::ClassType; std::string_view name = inClass ? ""sv : syntax.name.valueText(); - + auto& comp = scope.getCompilation(); auto body = comp.emplace(comp, syntax.name.location()); auto result = comp.emplace(comp, name, syntax.name.location(), *body); result->setSyntax(syntax); result->setAttributes(scope, syntax.attributes); - + result->name = syntax.name.valueText(); if (!syntax.extends) { if (syntax.portList) { SmallVector args; @@ -281,7 +281,7 @@ const CovergroupType& CovergroupType::fromSyntax(const Scope& scope, VariableLifetime::Automatic, Visibility::Public); var->setType(*result); - var->flags |= VariableFlags::Const; + var->flags |= VariableFlags::Const | VariableFlags::isDuplicate; classProperty = var; if (syntax.extends) diff --git a/source/ast/symbols/ParameterSymbols.cpp b/source/ast/symbols/ParameterSymbols.cpp index 6ebd7fe8b..cabec4d3f 100644 --- a/source/ast/symbols/ParameterSymbols.cpp +++ b/source/ast/symbols/ParameterSymbols.cpp @@ -270,7 +270,8 @@ bool TypeParameterSymbol::isOverridden() const { } void TypeParameterSymbol::serializeTo(ASTSerializer& serializer) const { - serializer.write("type", targetType.getType()); + if (!serializer.getMinimalInfoEnabled()) + serializer.write("type", targetType.getType()); serializer.write("isLocal", isLocalParam()); serializer.write("isPort", isPortParam()); serializer.write("isBody", isBodyParam()); diff --git a/source/ast/symbols/PortSymbols.cpp b/source/ast/symbols/PortSymbols.cpp index 71ec46aa7..db92441c8 100644 --- a/source/ast/symbols/PortSymbols.cpp +++ b/source/ast/symbols/PortSymbols.cpp @@ -1499,7 +1499,8 @@ void PortSymbol::fromSyntax( } void PortSymbol::serializeTo(ASTSerializer& serializer) const { - serializer.write("type", getType()); + if (!serializer.getMinimalInfoEnabled()) + serializer.write("type", getType()); serializer.write("direction", toString(direction)); if (isNullPort) @@ -1566,7 +1567,8 @@ const Type& MultiPortSymbol::getType() const { } void MultiPortSymbol::serializeTo(ASTSerializer& serializer) const { - serializer.write("type", getType()); + if (!serializer.getMinimalInfoEnabled()) + serializer.write("type", getType()); serializer.write("direction", toString(direction)); serializer.startArray("ports"); diff --git a/source/ast/types/AllTypes.cpp b/source/ast/types/AllTypes.cpp index 15443d09e..8240d31c8 100644 --- a/source/ast/types/AllTypes.cpp +++ b/source/ast/types/AllTypes.cpp @@ -1280,7 +1280,8 @@ ConstantValue TypeAliasType::getDefaultValueImpl() const { } void TypeAliasType::serializeTo(ASTSerializer& serializer) const { - serializer.write("target", targetType.getType()); + if(!serializer.getMinimalInfoEnabled()) + serializer.write("target", targetType.getType()); if (firstForward) serializer.write("forward", *firstForward); } diff --git a/source/ast/types/NetType.cpp b/source/ast/types/NetType.cpp index bc56b24e3..b1bec0d5c 100644 --- a/source/ast/types/NetType.cpp +++ b/source/ast/types/NetType.cpp @@ -135,7 +135,8 @@ const SubroutineSymbol* NetType::getResolutionFunction() const { } void NetType::serializeTo(ASTSerializer& serializer) const { - serializer.write("type", getDataType()); + if (!serializer.getMinimalInfoEnabled()) + serializer.write("type", getDataType()); } NetType& NetType::fromSyntax(const Scope& scope, const NetTypeDeclarationSyntax& syntax) { diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt index 0abaf9ec2..16c0316c8 100644 --- a/tests/unittests/CMakeLists.txt +++ b/tests/unittests/CMakeLists.txt @@ -5,6 +5,7 @@ add_executable( unittests + ast/DefaultPrinterTests.cpp ast/AssertionTests.cpp ast/ClassTests.cpp ast/ConfigTests.cpp diff --git a/tests/unittests/ast/DefaultPrinterTests.cpp b/tests/unittests/ast/DefaultPrinterTests.cpp new file mode 100644 index 000000000..6283a306b --- /dev/null +++ b/tests/unittests/ast/DefaultPrinterTests.cpp @@ -0,0 +1,563 @@ +// SPDX-FileCopyrightText: Michael Popoloski +// SPDX-License-Identifier: MIT + +#include "Test.h" +#include +#include +#include +#include + +#include "slang/ast/Statements.h" +#include "slang/ast/printer/defaultAstPrinter.h" +#include "slang/syntax/SyntaxTree.h" +#include "slang/text/Json.h" + +std::tuple getAst( + slang::ast::Compilation& compilation) { + slang::JsonWriter jsonWriter; + slang::ast::ASTSerializer jsonPrinter(compilation, jsonWriter); + const slang::ast::RootSymbol& rootAst = compilation.getRoot(); + + jsonPrinter.setIncludeAddresses(false); + jsonPrinter.setIncludeSourceInfo(false); + jsonPrinter.enableMinimalInfo(true); + + jsonPrinter.startObject(); + jsonPrinter.serialize(rootAst); + jsonPrinter.endObject(); + + return {std::string(jsonWriter.view()), rootAst}; +} + + +// checks if the ast of the original code is equal to the ast of the generated code +bool isEqual(std::shared_ptr tree, std::string name_test = "test") { + slang::ast::Compilation compilation; + compilation.addSyntaxTree(tree); + auto [old_ast_json, old_rootAst] = getAst(compilation); + + // regenerate the code + slang::ast::AstPrinter printer(compilation); + old_rootAst.visit(printer); + std::string_view new_code = printer.str(); + std::cout << new_code; + + // calculate ast new code + slang::ast::Compilation new_compilation; + auto new_tree = slang::syntax::SyntaxTree::fromText(new_code); + new_compilation.addSyntaxTree(new_tree); + auto [new_ast_json, new_rootAst] = getAst(new_compilation); + + // dump the content to a file if the ast don't match + if (true) { + name_test.append(".txt"); + std::ofstream out(name_test); + out << "original json:"; + out << old_ast_json; + out << "\nnew json:"; + out << new_ast_json; + out << "\nnew code:"; + out << new_code << "\n"; + out.close(); + } + return old_ast_json == new_ast_json; +} + +// checks if the ast of the original code is equal to the ast of the generated code +bool isEqual(std::string original_code, std::string name_test = "test") { + // calculate ast original code + auto tree = slang::syntax::SyntaxTree::fromText(original_code); + return isEqual(tree, name_test ); +} + +TEST_CASE("InstanceSymbol printer") { + std::string code = R"(module Foo; endmodule)"; + CHECK(isEqual(code)); +}; + +TEST_CASE("InstanceSymbol with ports") { + std::string code = R"( +module Foo (input a,b); +endmodule +)"; + CHECK(isEqual(code, "ports")); +}; + +TEST_CASE("InstanceSymbol with parameters") { + std::string code = R"( +module Foo #(parameter N=2, parameter DOWN=0) +(input a,input b); +endmodule +)"; + CHECK(isEqual(code, "paramas")); +}; + +TEST_CASE("InstanceSymbol with import") { + std::string code = R"( +module automatic m1 import p::*; #(int i = 1) + (a, b, , .c({a, b[0]})); + input a; + output [1:0] b; +endmodule +)"; + CHECK(isEqual(code,"import")); +}; + +TEST_CASE("BlockStatement printer") { + std::string code = R"( +module static BAR; + initial begin + fork:test_parralel + join + begin + end + end +endmodule)"; + CHECK(isEqual(code, "BlockStatement")); +}; + +TEST_CASE("all.sv 0-8") { + std::string code = R"( +timeunit 1ns / 1ps; +timeprecision 1ps; +(* foo = 1 *) package static p; +timeunit 1ns; +program; endprogram +export *::*; +endpackage +)"; + CHECK(isEqual(code)); +} + +TEST_CASE("all.sv 16-20"){ + std::string code = R"( +module m2 #(parameter i = 1, localparam j = i) + (input int a[], (* bar = "asdf" *) output wire b = 1, ref c, + interface.mod d, .e()); +endmodule)"; + CHECK(isEqual(code, "sv16_20")); +} + +TEST_CASE("all.sv 21-26") { + std::string code = R"( +extern interface I(input a, output b); + +interface I(.*); + modport mod(input a), mod2(input a); +endinterface)"; + CHECK(isEqual(code, "sv21_26")); +} + +TEST_CASE("all.sv 26-80") { + std::string code = R"( +extern interface I(input a, output b); + +interface I(.*); + modport mod(input a), mod2(input a); +endinterface + +macromodule m3; + wire b; + logic c; + m2 m(, b, c, d, ); + + //$info("Hello %s", "world"); + + I d(.a(), .b()); + I d(.a(), .b()); + + wor [1:0] w = 1; + assign (supply0, weak1) #(1:2:3, 2:1:0) w = 2; + + wor u,v; + alias {u,v} = w; + + logic f; + event ev; + initial begin + repeat (3) @(negedge b) f = #2 1; + wait (f) ++f; + wait fork; + wait_order (m3.ev) f++; + + fork : fkb + static int i = 1; + disable fork; + join_none + + //disable m3.foo; TODO invalid statements fixe + + if (1) begin end else begin end + + unique0 casez (w) + 0, 1: ; + default ; + endcase + + case (w) inside + [0: 3]: ; + endcase + end + always_ff @(posedge b iff f == 1) begin + forever break; + repeat (f + 2) continue; + while (1) + ; + for (int i = 0, j = i; i < 10; i += 2, j += i) begin end + foreach (w[q]) begin end + end + + + always @* begin : foo + end : foo + + +endmodule : m3)"; + CHECK(isEqual(code, "sv26_80")); +} + +TEST_CASE("all.sv 80-120") { + std::string code = R"( +macromodule m3; + always_comb begin + typedef union tagged { + void Invalid; + int Valid; + } VInt; + + typedef union tagged { + struct { + bit [4:0] reg1, reg2, regd; + } Add; + union tagged { + bit [9:0] JmpU; + struct { + bit [1:0] cc; + bit [9:0] addr; + } JmpC; + } Jmp; + } Instr; + + + VInt v; + Instr instr; + automatic int rf[] = new [3]; + static longint pc = 'x; + + + case (v) matches + tagged Valid .n : $display ("v is Valid with value %d", n); + endcase + + case (instr) matches + tagged Add .s: case (s) matches + '{.*, .*, 0} : ; // no op + '{.r1, .r2, .rd} : rf[rd] = rf[r1] + rf[r2]; + endcase + tagged Jmp .j: case (j) matches + tagged JmpU .a : pc = pc + a; + tagged JmpC '{.c, .a} : if (rf[c]) pc = a; + endcase + endcase + + if (instr matches (tagged Jmp .j) &&& + j matches (tagged JmpC '{cc:.c,addr:.a})) begin + pc = c[0] & a[0]; + end + else begin + end + + end +endmodule)"; + CHECK(isEqual(code, "sv80_128")); +} + +TEST_CASE("all.sv 129-150") { + std::string code = R"( +macromodule m3; + always_latch begin + end + + genvar j; + for (genvar i = 0; i < 10; i += 2) + if (i == 7) begin + end + + ; + + generate + case ($bits(w)) + 0, 1: begin end + 2: begin end + default: begin end + endcase + endgenerate + + assertion0: assert #0 (1 == 1) else $display("Hello!"); + assertion1: assume final (2 != 1) else $display("Hello!"); + +endmodule +)"; + CHECK(isEqual(code, "sv129_150")); +} + +TEST_CASE("all.sv 150_153") { + std::string code = R"( +macromodule m3; + if (1) begin + logic a,b,c,d,e,f; + + property p1(x,y); + ##1 x |-> y; + endproperty + + wire clk; + property p2; + @(posedge clk) + a ##1 (b || c)[->1] |-> + if (b) + p1(d,e) + else + f; + endproperty + end +endmodule +)"; + CHECK(isEqual(code, "sv150_153")); +} + +TEST_CASE("all.sv 193_200") { + std::string code = R"( +extern primitive prim(output reg a, input b); + +primitive prim(output reg a, input b); + table + 0 : ? : 1; + 1 : 0 : x; + endtable + +endprimitive +(* attr = 3.14 *) bind m3.m m1 #(1) bound('x, , , ); +)"; + CHECK(isEqual(code, "sv193_200")); +} + +//ast is the same but the generated source code is o low quality +TEST_CASE("all.sv 202") { + std::string code = R"(" +module m3; + module m; + endmodule +endmodule +module m1#(parameter N=2)(input q,input a,input b,input c); +endmodule +(* attr = 3.14 *) bind m3.m m1 #(1) bound('x, , , ); +)"; + CHECK(isEqual(code, "sv202")); +} + +TEST_CASE("all.sv 204_210") { + std::string code = R"(" +config cfg; + localparam i = 1; + design work.m3; + default liblist a b; + cell m3 use work.m3; +endconfig +)"; + CHECK(isEqual(code, "204_210")); +} + +TEST_CASE("all.sv 211_223") { + std::string code = R"(" +module ALU(input wire [7:0] i1, i2, input wire [2:1] opcode, output wire [7:0] o1;) + + specify + specparam s1 = 2; + if (opcode == 2'b00) (i1,i2 *> o1) = (25.0, 25.0); + if (opcode == 2'b01) (i1 => o1) = (5.6, 8.0); + if (opcode == s1) (i2 => o1) = (5.6, 8.0); + (opcode *> o1) = (6.1, 6.5); + endspecify +endmodule +)"; + CHECK(isEqual(code, "204_210")); +} + +TEST_CASE("all.sv 225_232") { + std::string code = R"( +interface Iface(); + extern function void foo(int i, real r); + extern forkjoin task t3(); + + modport m(export foo, function void bar(int, logic), task baz); + modport n(import foo, import task t3); + modport o(export t3); +endinterface + +)"; + CHECK(isEqual(code, "225_232")); +} +TEST_CASE("all.sv 250_266") { + std::string code = R"( + module m4; + Iface i1(); + n n1(i1); + + Iface i2(); + + localparam int baz = 3; + // het volgende zord niet opgeno;en in de ast + task i1.t2; + static int i = baz; + endtask + + task i2.t2; + static int i = baz; + endtask + endmodule + typedef enum { cover_none, cover_all } coverage_level; + +)"; + CHECK(isEqual(code, "250_266")); + } +TEST_CASE("all.sv 309_314") { + std::string code = R"( +module m5; + logic a, b; + assert_window1 aw1(1 + 1, a, b); +endmodule + +)"; + CHECK(isEqual(code, "309_314")); +} + + +TEST_CASE("all.sv 316_400") { + std::string code = R"( +class C; + int i; + static int j; + // extern is ingonerd in the ast ??? + //extern function int foo(int bar, int baz = 1); +endclass +class A; + integer i = 1; + integer j = 2; + function integer f(); + f = i; + endfunction +endclass + +class B extends A; + integer i = 2; + function void f(); + i = j; + super.i = super.j; + j = super.f(); + j = this.super.f(); + endfunction +endclass + +module m6; + A a = new; + A b1 = B::new; + B b2 = new; + //C2 c = new; + int depth; + integer i = b1.f(); + initial begin + b2.f(); + a = b2; + //c.i = c.j; + + randsequence(main) + main : first second; + first : add | dec := (1 + 1); + second : repeat($urandom_range(2, 6)) first; + add : if (depth < 2) first else second; + dec : case (depth & 7) + 0 : add; + 1, 2 : dec; + default : first; + endcase; + third : rand join first second; + //fourth(string s = "done") : { if (depth) break; }; + endsequence + end +endmodule +)"; + CHECK(isEqual(code, "316_4")); +} + + +TEST_CASE("all.sv 426_end") { + std::string code = R"( + class C3; + enum {red, green, blue} color; + bit [3:0] pixel_adr, pixel_offset, pixel_hue; + logic clk, x, y, c; + + covergroup g2 (string instComment) @(posedge clk); + Offset: coverpoint pixel_offset; + Hue: coverpoint pixel_hue; + AxC: cross color, pixel_adr; + all: cross color, Hue, Offset; + + option.comment = instComment; + + e: coverpoint x iff (clk) { + option.weight = 2; + wildcard bins a = { [0:63],65 }; + bins b[] = { [127:150],[148:191] }; // note overlapping values + bins c[] = { 200,201,202 }; + bins d = { [1000:$] }; + bins others[] = default; + + bins sa = (4 => 5 => 6), ([7:9],10 => 11,12); + bins sb[] = (12 => 3 [* 1]); + bins sc = (12 => 3 [-> 1]); + bins sd = (12 => 3 [= 1:2]); + } + cross e, y { + option.weight = c; + bins one = '{ '{1,2}, '{3,4}, '{5,6} }; + ignore_bins others = (!binsof(e.a) || !binsof(y) intersect {1}) with (e > 10); + } + + endgroup + endclass + )"; + CHECK(isEqual(code, "426_end")); +} + +TEST_CASE("inetTest.sv") { + std::string code = R"( +module jmagnitudeComparator(AEQB, AGTB, ALTB, A, B); + output reg AEQB, AGTB, ALTB; + input [3:0] A, B; + + always @(A,B) + begin + if( A === B ) + begin + AEQB = 1; + AGTB = 0; + ALTB = 0; + end + else if ( A > B ) + begin + AEQB = 0; + AGTB = 1; + ALTB = 0; + end + else + begin + AEQB = 0; + AGTB = 0; + ALTB = 1; + end + end +endmodule )"; + CHECK(isEqual(code, "inetTest")); +} diff --git a/tests/unittests/ast/SerializerTests.cpp b/tests/unittests/ast/SerializerTests.cpp index 6e5183b9d..3b2feb063 100644 --- a/tests/unittests/ast/SerializerTests.cpp +++ b/tests/unittests/ast/SerializerTests.cpp @@ -627,7 +627,7 @@ endclass "visibility": "Public" }, { - "name": "", + "name": "g2", "kind": "CovergroupType", "members": [ { @@ -801,7 +801,7 @@ endclass { "name": "g2", "kind": "ClassProperty", - "type": "", + "type": "g2", "lifetime": "Automatic", "flags": "const", "visibility": "Public"