用于HTML的词法分析器(java)Markdown源代码

问题描述:

我甚至不知道从哪里开始编写逐字符的词法分析器。我根据我给出的规则和细节为Markdown语言(特别是HTML)编写BNF语法规则,因此不需要添加任何内容。我现在必须设计和实现逐个字符的词法分析器,将我的Markdown语言中的源文件的词位分割为标记。这里是我的BNF语法:用于HTML的词法分析器(java)Markdown源代码

端子:

#DOCUMENT BEGIN, 
#DOCUMENT END 
#HEAD BEGIN, 
#HEAD END, 
#TITLE BEGIN, 
#TITLE END, 
#PARAGRAPH BEGIN, 
#PARAGRAPH END, 
#BOLD BEGIN, 
#BOLD END, 
#ITALICS BEGIN, 
#ITALICS END, 
#LIST BEGIN, 
#LIST END, 
#ITEM BEGIN, 
#ITEM END, 
#LINK BEGIN, 
#TEXT, 
#ADDRESS, 
#LINK END, 
#DEFINE BEGIN, 
#NAME, 
#VALUE, 
#DEFINE END, 
#USE BEGIN, 
#USE END 

注意,这些端子不区分大小写。

非终端:

<document> ::= #DOCUMENT BEGIN <macro-­‐define> <head> <body> #DOCUMENT END 

<head> ::= #HEAD BEGIN <title> #HEAD END | ε 

<title> ::= #TITLE BEGIN <text> #TITLE END | ε 

<body> ::= <inner-­‐text> <body> 
      | <paragraph> <body> 
      | <bold> <body> 
      | <italics> <body> 
      | <list> <body> 
      | ε 

<paragraph> ::= #PARAGRAPH BEGIN <macro-­‐define> <inner-­‐paragraph> #PARAGRAPH END 

<inner-­‐paragraph> ::= <inner-­‐text> <inner-­‐paragraph> 
         | <bold> <inner-­‐paragraph> 
         | <italics> <inner-­‐paragraph> 
         | <list> <inner-­‐paragraph> 
         | ε 

<inner-­‐text> ::= <macro-­‐use> <inner-­‐text> 
        | <text> <inner-­‐text> 
        | ε 

<macro-­‐define> ::= #DEFINE BEGIN #NAME <text> #VALUE <body> #DEFINE END <macro-­‐define> 
        | ε 

<macro-­‐use> ::= #USE BEGIN <text> #USE END | ε 

<bold> ::= #BOLD BEGIN <macro-­‐define> <inner-­‐text> #BOLD END 

<italics> ::= #ITALICS BEGIN <macro-­‐define> <inner-­‐text> #ITALICS END 

<link> ::= #LINK BEGIN #TEXT <text> #ADDRESS <text> #LINK END 

<list> ::= #LIST BEGIN #ITEM BEGIN <macro-­‐define> <inner-­‐list> #ITEM END <list-­‐items> #LIST END 

<list-­‐items> ::= #ITEM BEGIN <macro-­‐define> <inner-­‐list> #ITEM END <list-­‐items> | ε 

<inner-­‐list> ::= | <bold> <inner-­‐list> 
        | <italics> <inner-­‐list> 
        | <list><inner-­‐list> 
        | <inner-­‐text> <inner-­‐list> 
        | ε 

<text> ::= Any plain text | ε 

我们可以假设,HTML字符,如 “<”, “>”, “&” 和 “/” 不以任何文本中出现源文件。我们还可以假设“#”只出现在我们的Markdown注释之前(例如#DOCUMENT)。我认为最好有单独的Java类来表示令牌对象,例如:DocumentBegin,DocumentEnd,ParagraphBegin,ParagraphEnd等。遇到的任何词汇错误(例如#DOC BEGIN)都应作为输出报告给控制台尽可能多的错误信息。遇到第一个错误后编译器应该退出。如果遇到错误,则不应创建输出文件。

我的问题是,我知道一个词法分析器应该做什么,但老实说,我不知道从哪里开始编码/实现。如果您需要更详细的问题解答,请提问,我可以尽力解释。这是我们班上应有的一个大项目的一部分。我无法完成这部分,并失去了很多要点,但现在我只需要了解它,所以一旦我们经过测试,我就不会迷失方向。

+0

我不认为stackoverflow可以处理关于解析技术的完整讲座(不,我不是一个人给予的)。一般来说,我会寻找一个像javacc这样的编译器编译器,然后检查你是否可以做任何事情。 –

+0

我建议你得到一份Flex的副本,并阅读文档。您还应该阅读关于词法分析的任何编译器一章。 –

+0

您可能也想在Antlr论坛上提问。 Antlr可能不是您需要的理想工具,但有许多专家可能会提供帮助。 – user1031420

我发现在Java中这样做的最好的(也就是我所知道的)词法分析器叫做JFlex。我们在大学使用它来标记语言,并且我已经在商业上使用它来为应用程序中的特定领域语言创建语法突出显示。

JFlex的词法分析器

http://jflex.de/

杯分析器

http://www2.cs.tum.edu/projects/cup/

约LALR一点点(1)解析器

http://en.wikipedia.org/wiki/LALR_parser

如果您需要示例(即,示例代码)给我发消息,我会给你发一些笔记。虽然我确信一些大学网站(例如普林斯顿大学)可能有一些东西,但快速谷歌没有显示出任何有用的东西。

干杯,

约翰

+0

约翰,它似乎(从问题),这个人正在寻找一个词法分析器,而不是使用现成的已经实施的。 –

好吧,这是一个相当有点晚了,但在这里我们去。

词法分析器通常与语法(和BNF符号)相关联,但实际上它们有点不同。

词法分析器将字符转换为令牌,这些令牌有些被处理为语法的“原子”,而解析器将令牌转化为某种中间结构(通常是树)。专注于词法分析器部分,您可以将其视为输入的低通处理,很大程度上是将字母处理为单词。

由于您已经拥有BNF语法,因此您已经知道要使用的所有令牌(结束语),因此请将它们放入列表中。这个想法是如何快速决定哪些系列的字母将映射到列表中的每个项目。例如

#, D, E, F, I, N, E, <whitespace> => #DEFINE 
#, D, O, C, U, M, E, N, T, <whitespace> => #DOCUMENT 
B, E, G, I, N, <whitespace> => BEGIN 
E, N, D, <whitespace> => END 

有迹象表明,在解析攀升了几个问题:

首先,你有很多的比较做。读入的第一个字符可能是'#',如果是,那么您仍然有超过20个可能匹配的项目。这意味着你将不得不继续你的比赛进入下一个角色,如果它是'D',这仍然意味着有两个可能的匹配'#DEFINE'和'#DOCUMENT'。第二,如果在处理完'#BEGIN'之后,你有'#BEGIN'和'#BEGINNING'这样的词,那么在你抓住下一个字符之前,你不能在两者之间做出选择。在认为字符的“消耗”的系统中抓取下一个字符会使下一个令牌的处理复杂化。可能需要窥视或预测,但这些会增加逻辑的复杂性以决定生成哪些令牌。

三,你有一个通配符'文本'标记。该标记几乎可以匹配任何东西,因此您需要针对所有其他标记进行检查,以确保您的标记生成逻辑始终知道应该生成哪个标记。理想情况下,令牌生成器(Lexer)不依赖任何解析来“知道”下一个令牌;然而,有些语言很复杂,解析器给Lexer提供了“提示”。避免这些类型的系统使得编译器实现更清晰。不幸的是,在一些已经存在的语言中,以这种方式构建事物并不总是可能的。

因此,要知道你有什么想法(你可能已经在某种程度上已经拥有了),你怎么去做呢?

那么,你需要某种索引来跟踪你已经消耗的字符(这已经完全转化为令牌),所以你不会意外地给字符对令牌流的双重影响。如果你要展望未来,你需要第二个指示器来“向前看”,并且你会想要限制前瞻的数量(使逻辑更难)。

然后您需要未知数量的数据结构(称为令牌)。尽管并不总是需要这样做,但我建议跟踪令牌中的起始行号,起始字符索引,结束行号和结尾字符索引。它使调试更容易。另外,在令牌中“捕获”子字符串是一个好主意。你可以称它为你所愿,但有些人称之为令牌的“形象”。

当然,如果您的解析器可以不同类型的令牌区分,那么你应该通过某种方式存储(或)令牌的该标记的类型。偶尔一个人有一个令牌的“价值”的概念,它也可以被存储。

经过一番努力,您应该能够将一串字符推送到Lexer中,并且有一个令牌流出来。祝你好运。