spir denis.spir at
Thu Nov 11 07:34:41 PST 2010

On Thu, 11 Nov 2010 07:47:01 -0500
"Steven Schveighoffer" <schveiguy at> wrote:

> This is not enough code to understand the problem.  For instance,  
> writefln(pattern) prints a class, but we don't see Pattern's toString  
> function.  We don't see the code that creates the object and then calls  
> check.  Maybe something happens between calls?
> It's impossible to diagnose something like this without a working example,  
> so you need to trim it down to something that still compiles and fails,  
> and then share that entire code.
> -Steve


I tried to build a example mini-app to show the issue more simply, but couldn't. The point is the bug only shows when using the most complicated element of my code (class List), built on top of ~ 1000 lines. I guess the best is to try to explain the case and provide example test. See code of the class and test case below.

This is a PEG matching lib. Patterns and whole grammars are written in plain D, eg:
    DOT = new Character('.');
    digits = new String(new Klass("0-9"));
    integer = new Tuple(digits, DOT, digits);
There are about 15 little test suites running fine for all bases (Node types, Exceptions, Pattern), utilities, and every Pattern type, including 3 other helpers like List.

List is a helper for matching lists of separated items -- a very common & annoying case. It does not only build the actual pattern, but also extracts the elements out of the mess for you (see embedded doc).

Its actual pattern is constructed in this(), as a Tuple formed of a sub-pattern matching the first element, and a ZeroOrMore repetition matching more (sep pattern) Tuples (see this()). The test case is as follows:
    auto number = new String(new Klass("0-9"));
    auto PLUS = new Literal("+");
    auto addition = new List(number, PLUS);
Writing out the constructed pattern attribute at the end of this() correctly gives:
    pattern: ([0-9]+ ("+" [0-9]+)*)

(Since a List's actual pattern is an instance of Tuple, what is shown here is the result of Tuple's toString(), which itself uses a tool func listText() --> code below after the class and test case.)

Using a List instance (here 'addition') is calling an operational method, of which only 'match' is implemented yet (in Pattern). Match does little stuff more that delegating to check(source) (specialised in List). If I then write out the pattern from check(), I read:
    pattern: (null null)
Thus, no surprise that I get a segfault when matching, since the top pattern will delegate to null subpatterns.

But strangely enough, I also get a segfault by simply typing "writeln(addition.pattern)" in the test func.

I can also post the whole module somewhere online if needed.

PS: Last minute! I just retried to write out the pattern from inside check(). This time, instead of "(null null)", I get "(DeeMatch.List DeeMatch.Source)". ???
(DeeMatch is the temp. module name, so it's 2 class names.)

Thank you,

==================== test func ======================
void testList2 () {
    writeln("=== List ========================");
    auto number = new String(new Klass("0-9"));
    auto PLUS = new Literal("+");
    auto addition = new List(number, PLUS);
    // This check works fine:
    writeln(addition);          // --> List([0-9]+, "+", 2)
    // But if I uncomment the following: segfault
    // writeln(addition.pattern);
    // If I run a test as is, I get a segfault when
    // accessing one of addition's patterns subpattern.
    // Trick to make code execute w/o segfault:
    // redefine addition.pattern by hand
    addition.pattern = new Tuple(
        new ZeroOrMore(new Tuple(PLUS,number))
    // Then, I can use addition: 
    auto node = addition.match("1+23+456");
    assert (node.toString() == `["1" "23" "456"]`);
    // instead of: `("1" [("+" "23") ("+" "456")])`

==================== List class =====================
class List : Pattern {
    /**  pattern type matching a list of separated elements
            moreElements = ZeroOrMore(Tuple(sep, element));
            list = Tuple(element, moreElements);
            --> (e1 ((s e2) (s e3)))
        Using List instead works as follows:
            list = List(element, sep);
            --> (e1 e2 e3)
        Parameter min defines minimal number of elements
        (typically 0 1 or 2). */
    static string typename = "List";
    static string errorMessageForm = 
        "Cannot find at least %s elements for List pattern %s.";
    Pattern element;
    Pattern sep;
    uint min;
    Tuple pattern;
    this (Pattern element, Pattern sep, uint min=2) {
        this.min = min;
        // for output
        this.element = element;
        this.sep = sep;
        // Construct pattern.
        this.pattern = new Tuple(
            new ZeroOrMore(new Tuple(sep,element))
    // operational
    override Result check (Source source) {
        /**  Match text and form a list of elements. */
        // Try & match pattern.
        // Note: match mail fail if min==0
        Result result;
        TupleNode node;
        try {
            result = this.pattern.check(source);
            node = cast(TupleNode)(result.node);
        } catch (MatchError e) {
            if (this.min == 0) {
                auto zeroNode = new ListNode(this, []);
                return new Result(zeroNode, source);
                throw e;
        // Extract element nodes.
        // First, get & record first element.
        writeln("*** ", node);
        Node firstElement = node.nodes[0];      // first element node
        Node[] listNodes = [firstElement];      // actual result!
        // Then, get to list of additional elements.
        auto nodeOfPairs = cast(ListNode)(node.nodes[1]);
        TupleNode[] pairNodes = cast(TupleNode[])(nodeOfPairs.nodes);
        // Extract additional elements
        if (pairNodes.length > 0) {
            foreach (TupleNode pairNode ; pairNodes)    // (sep,element) pairs
                listNodes ~= pairNode.nodes[1];
        // Check number of elements.
        if (listNodes.length < this.min) {
            string message = format(this.errorMessageForm, this.min, this);
            throw new MatchFailure(this, source, message);
        // Construct result node.
        auto listNode = new ListNode(this, listNodes);
        return new Result(listNode, source);
    // feedback
    override string toString () {
        /** "List(element, sep, min)" */
        return format("List(%s, %s, %s)", this.element,this.sep,this.min);

==================== Tuple toString() =======================
    override string toString () {
        /** "(p1 p2 ...)" */
        return listText!Pattern(this.patterns, " " ,"(",")");
=== using tool func
string listText(Element) (Element[] elements,
    string sep, string lDelim,string rDelim) {
    /** textual representation of elements held in a plain array */
    string[] elementTexts = new string[elements.length];
    foreach (uint i, Element element ; elements)
        elementTexts[i] = to!string(element);
    string content = join(elementTexts, sep);
    return format("%s%s%s", lDelim, content, rDelim);

More information about the Digitalmars-d-learn mailing list