hand coding a parser

后端 未结 5 939
故里飘歌
故里飘歌 2020-12-23 02:13

For all you compiler gurus, I wanna write a recursive descent parser and I wanna do it with just code. No generating lexers and parsers from some other grammar and don\'t te

5条回答
  •  自闭症患者
    2020-12-23 02:55

    What you're writing is called a pushdown automaton. This is usually more power than you need to write a lexer, it's certainly excessive if you're writing a lexer for a modern language like CSS. A recursive descent parser is close in power to a pushdown automaton, but recursive descent parsers are much easier to write and to understand. Most parser generators generate pushdown automatons.

    Lexers are almost always written as finite state machines, i.e., like your code except get rid of the "stack" object. Finite state machines are closely related to regular expressions (actually, they're provably equivalent to one another). When designing such a parser, one usually starts with the regular expressions and uses them to create a deterministic finite automaton, with some extra code in the transitions to record the beginning and end of each token.

    There are tools to do this. The lex tool and its descendants are well known and have been translated into many languages. The ANTLR toolchain also has a lexer component. My preferred tool is ragel on platforms that support it. There is little benefit to writing a lexer by hand most of the time, and the code generated by these tools will probably be faster and more reliable.

    If you do want to write your own lexer by hand, good ones often look something like this:

    function readToken() // note: returns only one token each time
        while !eof
            c = peekChar()
            if c in A-Za-z
                return readIdentifier()
            else if c in 0-9
                return readInteger()
            else if c in ' \n\r\t\v\f'
                nextChar()
            ...
        return EOF
    
    function readIdentifier()
        ident = ""
        while !eof
            c = nextChar()
            if c in A-Za-z0-9
                ident.append(c)
            else
                return Token(Identifier, ident)
                // or maybe...
                return Identifier(ident)
    

    Then you can write your parser as a recursive descent parser. Don't try to combine lexer / parser stages into one, it leads to a total mess of code. (According to the Parsec author, it's slower, too).

提交回复
热议问题