module parser; import dparse.lexer, dparse.ast, dparse.parser, dparse.rollback_allocator; import iz; /** * By default libdparse outputs errors and warnings to the standard streams. * This function prevents that. */ void ignoreErrors(string, size_t, size_t, string, bool) @system {} /// dparse Parser with bound checks fixed final class Parser : dparse.parser.Parser { override PragmaExpression parsePragmaExpression() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!PragmaExpression; mixin(tokenCheck!"pragma"); mixin(tokenCheck!"("); const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; if (currentIs(tok!",")) { advance(); mixin(parseNodeQ!(`node.argumentList`, `ArgumentList`)); } mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } override TemplateValueParameterDefault parseTemplateValueParameterDefault() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateValueParameterDefault; mixin(tokenCheck!"="); if (!moreTokens) { error("template argument default value expected instead of EOF"); return null; } switch (current.type) { case tok!"__FILE__": case tok!"__FILE_FULL_PATH__": case tok!"__MODULE__": case tok!"__LINE__": case tok!"__FUNCTION__": case tok!"__PRETTY_FUNCTION__": node.token = advance(); break; default: mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); break; } node.tokens = tokens[startIndex .. index]; return node; } } /** * Params: * parserConfig = a parser configuration. * Returns: * The parsed module. */ Module parseModule(P = .Parser)(auto ref ParserConfig parserConfig) { auto parser = new P(); with (parserConfig) { parser.fileName = fileName; parser.tokens = tokens; parser.messageFunction = messageFunction; parser.messageDelegate = messageDelegate; parser.allocator = allocator; } Module mod = parser.parseModule(); with (parserConfig) { if (warningCount !is null) *warningCount = parser.warningCount; if (errorCount !is null) *errorCount = parser.errorCount; } return mod; } /** * Params: * tokens = The tokens parsed by dparse.lexer. * fileName = The name of the file being parsed. * allocator = A pointer to a rollback allocator. * messageFuncOrDg = Either a function or a delegate that receives the parser messages. * errorCount = An optional pointer to a variable receiving the error count. * warningCount = An optional pointer to a variable receiving the warning count. * Returns: * The parsed module. */ Module parseModule(P = .Parser,F)(const(Token)[] tokens, string fileName, RollbackAllocator* allocator, F messageFuncOrDg = null, uint* errorCount = null, uint* warningCount = null) { static if (is(F)) { static if (is(F : MessageFunction)) return ParserConfig(tokens, fileName, allocator, messageFuncOrDg, null, errorCount, warningCount).parseModule(); else static if (is(F : MessageDelegate)) return ParserConfig(tokens, fileName, allocator, null, messageFuncOrDg, errorCount, warningCount).parseModule(); else static assert(0, "F must be a MessageFunction or a MessageDelegate"); } else { return ParserConfig(tokens, fileName, allocator, null, null, null, null).parseModule!P(); } } /** * Produces and visits the AST for a source code. * * This function is used to handle the content of a MixinExpression in an * ASTVisitor. */ T parseAndVisit(T : ASTVisitor, A...)(const(char)[] source, A a) { import std.functional; RollbackAllocator allocator; LexerConfig config = LexerConfig("", StringBehavior.source, WhitespaceBehavior.skip); StringCache cache = StringCache(StringCache.defaultBucketCount); const(Token)[] tokens = getTokensForParser(cast(ubyte[]) source, config, &cache); Module mod = parseModule(tokens, "", &allocator, toDelegate(&ignoreErrors)); T result = construct!(T)(a); result.visit(mod); return result; }