summaryrefslogtreecommitdiffstats
path: root/tools/slang-cpp-extractor
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2022-03-15 10:45:24 -0400
committerGitHub <noreply@github.com>2022-03-15 10:45:24 -0400
commit8533dd2344d8be040a992a86f23e7cf696d59c4a (patch)
tree9121a5a5fa98eb4f5101421341142616ac703ba8 /tools/slang-cpp-extractor
parent06d04ab5e63ad43d7ad9de3fbc4c8ed64b13265a (diff)
C++ extractor callable support (#2159)
* #include an absolute path didn't work - because paths were taken to always be relative. * Split doc extractor such that can be used in C++ extractor. * Compiles. Update the stdlib docs. * Fix issue on release builds. * Add support for extracting documentation to C++ extractor. * Dump out markup. Make enum value backing type take tokens. * Node::Type -> Node::Kind * More improvements around Node::Type -> Node::Kind * Support for parsing callable types. * Fix issue params for callable, and default value for variable. * Add support for static. * Improve handling parsing of contained types. * Small improvements around template consumption. * Improve dumping with markup/static. * Small improvements around reflection. * Add more flexible handling of markers. Allow reflection without markers.
Diffstat (limited to 'tools/slang-cpp-extractor')
-rw-r--r--tools/slang-cpp-extractor/cpp-extractor-main.cpp4
-rw-r--r--tools/slang-cpp-extractor/diagnostic-defs.h6
-rw-r--r--tools/slang-cpp-extractor/identifier-lookup.cpp14
-rw-r--r--tools/slang-cpp-extractor/identifier-lookup.h7
-rw-r--r--tools/slang-cpp-extractor/macro-writer.cpp7
-rw-r--r--tools/slang-cpp-extractor/node.cpp80
-rw-r--r--tools/slang-cpp-extractor/node.h49
-rw-r--r--tools/slang-cpp-extractor/parser.cpp730
-rw-r--r--tools/slang-cpp-extractor/parser.h17
-rw-r--r--tools/slang-cpp-extractor/unit-test.cpp26
10 files changed, 774 insertions, 166 deletions
diff --git a/tools/slang-cpp-extractor/cpp-extractor-main.cpp b/tools/slang-cpp-extractor/cpp-extractor-main.cpp
index 9db61f8e0..371392851 100644
--- a/tools/slang-cpp-extractor/cpp-extractor-main.cpp
+++ b/tools/slang-cpp-extractor/cpp-extractor-main.cpp
@@ -107,6 +107,10 @@ static DocMarkupExtractor::SearchStyle _getSearchStyle(Node* node)
{
return SearchStyle::Variable;
}
+ case Node::Kind::Callable:
+ {
+ return SearchStyle::Before;
+ }
default: break;
}
diff --git a/tools/slang-cpp-extractor/diagnostic-defs.h b/tools/slang-cpp-extractor/diagnostic-defs.h
index ba43f1844..c7e364291 100644
--- a/tools/slang-cpp-extractor/diagnostic-defs.h
+++ b/tools/slang-cpp-extractor/diagnostic-defs.h
@@ -21,6 +21,7 @@
DIAGNOSTIC(-1, Note, seeDeclarationOf, "see declaration of '$0'")
DIAGNOSTIC(-1, Note, seeOpen, "see open $0")
DIAGNOSTIC(-1, Note, commandLine, "Command line: $0")
+DIAGNOSTIC(-1, Note, previousLocation, "previous location")
DIAGNOSTIC(1, Error, cannotOpenFile, "cannot open file '$0'.")
DIAGNOSTIC(1, Error, errorAccessingFile, "error accessing file '$0'.")
@@ -46,6 +47,11 @@ DIAGNOSTIC(100012, Error, typeInDifferentTypeSet, "Type $0 in different type set
DIAGNOSTIC(100013, Error, expectingIdentifier, "Expecting an identifier, found $0")
DIAGNOSTIC(100014, Error, cannotDeclareTypeInScope, "Cannot declare types in this scope")
DIAGNOSTIC(100015, Error, identifierAlreadyDefined, "Identifier already defined '$0'")
+DIAGNOSTIC(100016, Error, expectingType, "Expecting a type")
+DIAGNOSTIC(100017, Error, cannotParseExpression, "Cannot parse expression")
+DIAGNOSTIC(100018, Error, cannotOverload, "Cannot overload this declaration");
+DIAGNOSTIC(100019, Error, classMarkerOutsideOfClass, "A class/struct marker is found outside of class or struct");
+DIAGNOSTIC(100020, Error, classMarkerAlreadyFound, "A class/struct marker already found in type '$0'");
// Command line errors 100100
diff --git a/tools/slang-cpp-extractor/identifier-lookup.cpp b/tools/slang-cpp-extractor/identifier-lookup.cpp
index c7be75d82..db8ea2846 100644
--- a/tools/slang-cpp-extractor/identifier-lookup.cpp
+++ b/tools/slang-cpp-extractor/identifier-lookup.cpp
@@ -22,6 +22,11 @@ using namespace Slang;
IdentifierFlag::Keyword, /// Access
IdentifierFlag::Reflection, /// Reflected
IdentifierFlag::Reflection, /// Unreflected
+
+ IdentifierFlag::Keyword, /// virtual
+ 0, /// Calling convention
+ IdentifierFlag::Keyword, /// template
+ IdentifierFlag::Keyword, /// static
};
void IdentifierLookup::set(const UnownedStringSlice& name, IdentifierStyle style)
@@ -63,7 +68,7 @@ void IdentifierLookup::initDefault(const UnownedStringSlice& markPrefix)
// Some keywords
{
- const char* names[] = { "virtual", "continue", "if", "case", "break", "catch", "default", "delete", "do", "else", "for", "new", "goto", "return", "switch", "throw", "using", "while", "operator" };
+ const char* names[] = { "continue", "if", "case", "break", "catch", "default", "delete", "do", "else", "for", "new", "goto", "return", "switch", "throw", "using", "while", "operator", "explicit"};
set(names, SLANG_COUNT_OF(names), IdentifierStyle::Keyword);
}
@@ -88,6 +93,13 @@ void IdentifierLookup::initDefault(const UnownedStringSlice& markPrefix)
}
}
+ {
+ set("virtual", IdentifierStyle::Virtual);
+ set("SLANG_MCALL", IdentifierStyle::CallingConvention);
+ set("template", IdentifierStyle::Template);
+ set("static", IdentifierStyle::Static);
+ }
+
// Keywords which introduce types/scopes
{
const Pair pairs[] =
diff --git a/tools/slang-cpp-extractor/identifier-lookup.h b/tools/slang-cpp-extractor/identifier-lookup.h
index 3cee909ef..40644a1ba 100644
--- a/tools/slang-cpp-extractor/identifier-lookup.h
+++ b/tools/slang-cpp-extractor/identifier-lookup.h
@@ -30,6 +30,13 @@ enum class IdentifierStyle
Reflected,
Unreflected,
+ CallingConvention, ///< Used on a method
+ Virtual, ///<
+
+ Template,
+
+ Static,
+
CountOf,
};
diff --git a/tools/slang-cpp-extractor/macro-writer.cpp b/tools/slang-cpp-extractor/macro-writer.cpp
index c4245a646..0f217cd4e 100644
--- a/tools/slang-cpp-extractor/macro-writer.cpp
+++ b/tools/slang-cpp-extractor/macro-writer.cpp
@@ -120,13 +120,16 @@ SlangResult MacroWriter::calcChildrenHeader(NodeTree* tree, TypeSet* typeSet, St
// Define the derived types
out << "#define " << m_options->m_markPrefix << "FIELDS_" << reflectTypeName << "_" << classNode->m_name.getContent() << "(_x_, _param_)";
- // Find all of the fields
+ // Find all of the instance fields fields
List<FieldNode*> fields;
for (Node* child : classNode->m_children)
{
if (auto field = as<FieldNode>(child))
{
- fields.add(field);
+ if (!field->m_isStatic)
+ {
+ fields.add(field);
+ }
}
}
diff --git a/tools/slang-cpp-extractor/node.cpp b/tools/slang-cpp-extractor/node.cpp
index 2074b7c41..3fbbf9c56 100644
--- a/tools/slang-cpp-extractor/node.cpp
+++ b/tools/slang-cpp-extractor/node.cpp
@@ -294,7 +294,7 @@ ScopeNode* ScopeNode::getAnonymousNamespace()
return m_anonymousNamespace;
}
-void ScopeNode::addChild(Node* child)
+void ScopeNode::addChildIgnoringName(Node* child)
{
SLANG_ASSERT(child->m_parentScope == nullptr);
// Can't add anonymous namespace this way - should be added via getAnonymousNamespace
@@ -302,6 +302,11 @@ void ScopeNode::addChild(Node* child)
child->m_parentScope = this;
m_children.add(child);
+}
+
+void ScopeNode::addChild(Node* child)
+{
+ addChildIgnoringName(child);
if (child->m_name.hasContent())
{
@@ -316,9 +321,6 @@ Node* ScopeNode::findChild(const UnownedStringSlice& name) const
{
return *nodePtr;
}
-
-
-
return nullptr;
}
@@ -377,8 +379,7 @@ static bool _needsSpace(const Token& prevTok, const Token& tok)
auto loc = tok.getLoc();
auto prevContent = prevTok.getContent();
- auto content = tok.getContent();
-
+
if (prevLoc + prevContent.getLength() == loc)
{
return false;
@@ -494,17 +495,76 @@ void EnumNode::dump(int indent, StringBuilder& out)
out << "}\n";
}
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! CallableNode !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void CallableNode::dump(int indent, StringBuilder& out)
+{
+ if (!isReflected())
+ {
+ return;
+ }
+
+ dumpMarkup(indent, out);
+
+ _indent(indent, out);
+
+ if (m_isStatic)
+ {
+ out << "static ";
+ }
+ if (m_isVirtual)
+ {
+ out << "virtual ";
+ }
+
+ out << m_returnType << " ";
+ out << m_name.getContent() << "(";
+
+ const Index count = m_params.getCount();
+ for (Index i = 0; i < count; ++i)
+ {
+ if (i > 0)
+ {
+ out << ", ";
+ }
+
+ const auto& param = m_params[i];
+ out << param.m_type;
+ if (param.m_name.type == TokenType::Identifier)
+ {
+ out << " " << param.m_name.getContent();
+ }
+ }
+
+ out << ")";
+
+ if (m_isPure)
+ {
+ out << " = 0";
+ }
+
+ out << "\n";
+}
+
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! FieldNode !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
void FieldNode::dump(int indent, StringBuilder& out)
{
- if (isReflected())
+ if (!isReflected())
{
- dumpMarkup(indent, out);
+ return;
+ }
- _indent(indent, out);
- out << m_fieldType << " " << m_name.getContent() << "\n";
+ dumpMarkup(indent, out);
+
+ _indent(indent, out);
+
+ if (m_isStatic)
+ {
+ out << "static ";
}
+
+ out << m_fieldType << " " << m_name.getContent() << "\n";
}
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ClassLikeNode !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
diff --git a/tools/slang-cpp-extractor/node.h b/tools/slang-cpp-extractor/node.h
index f649f1adb..db9a10e36 100644
--- a/tools/slang-cpp-extractor/node.h
+++ b/tools/slang-cpp-extractor/node.h
@@ -41,6 +41,11 @@ public:
TypeDef,
+ Callable, ///< Functions/methods
+
+ Other, ///< Used 'other' parsing like for TYPE
+ Unknown, ///< Used for marking tokens consumed but usage is not known
+
CountOf,
};
@@ -64,7 +69,7 @@ public:
/// Returns true if kind can cast to this type
/// Used for implementing as<T> casting
- static bool isOfKind(Kind type) { return true; }
+ static bool isOfKind(Kind kind) { SLANG_UNUSED(kind); return true; }
static bool isKindScope(Kind kind) { return int(kind) >= int(KindRange::ScopeStart) && int(kind) <= int(KindRange::ScopeEnd); }
static bool isKindClassLike(Kind kind) { return int(kind) >= int(KindRange::ClassLikeStart) && int(kind) <= int(KindRange::ClassLikeEnd); }
@@ -95,7 +100,10 @@ public:
return false;
}
+ bool isNamespace() const { return m_kind == Kind::Namespace; }
bool isClassLike() const { return isKindClassLike(m_kind); }
+ bool isScope() const { return isKindScope(m_kind); }
+ bool isEnumLike() const { return isKindEnumLike(m_kind); }
/// These are useful for the filter
static bool isClassLikeAndReflected(Node* node) { return node->isClassLike() && node->isReflected(); }
@@ -175,8 +183,12 @@ struct ScopeNode : public Node
virtual void dump(int indent, StringBuilder& out) SLANG_OVERRIDE;
virtual void calcScopeDepthFirst(List<Node*>& outNodes) SLANG_OVERRIDE;
+ /// True if can contain callable entries
+ bool canContainCallable() const { return isClassLike() || isNamespace(); }
+
/// True if can accept fields (class like types can)
bool canContainFields() const { return isClassLike(); }
+
/// True if the scope can accept types
bool canContainTypes() const { return canKindContainTypes(m_kind); }
@@ -185,6 +197,8 @@ struct ScopeNode : public Node
/// Add a child node to this nodes scope
void addChild(Node* child);
+ /// Adds the child but does not add the name to the map
+ void addChildIgnoringName(Node* child);
/// Find a child node in this scope with the specified name. Return nullptr if not found
Node* findChild(const UnownedStringSlice& name) const;
@@ -216,7 +230,7 @@ struct FieldNode : public Node
{
typedef Node Super;
- static bool isOfKind(Kind type) { return type == Kind::Field; }
+ static bool isOfKind(Kind kind) { return kind == Kind::Field; }
virtual void dump(int indent, StringBuilder& out) SLANG_OVERRIDE;
@@ -227,7 +241,9 @@ struct FieldNode : public Node
UnownedStringSlice m_fieldType;
- // We may want to add initializer tokens
+ bool m_isStatic = false;
+
+ /// TODO(JS): We may want to add initializer tokens
};
struct ClassLikeNode : public ScopeNode
@@ -281,6 +297,33 @@ struct ClassLikeNode : public ScopeNode
ClassLikeNode* m_superNode; ///< If this is a class/struct, the type it is derived from (or nullptr if base)
};
+struct CallableNode : public Node
+{
+ typedef Node Super;
+
+ static bool isOfKind(Kind kind) { return kind == Kind::Callable; }
+
+ virtual void dump(int indent, StringBuilder& out) SLANG_OVERRIDE;
+
+ CallableNode() :Super(Kind::Callable) {}
+
+ struct Param
+ {
+ UnownedStringSlice m_type;
+ Token m_name;
+ };
+
+ UnownedStringSlice m_returnType;
+
+ CallableNode* m_nextOverload = nullptr;
+
+ List<Param> m_params;
+
+ bool m_isStatic = false;
+ bool m_isVirtual = false;
+ bool m_isPure = false;
+};
+
struct EnumCaseNode : public Node
{
typedef Node Super;
diff --git a/tools/slang-cpp-extractor/parser.cpp b/tools/slang-cpp-extractor/parser.cpp
index 29bc517f5..3f87344ed 100644
--- a/tools/slang-cpp-extractor/parser.cpp
+++ b/tools/slang-cpp-extractor/parser.cpp
@@ -5,6 +5,7 @@
#include "../../source/compiler-core/slang-name-convention-util.h"
+#include "../../source/core/slang-string-util.h"
#include "../../source/core/slang-io.h"
namespace CppExtract {
@@ -33,6 +34,8 @@ Parser::Parser(NodeTree* nodeTree, DiagnosticSink* sink) :
// Node::Type::TypeDef,
// Node::Type::Enum,
// Node::Type::EnumClass,
+
+ Node::Kind::Callable,
};
setKindsEnabled(defaultEnabled, SLANG_COUNT_OF(defaultEnabled));
}
@@ -215,7 +218,7 @@ SlangResult Parser::_maybeConsumeScope()
}
else if (type == TokenType::LBrace)
{
- m_reader.advanceToken();
+ //m_reader.advanceToken();
return consumeToClosingBrace();
}
else if (type == TokenType::EndOfFile)
@@ -238,6 +241,7 @@ SlangResult Parser::consumeToClosingBrace(const Token* inOpenBraceToken)
{
openToken = m_reader.advanceToken();
}
+ SLANG_ASSERT(openToken.type == TokenType::LBrace);
while (true)
{
@@ -310,6 +314,7 @@ SlangResult Parser::_parseEnum()
RefPtr<EnumNode> node = new EnumNode(kind);
node->m_name = nameToken;
+ node->m_reflectionType = m_currentScope->getContainedReflectionType();
if (advanceIfToken(TokenType::Colon))
{
@@ -382,28 +387,15 @@ SlangResult Parser::_parseEnum()
return SLANG_FAIL;
}
+ caseNode->m_reflectionType = m_currentScope->getContainedReflectionType();
+
// Add the value
node->addChild(caseNode);
- // TODO(JS):
- // This could be better. We could lookup the value etc. For now just assume only one token is valid.
-
if (advanceIfToken(TokenType::OpAssign))
{
List<Token> valueTokens;
- // Consume up to } or ,
- while (true)
- {
- TokenType assignType = m_reader.peekTokenType();
-
- if (assignType == TokenType::Comma ||
- assignType == TokenType::RBrace ||
- assignType == TokenType::EndOfFile)
- {
- break;
- }
- valueTokens.add(m_reader.advanceToken());
- }
+ SLANG_RETURN_ON_FAIL(_parseExpression(valueTokens));
if (valueTokens.getCount() > 0)
{
@@ -427,6 +419,97 @@ SlangResult Parser::_parseEnum()
return popScope();
}
+SlangResult Parser::_consumeTemplate()
+{
+ // Skip the current 'template' token.
+ m_reader.advanceToken();
+
+ // Consume everything in <>
+ SLANG_RETURN_ON_FAIL(expect(TokenType::OpLess));
+
+ {
+ Index arrowCount = 1;
+ while (true)
+ {
+ auto tokenType = m_reader.peekTokenType();
+
+ if (tokenType == TokenType::OpLess)
+ {
+ m_reader.advanceToken();
+ arrowCount++;
+ }
+ else if (tokenType == TokenType::OpGreater)
+ {
+ m_reader.advanceToken();
+ if (arrowCount == 1)
+ {
+ break;
+ }
+ --arrowCount;
+ }
+ else if (tokenType == TokenType::OpRsh)
+ {
+ if (arrowCount < 2)
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::unexpectedTemplateClose);
+ return SLANG_FAIL;
+ }
+ m_reader.advanceToken();
+ if (arrowCount == 2)
+ {
+ break;
+ }
+ arrowCount -= 2;
+ }
+ else if (tokenType == TokenType::EndOfFile)
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::unexpectedEndOfFile);
+ return SLANG_FAIL;
+ }
+ else
+ {
+ m_reader.advanceToken();
+ }
+ }
+ }
+
+ // Search for { or ; to consume remaining
+ while (true)
+ {
+ auto tokenType = m_reader.peekTokenType();
+
+ switch (tokenType)
+ {
+ case TokenType::EndOfFile:
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::unexpectedEndOfFile);
+ return SLANG_FAIL;
+ }
+ case TokenType::Semicolon:
+ {
+ // Ends with semicolon if it's a template pre-declaration
+ m_reader.advanceToken();
+ return SLANG_OK;
+ }
+ case TokenType::LBrace:
+ {
+ // If ends with {, means could be body of a struct/class or a body of a function/method.
+ // Consume it
+ SLANG_RETURN_ON_FAIL(consumeToClosingBrace());
+ // If we hit a ; just consume and ignore
+ advanceIfToken(TokenType::Semicolon);
+ return SLANG_OK;
+ }
+ default:
+ {
+ // Consume
+ m_reader.advanceToken();
+ break;
+ }
+ }
+ }
+}
+
SlangResult Parser::_maybeParseNode(Node::Kind kind)
{
// We are looking for
@@ -449,6 +532,8 @@ SlangResult Parser::_maybeParseNode(Node::Kind kind)
// Okay looks like we are opening a namespace
RefPtr<ScopeNode> node(new ScopeNode(Node::Kind::Namespace));
node->m_name = name;
+
+ node->m_reflectionType = m_currentScope->getContainedReflectionType();
// Push the node
return pushScope(node);
}
@@ -482,8 +567,16 @@ SlangResult Parser::_maybeParseNode(Node::Kind kind)
RefPtr<ClassLikeNode> node(new ClassLikeNode(kind));
node->m_name = name;
- // Defaults to not reflected
- SLANG_ASSERT(!node->isReflected());
+ // We default to the containing scope for reflection type.
+ if (!m_requireMarker)
+ {
+ node->m_reflectionType = m_currentScope->getContainedReflectionType();
+ }
+ else
+ {
+ // Defaults to not reflected
+ SLANG_ASSERT(!node->isReflected());
+ }
if (advanceIfToken(TokenType::Colon))
{
@@ -496,6 +589,8 @@ SlangResult Parser::_maybeParseNode(Node::Kind kind)
}
}
+ // We only accept a single super class. Consume everything afterwards until we hit the { brace
+
if (m_reader.peekTokenType() != TokenType::LBrace)
{
// Consume up until we see a brace else it's an error
@@ -518,89 +613,12 @@ SlangResult Parser::_maybeParseNode(Node::Kind kind)
return pushScope(node);
}
- Token braceToken = m_reader.advanceToken();
-
- while (true)
- {
- // Okay now we are looking for the markers, or visibility qualifiers
- if (advanceIfStyle(IdentifierStyle::Access))
- {
- // Consume it and a colon
- if (SLANG_FAILED(expect(TokenType::Colon)))
- {
- consumeToClosingBrace(&braceToken);
- return SLANG_OK;
- }
- continue;
- }
-
- switch (m_reader.peekTokenType())
- {
- case TokenType::Identifier: break;
- case TokenType::RBrace:
- {
- SLANG_RETURN_ON_FAIL(pushScope(node));
- SLANG_RETURN_ON_FAIL(popScope());
- m_reader.advanceToken();
- return SLANG_OK;
- }
- default:
- {
- SLANG_RETURN_ON_FAIL(pushScope(node));
- return SLANG_OK;
- }
- }
-
- // If it's one of the markers, then we continue to extract parameter
- if (advanceIfMarker(&node->m_marker))
- {
- break;
- }
-
- // We still need to add the node,
- SLANG_RETURN_ON_FAIL(pushScope(node));
- return SLANG_OK;
- }
-
- // Let's extract the type set
- {
- UnownedStringSlice slice(node->m_marker.getContent());
-
- SLANG_ASSERT(_isMarker(slice));
-
- // Strip the prefix and suffix
- slice = UnownedStringSlice(slice.begin() + m_options->m_markPrefix.getLength(), slice.end() - m_options->m_markSuffix.getLength());
-
- // Strip ABSTRACT_ if it's there
- UnownedStringSlice abstractSlice("ABSTRACT_");
- if (slice.startsWith(abstractSlice))
- {
- slice = UnownedStringSlice(slice.begin() + abstractSlice.getLength(), slice.end());
- }
-
- // TODO: We could strip other stuff or have other heuristics there, but this is
- // probably okay for now
-
- // Set the typeSet
- node->m_typeSet = m_nodeTree->getOrAddTypeSet(slice);
- }
-
- // Okay now looking for ( identifier)
- Token typeNameToken;
-
- SLANG_RETURN_ON_FAIL(expect(TokenType::LParent));
- SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &typeNameToken));
- SLANG_RETURN_ON_FAIL(expect(TokenType::RParent));
+ const Token braceToken = m_reader.advanceToken();
- if (typeNameToken.getContent() != node->m_name.getContent())
- {
- m_sink->diagnose(typeNameToken, CPPDiagnostics::typeNameDoesntMatch, node->m_name.getContent());
- return SLANG_FAIL;
- }
-
- node->m_reflectionType = ReflectionType::Reflected;
+ // Push the class scope
return pushScope(node);
}
+
SlangResult Parser::_consumeToSync()
{
@@ -776,33 +794,55 @@ UnownedStringSlice Parser::_concatTokens(TokenReader::ParsingCursor start)
SlangResult Parser::_maybeParseType(Index& ioTemplateDepth)
{
- _consumeTypeModifiers();
-
- advanceIfToken(TokenType::Scope);
while (true)
{
- Token identifierToken;
- if (!advanceIfToken(TokenType::Identifier, &identifierToken))
+ if (m_reader.peekTokenType() == TokenType::Identifier)
{
- return SLANG_FAIL;
+ const IdentifierStyle style = m_nodeTree->m_identifierLookup->get(m_reader.peekToken().getContent());
+
+ if (style != IdentifierStyle::TypeModifier && hasFlag(style, IdentifierFlag::Keyword))
+ {
+ return SLANG_FAIL;
+ }
}
- const IdentifierStyle style = m_nodeTree->m_identifierLookup->get(identifierToken.getContent());
- if (hasFlag(style, IdentifierFlag::Keyword))
+ _consumeTypeModifiers();
+
+ advanceIfToken(TokenType::Scope);
+ while (true)
{
- return SLANG_FAIL;
+ Token identifierToken;
+ if (!advanceIfToken(TokenType::Identifier, &identifierToken))
+ {
+ return SLANG_FAIL;
+ }
+
+ const IdentifierStyle style = m_nodeTree->m_identifierLookup->get(identifierToken.getContent());
+ if (hasFlag(style, IdentifierFlag::Keyword))
+ {
+ return SLANG_FAIL;
+ }
+
+ if (advanceIfToken(TokenType::Scope))
+ {
+ continue;
+ }
+ break;
}
- if (advanceIfToken(TokenType::Scope))
+ if (m_reader.peekTokenType() == TokenType::OpLess)
{
+ SLANG_RETURN_ON_FAIL(_maybeParseTemplateArgs(ioTemplateDepth));
+ }
+
+ if (m_reader.peekTokenType() == TokenType::Scope)
+ {
+ // Skip the scope and repeat
+ m_reader.advanceToken();
continue;
}
- break;
- }
- if (m_reader.peekTokenType() == TokenType::OpLess)
- {
- SLANG_RETURN_ON_FAIL(_maybeParseTemplateArgs(ioTemplateDepth));
+ break;
}
// Strip all the consts etc modifiers
@@ -856,6 +896,61 @@ SlangResult Parser::_maybeParseType(List<Token>& outToks)
return SLANG_OK;
}
+SlangResult Parser::_parseMarker()
+{
+ SLANG_ASSERT(m_reader.peekTokenType() == TokenType::Identifier &&
+ _isMarker(m_reader.peekToken().getContent()) &&
+ m_currentScope->isClassLike());
+
+ ClassLikeNode* node = as<ClassLikeNode>(m_currentScope);
+
+ if (node->m_marker.type != TokenType::Unknown)
+ {
+ m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::classMarkerAlreadyFound, node->m_name.getContent());
+ m_sink->diagnose(node->m_marker, CPPDiagnostics::previousLocation);
+ return SLANG_FAIL;
+ }
+
+ // Set the marker token.
+ node->m_marker = m_reader.advanceToken();
+
+ // Looks like it's a marker
+ UnownedStringSlice slice(node->m_marker.getContent());
+
+ // Strip the prefix and suffix
+ slice = UnownedStringSlice(slice.begin() + m_options->m_markPrefix.getLength(), slice.end() - m_options->m_markSuffix.getLength());
+
+ // Strip ABSTRACT_ if it's there
+ UnownedStringSlice abstractSlice("ABSTRACT_");
+ if (slice.startsWith(abstractSlice))
+ {
+ slice = UnownedStringSlice(slice.begin() + abstractSlice.getLength(), slice.end());
+ }
+
+ // TODO: We could strip other stuff or have other heuristics there, but this is
+ // probably okay for now
+
+ // Set the typeSet
+ node->m_typeSet = m_nodeTree->getOrAddTypeSet(slice);
+
+ // Okay now looking for ( identifier)
+ Token typeNameToken;
+
+ SLANG_RETURN_ON_FAIL(expect(TokenType::LParent));
+ SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &typeNameToken));
+ SLANG_RETURN_ON_FAIL(expect(TokenType::RParent));
+
+ if (typeNameToken.getContent() != node->m_name.getContent())
+ {
+ m_sink->diagnose(typeNameToken, CPPDiagnostics::typeNameDoesntMatch, node->m_name.getContent());
+ return SLANG_FAIL;
+ }
+
+ // If has the marker it is assumed reflected
+ node->m_reflectionType = ReflectionType::Reflected;
+ return SLANG_OK;
+}
+
SlangResult Parser::_maybeParseType(UnownedStringSlice& outType)
{
auto startCursor = m_reader.getCursor();
@@ -965,6 +1060,130 @@ SlangResult Parser::_parseBalanced(DiagnosticSink* sink)
}
}
+SlangResult Parser::_consumeBalancedParens()
+{
+ SLANG_ASSERT(m_reader.peekTokenType() == TokenType::LParent);
+
+ Index parenCount = 0;
+
+ while (true)
+ {
+ const TokenType tokenType = m_reader.peekTokenType();
+
+ switch (tokenType)
+ {
+ case TokenType::LParent:
+ {
+ parenCount++;
+ break;
+ }
+ case TokenType::RParent:
+ {
+ --parenCount;
+ // If no more parens then we are done
+ if (parenCount == 0)
+ {
+ m_reader.advanceToken();
+ return SLANG_OK;
+ }
+ break;
+ }
+ case TokenType::EndOfFile:
+ {
+ // If we hit the end of the file, then not balanced
+ return SLANG_FAIL;
+ }
+ default: break;
+ }
+
+ m_reader.advanceToken();
+ }
+}
+
+SlangResult Parser::_parseExpression(List<Token>& outExprTokens)
+{
+ Index parenCount = 0;
+ Index bracketCount = 0;
+
+ // TODO(JS): NOTE! This doesn't handle an expression that contains a template params in Something<Arg1, 3>,
+ // because without knowing what Something is, it's not known if < is a comparison or or a 'template' bracket
+ //
+ // This can be worked around in the originating source by placing in parens
+
+ while (true)
+ {
+ TokenType tokenType = m_reader.peekTokenType();
+
+ switch (tokenType)
+ {
+ case TokenType::LParent:
+ {
+ parenCount++;
+ break;
+ }
+ case TokenType::RParent:
+ {
+ // If no parens, and nothing else is open then we are done
+ if (parenCount == 0)
+ {
+ if (bracketCount)
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotParseExpression);
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+ }
+ --parenCount;
+ break;
+ }
+ case TokenType::LBracket:
+ {
+ bracketCount++;
+ break;
+ }
+ case TokenType::RBracket:
+ {
+ // If no brackets are open we are done
+ if (bracketCount == 0)
+ {
+ if (parenCount)
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotParseExpression);
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
+ }
+ --bracketCount;
+ break;
+ }
+ case TokenType::EndOfFile:
+ {
+ if ((bracketCount | parenCount) == 0)
+ {
+ return SLANG_OK;
+ }
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotParseExpression);
+ return SLANG_FAIL;
+ }
+ case TokenType::RBrace:
+ case TokenType::Semicolon:
+ case TokenType::Comma:
+ {
+ if ((bracketCount | parenCount) == 0)
+ {
+ return SLANG_OK;
+ }
+ break;
+ }
+
+ default: break;
+ }
+
+ outExprTokens.add(m_reader.advanceToken());
+ }
+}
+
SlangResult Parser::_parseTypeDef()
{
if (!m_currentScope->canContainTypes())
@@ -995,6 +1214,7 @@ SlangResult Parser::_parseTypeDef()
RefPtr<TypeDefNode> node = new TypeDefNode;
node->m_name = name;
+ node->m_reflectionType = m_currentScope->getContainedReflectionType();
// Set what aliases too
node->m_targetTypeTokens.swapWith(toks);
@@ -1004,10 +1224,35 @@ SlangResult Parser::_parseTypeDef()
return SLANG_OK;
}
-SlangResult Parser::_maybeParseField()
+SlangResult Parser::_maybeParseContained(Node** outNode)
{
- // Can only add a field if we are in a class
- SLANG_ASSERT(m_currentScope->isClassLike());
+ *outNode = nullptr;
+
+ bool isStatic = false;
+ bool isVirtual = false;
+
+ while (m_reader.peekTokenType() == TokenType::Identifier)
+ {
+ const IdentifierStyle style = m_nodeTree->m_identifierLookup->get(m_reader.peekToken().getContent());
+
+ // Check for virtualness
+ if (style == IdentifierStyle::Virtual)
+ {
+ isVirtual = true;
+ m_reader.advanceToken();
+ continue;
+ }
+
+ // Check if static
+ if (style == IdentifierStyle::Static)
+ {
+ isStatic = true;
+ m_reader.advanceToken();
+ continue;
+ }
+
+ break;
+ }
UnownedStringSlice typeName;
if (SLANG_FAILED(_maybeParseType(typeName)))
@@ -1021,64 +1266,234 @@ SlangResult Parser::_maybeParseField()
return SLANG_OK;
}
- if (m_reader.peekTokenType() != TokenType::Identifier)
+ // Has a calling convention (must be a function/method)
+ Token callingConventionToken;
+ advanceIfStyle(IdentifierStyle::CallingConvention, &callingConventionToken);
+
+ // Expecting a name
+ Token nameToken;
+ if (!advanceIfToken(TokenType::Identifier, &nameToken))
{
_consumeToSync();
return SLANG_OK;
}
- Token fieldName = m_reader.advanceToken();
-
- if (m_reader.peekTokenType() == TokenType::LBracket)
+ // Handles other scenarios, but here for catching operator overloading
{
- auto startCursor = m_reader.getCursor();
-
- // If it's not balanced we just assume it's not correct - and ignore
- if (SLANG_FAILED(_parseBalanced(nullptr)))
+ const auto style = m_nodeTree->m_identifierLookup->get(nameToken.getContent());
+ if (style != IdentifierStyle::None)
{
_consumeToSync();
return SLANG_OK;
}
+ }
- UnownedStringSlice arraySuffix = _concatTokens(startCursor);
+ if (m_reader.peekTokenType() == TokenType::LParent)
+ {
+ if (!m_currentScope->canContainCallable())
+ {
+ SLANG_RETURN_ON_FAIL(_consumeBalancedParens());
+ // Consume everything up to ; or {
+ SLANG_RETURN_ON_FAIL(_consumeToSync());
+
+ return SLANG_OK;
+ }
- // The overall type is the typename concated with the arraySuffix
- StringBuilder buf;
- buf << typeName << arraySuffix;
+ // Looks like it's a callable
+ m_reader.advanceToken();
- StringSlicePool* typePool = m_nodeTree->m_typePool;
+ List<CallableNode::Param> params;
- typeName = typePool->getSlice(typePool->add(buf));
- }
+ if (m_reader.peekTokenType() != TokenType::RParent)
+ {
+ while (true)
+ {
+ UnownedStringSlice type;
+ SlangResult res = _maybeParseType(type);
- switch (m_reader.peekTokenType())
- {
- case TokenType::OpAssign:
+ if (SLANG_FAILED(res))
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::expectingType);
+ return res;
+ }
+
+ Token paramName;
+ if (m_reader.peekTokenType() == TokenType::Identifier)
+ {
+ paramName = m_reader.advanceToken();
+ }
+
+ // Check if we have a default value
+ auto peekType = m_reader.peekTokenType();
+
+ if (peekType == TokenType::OpAssign)
+ {
+ m_reader.advanceToken();
+
+ List<Token> exprTokens;
+ SLANG_RETURN_ON_FAIL(_parseExpression(exprTokens));
+
+ // Update the peek type
+ peekType = m_reader.peekTokenType();
+ }
+
+ CallableNode::Param param;
+ param.m_name = nameToken;
+ param.m_type = type;
+
+ params.add(param);
+
+ if (peekType == TokenType::RParent)
+ {
+ break;
+ }
+ if (peekType == TokenType::Comma)
+ {
+ m_reader.advanceToken();
+ continue;
+ }
+
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::expectingToken, ", or ) or =");
+ return SLANG_FAIL;
+ }
+ }
+
+ // Skip )
+ m_reader.advanceToken();
+
+ // Parse suffix
+ bool isPure = false;
+
+ // const?
+ _consumeTypeModifiers();
+
+ // = 0 ?
+ if (m_reader.peekTokenType() == TokenType::OpAssign)
{
- // Special case to handle
- // Type operator=(...
+ m_reader.advanceToken();
+ if (m_reader.peekTokenType() == TokenType::IntegerLiteral)
+ {
+ Int value = -1;
+ if (SLANG_SUCCEEDED(StringUtil::parseInt(m_reader.peekToken().getContent(), value)) &&
+ value == 0)
+ {
+ isPure = true;
+ m_reader.advanceToken();
+ }
+ else
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::expectingToken, "0");
+ return SLANG_FAIL;
+ }
+ }
+ }
+ if (m_reader.peekTokenType() == TokenType::Semicolon)
+ {
m_reader.advanceToken();
- if (m_reader.peekTokenType() == TokenType::LParent)
+ }
+ else if (m_reader.peekTokenType() == TokenType::LBrace)
+ {
+ SLANG_RETURN_ON_FAIL(consumeToClosingBrace());
+ }
+ else
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::expectingToken, "; or {");
+ return SLANG_FAIL;
+ }
+
+ RefPtr<CallableNode> callableNode = new CallableNode;
+
+ callableNode->m_returnType = typeName;
+ callableNode->m_name = nameToken;
+ callableNode->m_reflectionType = m_currentScope->getContainedReflectionType();
+
+ callableNode->m_isVirtual = isVirtual;
+ callableNode->m_isPure = isPure;
+ callableNode->m_isStatic = isStatic;
+
+ callableNode->m_params.swapWith(params);
+
+ Node* nodeWithName = m_currentScope->findChild(nameToken.getContent());
+
+ if (nodeWithName)
+ {
+ CallableNode* initialOverload = as<CallableNode>(nodeWithName);
+ if (!initialOverload)
{
- // Not a field
- break;
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotOverload);
+ m_sink->diagnose(nodeWithName->getSourceLoc(), CPPDiagnostics::seeDeclarationOf);
+ return SLANG_FAIL;
+ }
+
+ callableNode->m_nextOverload = initialOverload->m_nextOverload;
+ initialOverload->m_nextOverload = initialOverload;
+
+ m_currentScope->addChildIgnoringName(callableNode);
+ }
+ else
+ {
+ m_currentScope->addChild(callableNode);
+ }
+
+ *outNode = callableNode;
+ return SLANG_OK;
+ }
+ else
+ {
+ // Looks like variable
+ if (!m_currentScope->canContainFields())
+ {
+ _consumeToSync();
+ return SLANG_OK;
+ }
+
+ if (m_reader.peekTokenType() == TokenType::LBracket)
+ {
+ auto startCursor = m_reader.getCursor();
+
+ // If it's not balanced we just assume it's not correct - and ignore
+ if (SLANG_FAILED(_parseBalanced(nullptr)))
+ {
+ _consumeToSync();
+ return SLANG_OK;
}
+
+ UnownedStringSlice arraySuffix = _concatTokens(startCursor);
+
+ // The overall type is the typename concated with the arraySuffix
+ StringBuilder buf;
+ buf << typeName << arraySuffix;
+
+ StringSlicePool* typePool = m_nodeTree->m_typePool;
+
+ typeName = typePool->getSlice(typePool->add(buf));
}
- case TokenType::Semicolon:
+
+ // Check if has a default value
+ if (advanceIfToken(TokenType::OpAssign))
{
- FieldNode* fieldNode = new FieldNode;
+ List<Token> exprTokens;
+ SLANG_RETURN_ON_FAIL(_parseExpression(exprTokens));
+ }
+
+ // Hit end of field/variable
+ if (m_reader.peekTokenType() == TokenType::Semicolon)
+ {
+ RefPtr<FieldNode> fieldNode = new FieldNode;
fieldNode->m_fieldType = typeName;
- fieldNode->m_name = fieldName;
+ fieldNode->m_name = nameToken;
fieldNode->m_reflectionType = m_currentScope->getContainedReflectionType();
+ fieldNode->m_isStatic = isStatic;
m_currentScope->addChild(fieldNode);
- break;
+
+ *outNode = fieldNode;
+ return SLANG_OK;
}
- default: break;
}
-
+
_consumeToSync();
return SLANG_OK;
}
@@ -1243,6 +1658,11 @@ SlangResult Parser::parse(SourceOrigin* sourceOrigin, const Options* options)
switch (style)
{
+ case IdentifierStyle::Template:
+ {
+ SLANG_RETURN_ON_FAIL(_consumeTemplate());
+ break;
+ }
case IdentifierStyle::PreDeclare:
{
SLANG_RETURN_ON_FAIL(_parsePreDeclare());
@@ -1310,11 +1730,25 @@ SlangResult Parser::parse(SourceOrigin* sourceOrigin, const Options* options)
}
else
{
+ // If it's a marker handle it
+ if (_isMarker(m_reader.peekToken().getContent()))
+ {
+ if (!m_currentScope->isClassLike())
+ {
+ m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::classMarkerOutsideOfClass);
+ return SLANG_FAIL;
+ }
+
+ SLANG_RETURN_ON_FAIL(_parseMarker());
+ break;
+ }
+
// Special case the node that's the root of the hierarchy (as far as reflection is concerned)
// This could be a field
- if (m_currentScope->canContainFields())
+ if (m_currentScope->canContainFields() || m_currentScope->canContainCallable())
{
- SLANG_RETURN_ON_FAIL(_maybeParseField());
+ Node* containedNode = nullptr;
+ SLANG_RETURN_ON_FAIL(_maybeParseContained(&containedNode));
}
else
{
diff --git a/tools/slang-cpp-extractor/parser.h b/tools/slang-cpp-extractor/parser.h
index 5ed862d65..4815821c5 100644
--- a/tools/slang-cpp-extractor/parser.h
+++ b/tools/slang-cpp-extractor/parser.h
@@ -33,6 +33,11 @@ public:
void setKindEnabled(Node::Kind kind, bool isEnabled = true);
bool isTypeEnabled(Node::Kind kind) { return (m_nodeTypeEnabled & (NodeTypeBitType(1) << int(kind))) != 0; }
+
+ /// If set classes require a 'marker' to be reflected
+ void setRequireMarker(bool requireMarker) { m_requireMarker = requireMarker; }
+ bool getRequireMarker() const { return m_requireMarker; }
+
void setKindsEnabled(const Node::Kind* kinds, Index kindsCount, bool isEnabled = true);
Parser(NodeTree* nodeTree, DiagnosticSink* sink);
@@ -48,14 +53,17 @@ protected:
SlangResult _parseTypeSet();
SlangResult _maybeParseNode(Node::Kind kind);
- SlangResult _maybeParseField();
+ SlangResult _maybeParseContained(Node** outNode);
SlangResult _parseTypeDef();
SlangResult _parseEnum();
+ SlangResult _parseMarker();
SlangResult _maybeParseType(List<Token>& outToks);
SlangResult _maybeParseType(UnownedStringSlice& outType);
+ SlangResult _parseExpression(List<Token>& outExprTokens);
+
SlangResult _maybeParseType(Index& ioTemplateDepth);
SlangResult _maybeParseTemplateArgs(Index& ioTemplateDepth);
SlangResult _maybeParseTemplateArg(Index& ioTemplateDepth);
@@ -66,9 +74,14 @@ protected:
/// Concatenate all tokens from start to the current position
UnownedStringSlice _concatTokens(TokenReader::ParsingCursor start);
+ /// Consume what looks like a template definition
+ SlangResult _consumeTemplate();
+
void _consumeTypeModifiers();
SlangResult _consumeToSync();
+ /// Consumes balanced parens. Will return an error if not matched. Assumes starts on opening (
+ SlangResult _consumeBalancedParens();
NodeTypeBitType m_nodeTypeEnabled;
@@ -82,6 +95,8 @@ protected:
NodeTree* m_nodeTree; ///< Shared state between parses. Nodes will be added to this
+ bool m_requireMarker = true;
+
const Options* m_options;
};
diff --git a/tools/slang-cpp-extractor/unit-test.cpp b/tools/slang-cpp-extractor/unit-test.cpp
index a30a3caa7..c239a2a2c 100644
--- a/tools/slang-cpp-extractor/unit-test.cpp
+++ b/tools/slang-cpp-extractor/unit-test.cpp
@@ -41,12 +41,26 @@ struct TestState
};
static const char someSource[] =
+"#define SLANG_REFLECTED\n"
+"\n"
+"SLANG_REFLECTED\n"
+"class ISomeInterface\n"
+"{\n"
+" public:\n"
+" virtual int SLANG_MCALL someMethod(int a, int b) const = 0;\n"
+" virtual float SLANG_MCALL anotherMethod(float a) = 0;\n"
+"};\n"
+"\n"
"enum SomeEnum\n"
"{\n"
" Value,\n"
" Another = 10,\n"
"};\n"
-"typedef SomeEnum AliasEnum;\n";
+"typedef SomeEnum AliasEnum;\n"
+"void someFunc(int a, float b) { }\n"
+"namespace Blah {\n"
+"int add(int a, int b) { return a + b; }\n"
+"}\n";
/* static */SlangResult UnitTestUtil::run()
@@ -65,6 +79,8 @@ static const char someSource[] =
SourceOrigin* sourceOrigin = tree.addSourceOrigin(sourceFile, state.m_options);
Parser parser(&tree, &state.m_sink);
+ // We don't require markers to reflect
+ parser.setRequireMarker(false);
{
const Node::Kind enableKinds[] = { Node::Kind::Enum, Node::Kind::EnumClass, Node::Kind::EnumCase, Node::Kind::TypeDef };
@@ -72,6 +88,14 @@ static const char someSource[] =
}
SLANG_RETURN_ON_FAIL(parser.parse(sourceOrigin, &state.m_options));
+
+
+ {
+ StringBuilder buf;
+ tree.getRootNode()->dump(0, buf);
+
+ SLANG_UNUSED(buf);
+ }
}
return SLANG_OK;