I am developing a library which lets user set a crucial type alias, or do it through preprocessor directives.
This type alias (or the directive) is undeclared in the library, by design. Thus, when developing my code I get annoying error messages and squiggles for this undeclared type. This could be avoided if I declare a temporary type for it somewhere. However, I do not like the idea of declaring it when I work with the code and then remove it when I publish it. It is also bug prone, since I could easily forget to remove it.
My question is: Can I define preprocessor directives for VS Code's static analysis (IntelliSense? C/C++ Extension)?
That would let me consider the analysis like what a well defined type alias would produce. And avoid annoying error messages/squiggles.
Minimal reproducable example:
Online compiler example
tCore.hpp
#pragma once
#include <string>
// User is responsible of declaring the tCore type
// tCore interface methods
template<typename TCore>
std::string printImpl();
tPrint.hpp
#pragma once
#include <iostream>
class tPrint {
public:
tPrint() = default;
void print() const {
std::cout << printImpl<tCore>() << std::endl; // <-- Static analysis error here!
}
};
tFoo.hpp - tCore candidate
#pragma once
#include <string>
#include "tCore.hpp"
struct tFoo {};
template<>
std::string printImpl<tFoo>() {
return "tFoo";
}
main.cpp
#include <tFoo.hpp>
using tCore = tFoo;
int main() {
tPrint p{};
p.print(); // -> "tFoo"
return 0;
}
I found out it was IntelliSense causing the error through the C/C++ Extension.
I also found an option of adding compiler arguments to IntelliSense, which is exactly what I was looking for.
Either through the UI:
Press
F1 -> > C/C++: Edit Configurations (UI) -> Scroll down to Defines
Or via the JSON :
c_cpp_properties.json configurations has a field defines which holds any compiler arguments.
Related
On Ubuntu 20.04, ROS2 Foxy, GCC version 9.4.0, C++ rclcpp node.
The completely undocumented builder pattern for messages works as follows. (Odometry for example)
#include <nav_msgs/msg/odometry.hpp>
// https://docs.ros2.org/foxy/api/nav_msgs/msg/Odometry.html
nav_msgs::msg::Odometry const odom = nav_msgs::build<nav_msgs::msg::Odometry>()
.header(std_msgs::msg::Header()) .child_frame_id("")
.pose(geometry_msgs::msg::PoseWithCovariance())
.twist(geometry_msgs::msg::TwistWithCovariance());
// This works // Excuse typos
This is opposed to the "classic" style (since ROS committee won't allow initialization with arguments):
nav_msgs::msg::Odometry odom; // Notice can't make it const
odom.header = std_msgs::msg::Header();
odom.header.stamp = builtin_interfaces::msg::Time(); // or = node.get_clock()->now();
odom.header.frame_id = "...";
// ... etc, fill in other odometry members
Problem: But a lot of the time I get a cryptic error, and I can't discern why.
Ex, if I tried:
odom.header = std_msgs::build<std_msgs::msg::Header>() // Error here, on ')'
.stamp(node.get_clock()->now()).frame_id("map");
error: use of auto std_msgs::build() [with MessageType = std_msgs::msg::Header_<std::allocator<void> >] before deduction of auto
I tried to make the type as explicit as possible; casting to target type, creating an intermediary variable of the target type, no change in error.
To even understand that the builder pattern exists, unless you found some cryptic scrawl on the internet (like so), you have to dig into the generated header files, so I did.
You will find
/// vim /opt/ros/foxy/include/std_msgs/msg/header.hpp
// generated from rosidl_generator_cpp/resource/idl.hpp.em
// generated code does not contain a copyright notice
#ifndef STD_MSGS__MSG__HEADER_HPP_
#define STD_MSGS__MSG__HEADER_HPP_
#include "std_msgs/msg/detail/header__struct.hpp"
#include "std_msgs/msg/detail/header__builder.hpp"
#include "std_msgs/msg/detail/header__traits.hpp"
#endif // STD_MSGS__MSG__HEADER_HPP_
/// vim /opt/ros/foxy/include/std_msgs/msg/detail/header__builder.hpp
// ...
namespace std_msgs {
namespace msg {
namespace builder {
// ... bunch of builder classes calling the next, ex:
class Init_Header_stamp { ... };
} // namespace builder
} // namespace msg
template<typename MessageType>
auto build();
template<>
inline
auto build<::std_msgs::msg::Header>()
{
return std_msgs::msg::builder::Init_Header_stamp();
}
} // namespace std_msgs
Using this as a guide, I wondered if I repeated / copy-pasted the template declaration in my code, and replaced auto with the actual type(s), would that work... but at this point my c++ fu was exhausted, and all the cobbled substitutions just gave more weird errors.
Thoughts?
Solution:
The parent type includes the child type definitions, but not the builder definitions.
In this example, to include Odometry and then use the builder pattern on Header, you also need to #include <std_msgs/msg/header.hpp>.
Why: (no, really, why was it implemented like this, where is the documentation)
/// In vim /opt/ros/foxy/include/nav_msgs/msg/detail/odometry__struct.hpp
// Include directives for member types
// Member 'header'
#include "std_msgs/msg/detail/header__struct.hpp"
// Member 'pose'
#include "geometry_msgs/msg/detail/pose_with_covariance__struct.hpp"
// Member 'twist'
#include "geometry_msgs/msg/detail/twist_with_covariance__struct.hpp"
/// Notice, doesn't include the full child header, which also includes __builder.hpp,
/// but just the sub-header with type definitions, __struct.hpp!
So include every header manually if you use it's builder pattern.
One more day, one more dumb question on stackoverflow, please excuse me.
The idea was to make dll and then import it to another project to use it there, but after including dll header file in second project and writing paths to the .lib and header files I still have these errors:
E0070 incomplete type is not allowed prjct-1 D:\projects-vs\firstTryWT\prjct-1\prjct-1.cpp 33
C2027 use of undefined type 'stmr::MathProblem' prjct-1 D:\projects-vs\firstTryWT\prjct-1\prjct-1.cpp 33
C2079 'problem' uses undefined class 'stmr::MathProblem' prjct-1 D:\projects-vs\firstTryWT\prjct-1\prjct-1.cpp 33
C3493 'problem' cannot be implicitly captured because no default capture mode has been specified prjct-1 D:\projects-vs\firstTryWT\prjct-1\prjct-1.cpp 35
I've tried to rebuild, change some code till I understand that I either already did what is needed or I don't know what is needed. Anyway, here is the code:
Dll header file:
#pragma once
#ifdef STMR_EXPORTS
#define STMR_API __declspec(dllexport)
#else
#define STMR_API __declspec(dllimport)
#endif
#include <vector>
#include <string>
namespace stmr
{
class STMR_API MathProblem;
class STMR_API Operation;
}
Definitions of both classes have STMR_API keyword. I have 'STMR_EXPORTS' in C/C++ -> Preprocessor and '$(OutDir)$(TargetName).lib' in Linker -> Advanced -> Import Library so that I have the import lib generated.
main cpp of the project which is supposed to use the dll:
#include <Wt/WApplication.h>
#include <Wt/WBreak.h>
#include <Wt/WContainerWidget.h>
#include <Wt/WLineEdit.h>
#include <Wt/WPushButton.h>
#include <Wt/WText.h>
#include "stmrLib.h"
class HelloApplication : public Wt::WApplication
{
public:
HelloApplication(const Wt::WEnvironment& env);
private:
Wt::WPushButton* button_m;
Wt::WText* summary_m;
};
HelloApplication::HelloApplication(const Wt::WEnvironment& env)
: Wt::WApplication(env)
{
button_m = root()->addWidget(std::make_unique<Wt::WPushButton>());
summary_m = root()->addWidget(std::make_unique<Wt::WText>());
stmr::MathProblem problem = stmr::MathProblem(problemText_m->text().narrow());
auto solving = [this] {
summary_m->setText("This equals " + std::to_string(problem.solve()));
};
button_m->clicked().connect(solving);
}
int main(int argc, char** argv)
{
return Wt::WRun(argc, argv, [](const Wt::WEnvironment& env) {
return std::make_unique<HelloApplication>(env);
});
}
I have correct path to the .lib file for dll in Linker -> General -> Additional Library Dependencies and stmr.lib/stmrd.lib in Linker -> Input -> Additional Dependencies for Release/Debug
Not sure if the problem in exporting or importing of the dll.
Feedback about question quality is appreciated.
In C++ (prior to modules) the header file needs to expose enough information about a class for another cpp file to create the object or use the type.
You have just forward declared it, which is enough to make pointers or references to the type and nothing else.
These errors have nothing to do with linking or DLLs or exports.
All compilation units that need to make instances or otherwise call methods of your types need to see the class definition. Put it in the headerfile. Method definitions can be in cpp files and exported.
The pImpl pattern may help you hide details, if you do not want to expose them.
I have been recently practicing managing multiple objects and drawing them in C++ using SFML library. I wanted my textures and future resources to be more reusable so I decided to make use of Thor library which suits my needs really well.
So I've written first few lines of code based on what you can find in this tutorial and the compiler always says:
main.cpp|12|error: 'textures_holder' does not name a type
This line gives an error :
textures_holder.acquire("Dirt", thor::Resources::fromFile<sf::Texture>("Textures\\dirt_block.png"));
I'm using Code::Blocks IDE with MinGW compiler and SFML 2.5.0.
Here's my main.cpp and the header file which contains extern object :
//...
#include <Thor/Resources.hpp>
#include "Dirt_Block.h"
using namespace std;
//Adding textures to the texture library
//THIS LINE GIVES AN ERROR
textures_holder.acquire("Dirt", thor::Resources::fromFile<sf::Texture>("Textures\\dirt_block.png"));
//Rest of code...
Dirt_Block.h (only the upper part) :
#ifndef DIRT_BLOCK_H
#define DIRT_BLOCK_H
#include <SFML\Graphics.hpp>
#include <vector>
#include <Thor/Resources.hpp>
#include <Thor/Resources/SfmlLoaders.hpp>
extern sf::Vector2u screenRes;
extern thor::ResourceHolder<sf::Texture, std::string> textures_holder;
//Rest of the code
I'd like to know what is causing this error and maybe help others who may experience similiar frustrating problems. Thanks for help.
EDIT :
As suggested in the comment I've declared a few extern int variables in the Dirt_Block.h so now it looks like this :
//...
extern int test_int_up;
extern sf::Vector2u screenRes;
extern thor::ResourceHolder<sf::Texture, std::string> textures_holder;
extern int test_int_d;
//...
And then assinged to them some value in main.cpp :
//...
test_int_up = 55;
test_int_d = 55;
//Adding textures to the texture library
textures_holder.acquire("Dirt", thor::Resources::fromFile<sf::Texture>("Textures\\dirt_block.png"));
//...
But the compiler gives error :
main.cpp|9|error: 'test_int_up' does not name a type
main.cpp|10|error: 'test_int_d' does not name a type
main.cpp|12|error: 'textures_holder' does not name a type
Much less distracting to see what your problem is without all the extraneous code!
C++ programs don't start from the top of the file and run code down to the bottom. They start at the main(), and control flow proceeds from there, with one thing triggering another.
(Note: That doesn't take into account global constructor ordering, which does go in order of declaration--but you have no guarantee of the order declarations from "different files" might run in.)
Point being, you can't just make random function or method calls in the middle of a file. That's where you put declarations. You have to be inside of a function or method to make calls, e.g.
int main() {
textures_holder.acquire(
"Dirt",
thor::Resources::fromFile<sf::Texture>("Textures\\dirt_block.png")
);
...
}
Suppose there's a library, one version of which defines a function with name foo, and another version has the name changed to foo_other, but both these functions still have the same arguments and return values. I currently use conditional compilation like this:
#include <foo.h>
#ifdef USE_NEW_FOO
#define trueFoo foo_other
#else
#define trueFoo foo
#endif
But this requires some external detection of the library version and setting the corresponding compiler option like -DUSE_NEW_FOO. I'd rather have the code automatically figure what function it should call, based on it being declared or not in <foo.h>.
Is there any way to achieve this in any version of C?
If not, will switching to any version of C++ provide me any ways to do this? (assuming the library does all the needed actions like extern "C" blocks in its headers)? Namely, I'm thinking of somehow making use of SFINAE, but for a global function, rather than method, which was discussed in the linked question.
In C++ you can use expression SFINAE for this:
//this template only enabled if foo is declared with the right args
template <typename... Args>
auto trueFoo (Args&&... args) -> decltype(foo(std::forward<Args>(args)...))
{
return foo(std::forward<Args>(args)...);
}
//ditto for fooOther
template <typename... Args>
auto trueFoo (Args&&... args) -> decltype(fooOther(std::forward<Args>(args)...))
{
return fooOther(std::forward<Args>(args)...);
}
If you are statically linking to a function, in most versions of C++, the name of the function is "mangled" to reflect its argument list. Therefore, an attempt to statically link to the library, by a program with an out-of-date .hpp file, will result in an "unknown symbol" linker-error.
In the C language, there's no metadata of any kind which indicates what the argument list of any exported function actually is.
Realistically, I think, you simply need to be sure that the .h or .hpp files that you're using to link to a library, actually reflect the corresponding object-code within whatever version of that library you are using. You also need to be sure that the Makefile (or "auto-make" process) will correctly identify any-and-all modules within your application which link-to that library and which therefore must be recompiled in case of any changes to it. (If it were me, I would recompile the entire application.) In short, you must see to it that this issue doesn't occur.
In C++ you can do something like this:
#include <iostream>
#include <type_traits>
//#define DEFINE_F
#ifdef DEFINE_F
void f()
{
}
#endif
namespace
{
constexpr struct special
{
std::false_type operator()() const;
}f;
}
struct checkForF
{
static const constexpr auto value = std::conditional< std::is_same<std::false_type, decltype(::f())>::value, std::false_type, std::true_type >::type();
};
int main()
{
std::cout << checkForF::value << std::endl;
}
ideone
Please note I only handle f without any parameters.
Is it at all possible to parse C++ with incomplete declarations with clang with its existing libclang API ? I.e. parse .cpp file without including all the headers, deducing declarations on the fly. so, e.g. The following text:
A B::Foo(){return stuff();}
Will detect unknown symbol A, call my callback that deducts A is a class using my magic heuristic, then call this callback the same way with B and Foo and stuff. In the end I want to be able to infer that I saw a member Foo of class B returning A, and stuff is a function.. Or something to that effect.
context: I wanna see if I can do sensible syntax highlighting and on the fly code analysis without parsing all the headers very quickly.
[EDIT] To clarify, I'm looking for very heavily restricted C++ parsing, possibly with some heuristic to lift some of the restrictions.
C++ grammar is full of context dependencies. Is Foo() a function call or a construction of a temporary of class Foo? Is Foo<Bar> stuff; a template Foo<Bar> instantiation and declaration of variable stuff, or is it weird-looking 2 calls to overloaded operator < and operator > ? It's only possible to tell in context, and context often comes from parsing the headers.
What I'm looking for is a way to plug my custom convention rules. E.g. I know that I don't overload Win32 symbols, so I can safely assume that CreateFile is always a function, and I even know its signature. I also know that all my classes start with a capital letter and are nouns, and functions are usually verbs, so I can reasonably guess that Foo and Bar are class names. In a more complex scenario, I know I don't write side-effect-free expressions like a < b > c; so I can assume that a is always a template instantiation. And so on.
So, the question is whether it's possible to use Clang API to call back every time it encounters an unknown symbol, and give it an answer using my own non-C++ heuristic. If my heuristic fails, then the parse fails, obviously. And I'm not talking about parsing Boost library :) I'm talking about very simple C++, probably without templates, restricted to some minimum that clang can handle in this case.
I know the question is fairly old, but have a look here :
LibFuzzy is a library for heuristically parsing C++ based on Clang's
Lexer. The fuzzy parser is fault-tolerant, works without knowledge of
the build system and on incomplete source files. As the parser
necessarily makes guesses, the resulting syntax tree may be partially
wrong.
It is a sub-project from clang-highlight, an (experimental?) tool which seems to be no longer developed.
I'm only interested in the fuzzy parsing part and forked it on my github page where I fixed several minor issues and made the tool autonomous (it can be compiled outside clang's source tree). Don't try to compile it with C++14 (which G++ 6's default mode), because there will be conflicts with make_unique.
According to this page, clang-format has its own fuzzy parser (and is actively developed), but the parser was (is ?) more tighly coupled to the tool.
Unless you heavily restrict the code that people are allowed to write, it is basically impossible to do a good job of parsing C++ (and hence syntax highlighting beyond keywords/regular expressions) without parsing all the headers. The pre-processor is particularly good at screwing things up for you.
There are some thoughts on the difficulties of fuzzy parsing (in the context of visual studio) here which might be of interest: http://blogs.msdn.com/b/vcblog/archive/2011/03/03/10136696.aspx
Another solution which I think will suit more the OP than fuzzy parsing.
When parsing, clang maintains Semantic information through the Sema part of the analyzer. When encountering an unknown symbol, Sema will fallback to ExternalSemaSource to get some information about this symbol. Through this, you could implement what you want.
Here is a quick example how to set up it. It is not entirely functional (I'm not doing anything in the LookupUnqualified method), you might need to do further investigations and I think it is a good start.
// Declares clang::SyntaxOnlyAction.
#include <clang/Frontend/FrontendActions.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/Support/CommandLine.h>
#include <clang/AST/AST.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Frontend/ASTConsumers.h>
#include <clang/Frontend/FrontendActions.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/Tooling.h>
#include <clang/Rewrite/Core/Rewriter.h>
#include <llvm/Support/raw_ostream.h>
#include <clang/Sema/ExternalSemaSource.h>
#include <clang/Sema/Sema.h>
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/ParseAST.h"
#include <clang/Sema/Lookup.h>
#include <iostream>
using namespace clang;
using namespace clang::tooling;
using namespace llvm;
class ExampleVisitor : public RecursiveASTVisitor<ExampleVisitor> {
private:
ASTContext *astContext;
public:
explicit ExampleVisitor(CompilerInstance *CI, StringRef file)
: astContext(&(CI->getASTContext())) {}
virtual bool VisitVarDecl(VarDecl *d) {
std::cout << d->getNameAsString() << "#\n";
return true;
}
};
class ExampleASTConsumer : public ASTConsumer {
private:
ExampleVisitor visitor;
public:
explicit ExampleASTConsumer(CompilerInstance *CI, StringRef file)
: visitor(CI, file) {}
virtual void HandleTranslationUnit(ASTContext &Context) {
// de cette façon, on applique le visiteur sur l'ensemble de la translation
// unit
visitor.TraverseDecl(Context.getTranslationUnitDecl());
}
};
class DynamicIDHandler : public clang::ExternalSemaSource {
public:
DynamicIDHandler(clang::Sema *Sema)
: m_Sema(Sema), m_Context(Sema->getASTContext()) {}
~DynamicIDHandler() = default;
/// \brief Provides last resort lookup for failed unqualified lookups
///
/// If there is failed lookup, tell sema to create an artificial declaration
/// which is of dependent type. So the lookup result is marked as dependent
/// and the diagnostics are suppressed. After that is's an interpreter's
/// responsibility to fix all these fake declarations and lookups.
/// It is done by the DynamicExprTransformer.
///
/// #param[out] R The recovered symbol.
/// #param[in] S The scope in which the lookup failed.
virtual bool LookupUnqualified(clang::LookupResult &R, clang::Scope *S) {
DeclarationName Name = R.getLookupName();
std::cout << Name.getAsString() << "\n";
// IdentifierInfo *II = Name.getAsIdentifierInfo();
// SourceLocation Loc = R.getNameLoc();
// VarDecl *Result =
// // VarDecl::Create(m_Context, R.getSema().getFunctionLevelDeclContext(),
// // Loc, Loc, II, m_Context.DependentTy,
// // /*TypeSourceInfo*/ 0, SC_None, SC_None);
// if (Result) {
// R.addDecl(Result);
// // Say that we can handle the situation. Clang should try to recover
// return true;
// } else{
// return false;
// }
return false;
}
private:
clang::Sema *m_Sema;
clang::ASTContext &m_Context;
};
// *****************************************************************************/
LangOptions getFormattingLangOpts(bool Cpp03 = false) {
LangOptions LangOpts;
LangOpts.CPlusPlus = 1;
LangOpts.CPlusPlus11 = Cpp03 ? 0 : 1;
LangOpts.CPlusPlus14 = Cpp03 ? 0 : 1;
LangOpts.LineComment = 1;
LangOpts.Bool = 1;
LangOpts.ObjC1 = 1;
LangOpts.ObjC2 = 1;
return LangOpts;
}
int main() {
using clang::CompilerInstance;
using clang::TargetOptions;
using clang::TargetInfo;
using clang::FileEntry;
using clang::Token;
using clang::ASTContext;
using clang::ASTConsumer;
using clang::Parser;
using clang::DiagnosticOptions;
using clang::TextDiagnosticPrinter;
CompilerInstance ci;
ci.getLangOpts() = getFormattingLangOpts(false);
DiagnosticOptions diagnosticOptions;
ci.createDiagnostics();
std::shared_ptr<clang::TargetOptions> pto = std::make_shared<clang::TargetOptions>();
pto->Triple = llvm::sys::getDefaultTargetTriple();
TargetInfo *pti = TargetInfo::CreateTargetInfo(ci.getDiagnostics(), pto);
ci.setTarget(pti);
ci.createFileManager();
ci.createSourceManager(ci.getFileManager());
ci.createPreprocessor(clang::TU_Complete);
ci.getPreprocessorOpts().UsePredefines = false;
ci.createASTContext();
ci.setASTConsumer(
llvm::make_unique<ExampleASTConsumer>(&ci, "../src/test.cpp"));
ci.createSema(TU_Complete, nullptr);
auto &sema = ci.getSema();
sema.Initialize();
DynamicIDHandler handler(&sema);
sema.addExternalSource(&handler);
const FileEntry *pFile = ci.getFileManager().getFile("../src/test.cpp");
ci.getSourceManager().setMainFileID(ci.getSourceManager().createFileID(
pFile, clang::SourceLocation(), clang::SrcMgr::C_User));
ci.getDiagnosticClient().BeginSourceFile(ci.getLangOpts(),
&ci.getPreprocessor());
clang::ParseAST(sema,true,false);
ci.getDiagnosticClient().EndSourceFile();
return 0;
}
The idea and the DynamicIDHandler class are from cling project where unknown symbols are variable (hence the comments and the code).
OP doesn't want "fuzzy parsing". What he wants is full context-free parsing of the C++ source code, without any requirement for name and type resolution.
He plans to make educated guesses about the types based on the result of the parse.
Clang proper tangles parsing and name/type resolution, which means it must have all that background type information available when it parses. Other answers suggest a LibFuzzy that produces incorrect parse trees, and some fuzzy parser for clang-format about which I know nothing. If one insists on producing a classic AST, none of these solutions will produce the "right" tree in the face of ambiguous parses.
Our DMS Software Reengineering Toolkit with its C++ front end can parse C++ source without the type information, and produces accurate "ASTs"; these are actually abstract syntax dags where forks in trees represent different possible interpretations of the source code according to a language-precise grammar (ambiguous (sub)parses).
What Clang tries to do is avoid producing these multiple sub-parses by using type information as it parses. What DMS does is produce the ambiguous parses, and in (optional) post-parsing (attribute-grammar-evaluation) pass, collect symbol table information and eliminate the sub-parses which are inconsistent with the types; for well-formed programs, this produces a plain AST with no ambiguities left.
If OP wants to make heuristic guesses about the type information, he will need to know these possible interpretations. If they are eliminated in advance, he cannot straightforwardly guess what types might be needed. An interesting possibility is the idea of modifying the attribute grammar (provided in source form as part of the DMS's C++ front end), which already knows all of C++ type rules, to do this with partial information. That would be an enormous head start over building the heuristic analyzer from scratch, given that it has to know about 600 pages of arcane name and type resolution rules from the standard.
You can see examples of the (dag) produced by DMS's parser.