dexed/dexed-d/src/parser.d

145 lines
4.5 KiB
D

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;
}