I'm writing a parser for a markup language that I have created (writing in python, but that's not really relevant to this question -- in fact if this seems like a bad idea, I'd love a suggestion for a better path).
I'm reading about parsers here: http://www.ferg.org/parsing/index.html, and I'm working on writing the lexer which should, if I understand correctly, split the content into tokens. What I'm having trouble understanding is what token types I should use or how to create them. For example, the token types in the example I linked to are:
- STRING
- IDENTIFIER
- NUMBER
- WHITESPACE
- COMMENT
- EOF
- Many symbols such as { and ( count as their own token type
The problem I'm having is that the more general token types seem a bit arbitrary to me. For example, why is STRING its own separate token type vs. IDENTIFIER. A string could be represented as STRING_START + (IDENTIFIER | WHITESPACE) + STRING_START.
This may also have to do with the difficulties of my language. For example, variable declarations are written as {var-name var value}
and deployed with {var-name}
. It seems like '{'
and '}'
should be their own tokens, but are VAR_NAME and VAR_VALUE eligible token types, or would these both fall under IDENTIFIER? What's more is that the VAR_VALUE can actually contain whitespace. The whitespace after var-name
is used to signify the start of the value in the declaration .. any other whitespace is part of the value. Does this whitespace become its own token? Whitespace only has that meaning in this context. Moreover, {
may not be the start of a variable declaration .. it depends on the context (there's that word again!). {:
starts a name declaration, and {
can even be used as part of some value.
My language is similar to Python in that blocks are created with indentation. I was reading about how Python uses the lexer to create INDENT and DEDENT tokens (that serve more or less as what {
and }
would do in a lot of other languages). Python claims to be context-free which means to me that at least the lexer shouldn't care about where it is in the stream while creating tokens. How does Python's lexer know it's building an INDENT token of a specific length without knowing about previous characters (e.g. that the previous line was a newline, so start creating the spaces for INDENT)? I ask because I need to know this too.
My final question is the stupidest one: why is a lexer even necessary? It seems to me that the parser could go character-by-character and figure out where it is and what it expects. Does the lexer add the benefit of simplicity?