• 解析树 完成树的实现之后，现在我们来看一个例子，告诉你怎么样利用树去解决一些实际问题。在这个章节，我们来研究解析树解析树常常用于真实世界的结构表示，例如句子或数学表达式。 图 1：一个简单句的解析树 图...
• Device Tree由一系列被命名的结点（node）和属性（property）组成，而结点本身可包含子结点。
• 个人总结的 内核解析设备的 流程图
• 当编译器分析该语句生成的解析树看起来像这样：  assignment  ________ statement ____  / | \  / := \  identifier ___ expression _______  | / | \  result / + \  ...

The Oracle (tm) Users' Co-Operative FAQ
What is a Parse Tree?

Author's name: Carel-Jan Engel Author's Email: cjpengel.dbalert@xs4all.nl  Date written: Mar 24, 2005 Oracle version(s): N/A  In documentation about tuning SQL, I see references to parse trees. What is aparse tree ?

Back to index of questions

A parse-tree is an internal structure, created by the compiler or interpreter while parsing some language construction. Parsing is also known as 'syntax analysis'.
An example (slightly adapted version of the example found at page 6 of the famous 'Dragon Book', Compilers: principles, techniques and tools, by Alfred V. Aho, Ravi Sethi and Jeffrey D. Ullman, Published by Addison Wesley. My copy is from 1986) will illustrate a parse tree. Rather than dealing with the complexities of a SQL statement, let's take a rather simple language construction: The assignment of the result of an expression to a variable:
result := salary + bonus * 1.10
When the compiler analyzes this statement the resulting parse-tree will look like this :
当编译器分析该语句生成的解析树看起来像这样：
assignment
________ statement ____
/                          |                      \
/                          :=                      \
identifier                             ___ expression _______
|                                     /                    |                            \
result                           /                      +                              \
expression                                   __ expression ___
|                                            /               |                    \
identifier                                /                  *                      \
|                                 expression                       expression
salary                                   |                                              |
identifier                                  number
|                                               |
bonus                                     1.10
The picture is an upside-down representation of a tree. The language elements in this small simple assignment are:identifiers (result, salary, bonus), operators (:=, +, *), and anumber (1.10). 'Identifier' is the language element that names a variable, function or procedure. 'Operator' is the language element that represents some action to be taken, upon theoperands at either end of the operator. Number is a constant, 1.10 in this statement. The syntax rules (' grammar') will specify which 'sentences' are valid.
图片是倒树的代表性。在这个小的简单的赋值的语言元素是：标识符（因此，工资，奖金），操作符（：=，+ *）和（1​​.10）aNumber的。 “标识”是一个变量，函数或过程的语言元素名称。 “经营者”的语言元素，代表了一些，在运营商的两端后应采取theoperands的行动。数量是一个常数，在此声明1.10。将指定的语法规则（“语法”），“句子”是有效的。
After successfully decomposing the statement into its internal representation, the compiler or interpreter can 'walk the tree' to create the executable code for the construction. An interpreter will not generate code for the execution, but will invoke built-in executing functions by itself. Let's take the interpreter for the rest of the explanation, execution of the steps is easier to explain than the code-generation of a compiler. For the example I assume the bonus to be 100, and the salary to be 1000. The tree-walk will start at the root of the tree, the assignment statement. The rule for the assignment will tell the interpreter that the right hand has to be evaluated first. This evaluation is also known as 'reduction'. The right hand side of the assignment needs to be reduced to a value, the result of the expression, before it can be assigned to the variable at the left hand side of the statement.
成功后分解成它的内部表示的语句，编译器或解释可以“行走的树”的建设，以创建可执行代码。解释器将不生成执行代码，但会调用内置的执行本身的职能。让我们看看其他的解释解释，执行的步骤是比一个编译器的代码生成更容易解释。对于这个例子，我假定为100，奖金和工资为1000。树步行将开始在树的根，赋值语句。转让的规则会告诉解释器，右手先计算。这种评价也被称为“还原”。转让的右侧的需求将减少到一个值，表达式的结果，才可以分配的语句左边的变量。

The first node at the right-hand side of the statement contains an expression with a '+' operator. The right hand side of the '+' operator needs to be assigned to the left hand side. So the walk goes on to the next node at the right hand side. There the interpreter will detect the expression with the '*' operator. The left hand side of this operator needs to be multiplied with the right hand side. The interpreter goes on to the right hand side, and detects an expression that consists of a single number: 1.10. This side is fully reduced, the result can be stored and the interpreter walks the tree back up to the '*' operator, and starts evaluating its left hand side. This is an expression that consists of one single identifier, representing a variable, 'bonus'., The memory location represented by this variable is read and it's contents (100) will be multiplied by the right hand side result, 1.10. This expression has been fully reduced to the result 110 now. The interpreter walks up, to the '+' operator, and starts evaluating its left hand side. There it will again detect an identifier, 'salary'. Its location is read (1000) and the expression is reduced to a number, 1000. The right and left hand side will be added, resulting in 1,110. Now the expression at the right hand side of the assignment is fully reduced, and the interpreter walks up the tree, finds the assignment operator ':='. This instructs the interpreter to copy the result of the expression to the left hand side. The left hand side contains an identifier, 'result'. The memory location represented by 'result' will be filled with the result of the expression, 1,110.
在右侧的声明的第一个节点包含一个“+”操作​​符的表达式。右侧的“+”运算需要被分配到左侧。所以走在右侧的下一个节点。有解释器将检测到的“*”操作符表达式。这个操作符左边需要乘以右侧。口译员的右侧，并检测到表达，由一个单一的数字：1.10。此方是完全还原，结果可存储和口译各界树“*”操作符，并开始评估其左侧。这是一个包含一个单一的标识符代表一个变量，“奖金”，这个变量所代表的内存位置读取和它的内容（100）将右边的结果，1.10乘以的表达。此表达式已全面降低到现在的结果110。口译员走了，“+”运算，并开始评估其左侧。在那里，它会再次检测标识符，“薪水”。它的位置是只读（1000），表达的是一个数字，1000。将增加的权利和左侧，导致在1110。现在，在右侧转让的表达式是完全降低，口译员走了树，发现赋值运算符':='.这指示解释复制的表达左侧。左侧包含一个标识符，“结果”。 “结果”所代表的内存位置将被填充与表达式的结果，1110。
It is just a simplified explanation of how an interpreter or compiler uses a parse tree. It's out of scope of this answer to create a complete introduction to compiler building practices. However, it might be clear that creating a parse-tree consumes some resources. Before the language elements can be recognized they must be read character by character, type checking and possible conversion needs to be done, identifiers (tables, columns etc.) need to be identified and checked in the data dictionary, and so on. After this 'hard parse' the parse tree is composed, and is a far cheaper form to use to execute a statement than doing all this analysis over and over again. Therefore, storing the parse-tree in the SQL-area for future use can save quite some time during the processing of SQL-statements that have come across before.
它仅仅是一个简单解释如何解释器或编译器的使用解析树。这是这个答案的范围，建立一个完整的介绍编译器的建设实践。但是，它可能是明确的，创建解析树消耗一些资源。的语言元素，可以确认之前，他们必须予以字符的字符，类型检查和可能的转换，需要做的，标识符（表，列等）的需要确定和检查数据字典，依此类推。在此之后的“硬解析”的解析树组成，是一个便宜得多的形式用来执行比一遍又一遍的做这一切分析的声明。因此，存储在SQL区，供日后使用解析树，跨前的SQL语句的处理过程中可以节省一段时间。

Further reading: If you are interested in compiler building techniques, consider reading The Dragon Book. It can be found at:  http://www.amazon.co.uk/exec/obidos/ASIN/0201101947/026-7499645-2696457

注：以上翻译是用google直接翻译的，意思不明白，参考英文原文
展开全文
• 2017/3/12 32.1 解析器 访问Python解析树 Python 3.6.1rc1文档 32.1parser访问的Python解析树 该parser模块为Python的内部解析器和字节码编译器提供了一个接口此接口的主要目的是允 许Python代码编辑Python表达式的...
• 资源介绍： Json解析形框易语言源码，源码调用了精易模块。 资源作者： 350246356 资源界面： 资源下载：
• 生成解析树 (AST) 的快速正则表达式引擎。 它以匹配的文本大小的线性时间执行此操作，并以模式大小的 O(m*log(m)) 缩放。 算法 该算法描述于 尼科·施瓦茨。 可扩展的代码克隆检测。 博士论文，伯尔尼大学，2014 年...
• 主要介绍了XML中的形结构与DOM文档对象模型,文中举了JavaScript解析DOM对象的例子,需要的朋友可以参考下
• 主要介绍了使用递归算法结合数据库解析成Java形结构的代码解析的相关资料,需要的朋友可以参考下
• 字符串解析器 快速创建用于解析字符串的解析树的工具
• 其次，提出了一种丰富的解析树结构，既可以很好地保留必要的结构化信息，又可以通过区分解析树结构的各个部分来有效避免噪声。 对CoNLL'; 2005共享任务的评估表明，新的树内核和丰富的解析树结构都对SRL做出了很大...
• 此项目包含一组库，这些库实现了具有高级分析功能的Java1.0-Java13解析器。
• 一个完整的解析器生成器，它通过使用上下文无关的语法处理标记，从而在创建抽象语法之前对输入字符串进行标记。 令牌是使用正则表达式库定义的，而实际的解析器是Earley的解析算法的实现。 尽管需要使用两个文件中...
• 将简单SQL语句解析为抽象语法（AST），然后将其转换回SQL。 用法 为SQL语句创建AST const { Parser } = require ( 'flora-sql-parser' ) ; const parser = new Parser ( ) ; const ast = parser . parse ( '...
• 前端通过js把数据库里的菜单展现出来，形成动态的菜单！
• XBOOT设备技术源码解析，可用于XBOOT的开发参考、指导。
• ## 依存句法树解析

千次阅读 2019-05-05 15:44:06
s=parse_sentence(it) # 通过Stanfordnlp依存句法分析得到一个句法 用nltk包装成树的结构 res=search(s) # 使用nltk遍历，然后把短语合并 print(res) recursionSearch.py： #encoding=utf8 import ...
#encoding=utf8
import re,os,json
from stanfordParse import pos
from stanfordParse import parse_sentence
from recursionSearch import search
def split_long_sentence_by_pos(text):
pos_tag=pos(text)
new_str=''
for apos in pos_tag:
if apos[1] not in del_flag:
new_str+=apos[0]
return new_str
def extract_parallel(text):
parallel_text=[]
pattern=re.compile('[，,][\u4e00-\u9fa5]{2,4}[，,]')
search_obj=pattern.search(text)
if search_obj:
start_start,end=search_obj.span()
rep=text[start_start:end-2]
rep1=text[start_start:end-1]
if '，' in rep1:
rep1.replace('，','、')
if ',' in rep1:
rep1.replace(',','、')
text.replace(rep1,text)
parallel_text.append(rep[1:])
text_leave=text.replace(rep,'')
while pattern.search(text_leave):
start,end=pattern.search(text_leave).span()
rep=text_leave[start:end-2]
rep1=text[start_start:end-1]
if '，' in rep1:
rep1.replace('，','、')
if ',' in rep1:
rep1.replace(',','、')
text.replace(rep1,text)
text_leave=text_leave.replace(rep,'')
parallel_text.append(rep[1:])

return parallel_text,text
else:
return None,text

def split_long_sentence_by_sep(text):
segment=[]
if '。' or '.' or '!' or '！' or '?' or '？' or ';' or '；' in text:
text=re.split(r'[。.!！?？;；]',text)
for seg in text:
if seg=='' or seg==' ':
continue
para,seg=extract_parallel(seg)
if len(seg)>19:
seg=split_long_sentence_by_pos(seg)
if len(seg)>19:
seg=re.split('[，,]',seg)
if isinstance(seg,list) and '' in seg:
seg=seg.remove('')
if isinstance(seg, list) and ' ' in seg:
seg=seg.remove(' ')
segment.append(seg)
return segment

return open(path,"r",encoding="utf8")

def get_np_words(t):
noun_phrase_list=[]
for tree in t.subtrees(lambda t:t.height()==3):
if tree.label()=='NP' and len(tree.leaves())>1:
noun_phrase=''.join(tree.leaves())
noun_phrase_list.append(noun_phrase)
return noun_phrase_list

def get_n_v_pair(t):
for tree in t.subtrees(lambda t:t.height()==3):
if tree.label()=='NP' and len(tree.leaves())>1:
noun_phrase=''.join(tree.leaves())

if __name__=="__main__":
out=open("dependency.txt",'w',encoding='utf8')
for it in itera:
s=parse_sentence(it)   # 通过Stanfordnlp依存句法分析得到一个句法树 用nltk包装成树的结构
res=search(s)          # 使用nltk遍历树，然后把短语合并
print(res)
recursionSearch.py：
#encoding=utf8
import nltk.tree as tree
import nltk

def get_vn_pair():
pass
def get_noun_chunk(tree):
noun_chunk=[]
if tree.label()=="NP":
nouns_phase=''.join(tree.leaves())
noun_chunk.append(nouns_phase)
return noun_chunk

def get_ip_recursion_noun(tree):
np_list=[]
if len(tree)==1:
tr=tree[0]
get_ip_recursion_noun(tr)
if len(tree)==2:
tr=tree[0]
get_ip_recursion_noun(tr)
tr=tree[1]
get_ip_recursion_noun(tr)
if len(tree)==3:
tr=tree[0]
get_ip_recursion_noun(tr)
tr=tree[1]
get_ip_recursion_noun(tr)
tr=tree[2]
get_ip_recursion_noun(tr)
if tree.label()=='NP':
np_list.append(get_noun_chunk(tree))
return np_list

def get_vv_loss_np(tree):
if not isinstance(tree,nltk.tree.Tree):
return False
stack=[]
np=[]
stack.append(tree)
current_tree=''
while stack:
current_tree=stack.pop()
if isinstance(current_tree,nltk.tree.Tree) and current_tree.label()=='VP':
continue
elif isinstance(current_tree,nltk.tree.Tree) and current_tree.label()!='NP':
for i in range(len(current_tree)):
stack.append(current_tree[i])
elif isinstance(current_tree,nltk.tree.Tree) and current_tree.label()=='NP':
np.append(get_noun_chunk(tree))
if np:
return np
else:
return False

def search(tree_in):                                         # 遍历刚才构建的树
if not isinstance(tree_in,nltk.tree.Tree):
return False
vp_pair=[]
stack=[]
stack.append(tree_in)
current_tree=''
while stack:
tree=stack.pop()
if isinstance(tree,nltk.tree.Tree) and tree.label()=="ROOT":    # 要处理的文本的语句
for i in range(len(tree)):
stack.append(tree[i])
if isinstance(tree,nltk.tree.Tree) and tree.label()=="IP":      # 简单从句
for i in range(len(tree)):
stack.append(tree[i])
if isinstance(tree,nltk.tree.Tree) and tree.label()=="VP":      # 动词短语
duplicate=[]
if len(tree)>=2:
for i in range(1,len(tree)):
if tree[0].label()=='VV' and tree[i].label()=="NP":  # 动词 和 名词短语
verb=''.join(tree[0].leaves())               # 合并动词 leaves是分词
noun=get_noun_chunk(tree[i])
if verb and noun:
vp_pair.append((verb,noun))                 # 返回 动名词短语对
duplicate.append(noun)
elif tree[0].label()=='VV' and tree[i].label()!="NP":
noun=get_vv_loss_np(tree)
verb=''.join(tree[0].leaves())
if verb and noun and noun not in duplicate:
duplicate.append(noun)
vp_pair.append((verb,noun))
if vp_pair:
return vp_pair
else:
return False

#if tree.label()=="NP":
#nouns_phase=''.join(tree.leaves())
#noun_chunk.append(nouns_phase)

stanfordParse.py：
#encoding=utf8
from stanfordcorenlp import StanfordCoreNLP
from nltk import Tree, ProbabilisticTree
nlp = StanfordCoreNLP('/home/lhq/桌面/NLP_basis/stanfordnlp', lang='zh')
import nltk,re

grammer = "NP: {<DT>?<JJ>*<NN>}"
cp = nltk.RegexpParser(grammer)                         #生成规则
pattern=re.compile(u'[^a-zA-Z\u4E00-\u9FA5]')
pattern_del=re.compile('(\a-zA-Z0-9+)')

def _replace_c(text):
"""
将英文标点符号替换成中文标点符号，并去除html语言的一些标志等噪音
:param text:
:return:
"""
intab = ",?!()"
outtab = "，？！（）"
deltab = " \n<li>< li>+_-.><li \U0010fc01 _"
trantab=text.maketrans(intab, outtab,deltab)
return text.translate(trantab)

def parse_sentence(text):
text=_replace_c(text)          # 文本去噪
try:
if len(text.strip())>6:    # 判断，文本是否大于6个字，小于6个字的我们认为不是句子
return Tree.fromstring(nlp.parse(text.strip()))        # nlp.parse(text.strip())：是将句子变成依存句法树  Tree.fromstring是将str类型的树转换成nltk的结构的树
except:
pass

def pos(text):
text=_replace_c(text)
if len(text.strip())>6:
return nlp.pos_tag(text)
else:
return False

def denpency_parse(text):
return nlp.dependency_parse(text)

from nltk.chunk.regexp import *


展开全文
• ## Linux设备树解析

万次阅读 多人点赞 2017-08-18 21:47:36
1. Device Tree简介
Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称“this whole ARM thing is a fucking pain in the ass”，引发ARM Linux社区的地震，随后ARM社区进行了一系列的重大修正。在过去的ARM Linux中，arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码，相当多数的代码只是在描述板级细节，而这些板级细节对于内核来讲，不过是垃圾，如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。 社区必须改变这种局面，于是PowerPC等其他体系架构下已经使用的Flattened Device Tree（FDT）进入ARM社区的视野。Device Tree是一种描述硬件的数据结构，它起源于OpenFirmware(OF)。在Linux2.6中，ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx，采用Device Tree后，许多硬件的细节可以直接透过它传递给Linux，而不再需要在kernel中进行大量的冗余编码。
Device Tree由一系列被命名的结点（node）和属性（property）组成，而结点本身可包含子结点。所谓属性，其实就是成对出现的name和value。在Device Tree中，可描述的信息包括（原先这些信息大多被hard code到kernel中）:
CPU的数量和类别 内存基地址和大小 总线和桥 外设连接 中断控制器和中断使用情况GPIO控制器和GPIO使用情况
2. Device Tree编译
Device Tree文件的格式为dts，包含的头文件格式为dtsi，dts文件是一种人可以看懂的编码格式。但是uboot和linux不能直接识别，他们只能识别二进制文件，所以需要把dts文件编译成dtb文件。dtb文件是一种可以被kernel和uboot识别的二进制文件。把dts编译成dtb文件的工具是dtc。Linux源码目录下scripts/dtc目录包含dtc工具的源码。在Linux的scripts/dtc目录下除了提供dtc工具外，也可以自己安装dtc工具，linux下执行：sudo apt-get install device-tree-compiler安装dtc工具。其中还提供了一个fdtdump的工具，可以反编译dtb文件。dts和dtb文件的转换如图1所示。  dtc工具的使用方法是：dtc –I dts –O dtb –o xxx.dtb xxx.dts，即可生成dts文件对应的dtb文件了。
3. Device Tree头信息
fdtdump工具使用，Linux终端执行ftddump –h，输出以下信息：  fdtdump -h  Usage: fdtdump [options]  Options: -[dshV]  -d, –debug Dump debug information while decoding the file  -s, –scan Scan for an embedded fdt in file  -h, –help Print this help and exit  -V, –version Print version and exit  本文采用s5pv21_smc.dtb文件为例说明fdtdump工具的使用。Linux终端执行fdtdump –sd s5pv21_smc.dtb > s5pv21_smc.txt，打开s5pv21_smc.txt文件，部分输出信息如下所示：  // magic: 0xd00dfeed  // totalsize: 0xce4 (3300)  // off_dt_struct: 0x38  // off_dt_strings: 0xc34  // off_mem_rsvmap: 0x28  // version: 17  // last_comp_version: 16  // boot_cpuid_phys: 0x0  // size_dt_strings: 0xb0  // size_dt_struct: 0xbfc  以上信息便是Device Tree文件头信息，存储在dtb文件的开头部分。在Linux内核中使用struct fdt_header结构体描述。struct fdt_header结构体定义在scripts\dtc\libfdt\fdt.h文件中。
struct fdt_header {
fdt32_t magic;               /* magic word FDT_MAGIC */
fdt32_t totalsize;           /* total size of DT block */
fdt32_t off_dt_struct;       /* offset to structure */
fdt32_t off_dt_strings;      /* offset to strings */
fdt32_t off_mem_rsvmap;      /* offset to memory reserve map */
fdt32_t version;                 /* format version */
fdt32_t last_comp_version;   /* last compatible version */

/* version 2 fields below */
fdt32_t boot_cpuid_phys;     /* Which physical CPU id we're booting on */
/* version 3 fields below */
fdt32_t size_dt_strings;     /* size of the strings block */

/* version 17 fields below */
fdt32_t size_dt_struct;      /* size of the structure block */
};

Device Tree中的节点信息举例如图3所示。    上述.dts文件并没有什么真实的用途，但它基本表征了一个Device Tree源文件的结构。1个root结点”/”；root结点下面含一系列子结点，本例中为”node@0”和”node@1”；结点”node@0”下又含有一系列子结点，本例中为”child-node@0”；各结点都有一系列属性。这些属性可能为空，如” an-empty-property”；可能为字符串，如”a-string-property”；可能为字符串数组，如”a-string-list-property”；可能为Cells（由u32整数组成），如”second-child-property”，可能为二进制数，如”a-byte-data-property”。Device Tree源文件的结构分为header、fill_area、dt_struct及dt_string四个区域。header为头信息，fill_area为填充区域，填充数字0，dt_struct存储节点数值及名称相关信息，dt_string存储属性名。例如：a-string-property就存储在dt_string区，”A string”及node1就存储在dt_struct区域。  我们可以给一个设备节点添加lable，之后可以通过&lable的形式访问这个lable，这种引用是通过phandle（pointer handle）进行的。例如，图3中的node1就是一个lable，node@0的子节点child-node@0通过&node1引用node@1节点。像是这种phandle的节点，在经过DTC工具编译之后，&node1会变成一个特殊的整型数字n，假设n值为1，那么在node@1节点下自动生成两个属性，属性如下：  linux,phandle = <0x00000001>;  phandle = <0x00000001>;  node@0的子节点child-node@0中的a-reference-to-something = <&node1>会变成a-reference-to-something = < 0x00000001>。此处0x00000001就是一个phandle得值，每一个phandle都有一个独一无二的整型值，在后续kernel中通过这个特殊的数字间接找到引用的节点。通过查看fdtdump输出信息以及dtb二进制文件信息，得到struct fdt_header和文件结构之间的关系信息如所示。
4. Device Tree文件结构
struct fdt_node_header {
fdt32_t tag;
char name[0];
};

struct fdt_property {
fdt32_t tag;
fdt32_t len;
fdt32_t nameoff;
char data[0];
};

#define FDT_BEGIN_NODE  0x1     /* Start node: full name */
#define FDT_END_NODE    0x2     /* End node */
#define FDT_PROP        0x3     /* Property: name off, size, content */
#define FDT_NOP         0x4     /* nop */
#define FDT_END         0x9

FDT_BEGIN_NODE和FDT_END_NODE标识node节点的起始和结束，FDT_PROP标识node节点下面的属性起始符，FDT_END标识Device Tree的结束标识符。因此，对于每个node节点的tag标识符一般为FDT_BEGIN_NODE，对于每个node节点下面的属性的tag标识符一般是FDT_PROP。  描述属性采用struct fdt_property描述，tag标识是属性，取值为FDT_PROP；len为属性值的长度（包括‘\0’，单位：字节）；nameoff为属性名称存储位置相对于off_dt_strings的偏移地址。  例如：compatible = “samsung,goni”, “samsung,s5pv210”;compatible是属性名称，”samsung,goni”, “samsung,s5pv210”是属性值。compatible属性名称字符串存放的区域是dt_string。”samsung,goni”, “samsung,s5pv210”存放的位置是fdt_property.data后面。因此fdt_property.data指向该属性值。fdt_property.tag的值为属性标识，len为属性值的长度（包括‘\0’，单位：字节）,此处len = 29。nameoff为compatible字符串的位置相对于off_dt_strings的偏移地址，即&compatible = nameoff + off_dt_strings。  dt_struct在Device Tree中的结构如图6所示。节点的嵌套也带来tag标识符的嵌套。
5. kernel解析Device Tree
struct property {
char *name;                          /* property full name */
int length;                          /* property value length */
void *value;                         /* property value */
struct property *next;             /* next property under the same node */
unsigned long _flags;
unsigned int unique_id;
struct bin_attribute attr;        /* 属性文件，与sysfs文件系统挂接 */
};

总的来说，kernel根据Device Tree的文件结构信息转换成struct property结构体，并将同一个node节点下面的所有属性通过property.next指针进行链接，形成一个单链表。  kernel中究竟是如何解析Device Tree的呢？下面分析函数解析过程。函数调用过程如图7所示。kernel的C语言阶段的入口函数是init/main.c/stsrt_kernel()函数，在early_init_dt_scan_nodes()中会做以下三件事：
struct device_node {
const char *name;              /* node的名称，取最后一次“/”和“@”之间子串 */
const char *type;              /* device_type的属性名称，没有为<NULL> */
phandle phandle;               /* phandle属性值 */
const char *full_name;         /* 指向该结构体结束的位置，存放node的路径全名，例如：/chosen */
struct fwnode_handle fwnode;

struct  property *properties;  /* 指向该节点下的第一个属性，其他属性与该属性链表相接 */
struct  property *deadprops;   /* removed properties */
struct  device_node *parent;   /* 父节点 */
struct  device_node *child;    /* 子节点 */
struct  device_node *sibling;  /* 姊妹节点，与自己同等级的node */
struct  kobject kobj;          /* sysfs文件系统目录体现 */
unsigned long _flags;          /* 当前node状态标志位，见/include/linux/of.h line124-127 */
void    *data;
};

/* flag descriptions (need to be visible even when !CONFIG_OF) */
#define OF_DYNAMIC        1 /* node and properties were allocated via kmalloc */
#define OF_DETACHED       2 /* node has been detached from the device tree*/
#define OF_POPULATED      3 /* device already created for the node */
#define OF_POPULATED_BUS  4 /* of_platform_populate recursed to children of this node */

struct device_node结构体中的每个成员作用已经备注了注释信息，下面分析以上信息是如何得来的。Device Tree的解析首先从unflatten_device_tree()开始，代码列出如下：
/**
* unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens the device-tree passed by the firmware, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used.
*/
void __init unflatten_device_tree(void)
{
__unflatten_device_tree(initial_boot_params, &of_root,
early_init_dt_alloc_memory_arch);

/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);
}

/**
* __unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens a device-tree, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used.
* @blob: The blob to expand
* @mynodes: The device_node tree created by the call
* @dt_alloc: An allocator that provides a virtual address to memory
* for the resulting tree
*/
static void __unflatten_device_tree(const void *blob,
struct device_node **mynodes,
void * (*dt_alloc)(u64 size, u64 align))
{
unsigned long size;
int start;
void *mem;

/* 省略部分不重要部分 */
/* First pass, scan for size */
start = 0;
size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);
size = ALIGN(size, 4);

/* Allocate memory for the expanded device tree */
mem = dt_alloc(size + 4, __alignof__(struct device_node));
memset(mem, 0, size);

/* Second pass, do actual unflattening */
start = 0;
unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
}

分析以上代码，在unflatten_device_tree()中，调用函数__unflatten_device_tree()，参数initial_boot_params指向Device Tree在内存中的首地址，of_root在经过该函数处理之后，会指向根节点，early_init_dt_alloc_memory_arch是一个函数指针，为struct device_node和struct property结构体分配内存的回调函数（callback）。在__unflatten_device_tree()函数中，两次调用unflatten_dt_node()函数，第一次是为了得到Device Tree转换成struct device_node和struct property结构体需要分配的内存大小，第二次调用才是具体填充每一个struct device_node和struct property结构体。__unflatten_device_tree()代码列出如下：

/**
* unflatten_dt_node - Alloc and populate a device_node from the flat tree
* @blob: The parent device tree blob
* @mem: Memory chunk to use for allocating device nodes and properties
* @poffset: pointer to node in flat tree
* @nodepp: The device_node tree created by the call
* @fpsize: Size of the node path up at the current depth.
* @dryrun: If true, do not allocate device nodes but still calculate needed
* memory size
*/
static void * unflatten_dt_node(const void *blob,
void *mem,
int *poffset,
struct device_node **nodepp,
unsigned long fpsize,
bool dryrun)
{
const __be32 *p;
struct device_node *np;
struct property *pp, **prev_pp = NULL;
const char *pathp;
unsigned int l, allocl;
static int depth;
int old_depth;
int offset;
int has_name = 0;
int new_format = 0;

/* 获取node节点的name指针到pathp中 */
pathp = fdt_get_name(blob, *poffset, &l);
if (!pathp)
return mem;

allocl = ++l;

/* version 0x10 has a more compact unit name here instead of the full
* path. we accumulate the full path size using "fpsize", we'll rebuild
* it later. We detect this because the first character of the name is
* not '/'.
*/
if ((*pathp) != '/') {
new_format = 1;
if (fpsize == 0) {
/* root node: special case. fpsize accounts for path
* plus terminating zero. root node only has '/', so
* fpsize should be 2, but we want to avoid the first
* level nodes to have two '/' so we use fpsize 1 here
*/
fpsize = 1;
allocl = 2;
l = 1;
pathp = "";
} else {
/* account for '/' and path size minus terminal 0
*/
fpsize += l;
allocl = fpsize;
}
}

/* 分配struct device_node内存，包括路径全称大小 */
np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
if (!dryrun) {
char *fn;
of_node_init(np);

/* 填充full_name，full_name指向该node节点的全路径名称字符串 */
np->full_name = fn = ((char *)np) + sizeof(*np);
if (new_format) {
/* rebuild full path for new format */
fn += strlen(fn);
}
*(fn++) = '/';
}
memcpy(fn, pathp, l);

/* 节点挂接到相应的父节点、子节点和姊妹节点 */
prev_pp = &np->properties;
}
}
/* 处理该node节点下面所有的property */
for (offset = fdt_first_property_offset(blob, *poffset);
(offset >= 0);
(offset = fdt_next_property_offset(blob, offset))) {
const char *pname;
u32 sz;

if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {
offset = -FDT_ERR_INTERNAL;
break;
}

if (pname == NULL) {
pr_info("Can't find property name in list !\n");
break;
}
if (strcmp(pname, "name") == 0)
has_name = 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property),
__alignof__(struct property));
if (!dryrun) {
/* We accept flattened tree phandles either in
* ePAPR-style "phandle" properties, or the
* legacy "linux,phandle" properties.  If both
* appear and have different values, things
* will get weird.  Don't do that. */

/* 处理phandle，得到phandle值 */
if ((strcmp(pname, "phandle") == 0) ||
(strcmp(pname, "linux,phandle") == 0)) {
if (np->phandle == 0)
np->phandle = be32_to_cpup(p);
}
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff */
if (strcmp(pname, "ibm,phandle") == 0)
np->phandle = be32_to_cpup(p);
pp->name = (char *)pname;
pp->length = sz;
pp->value = (__be32 *)p;
*prev_pp = pp;
prev_pp = &pp->next;
}
}
/* with version 0x10 we may not have the name property, recreate
* it here from the unit name if absent
*/
/* 为每个node节点添加一个name的属性 */
if (!has_name) {
const char *p1 = pathp, *ps = pathp, *pa = NULL;
int sz;

/* 属性name的value值为node节点的名称，取“/”和“@”之间的子串 */
while (*p1) {
if ((*p1) == '@')
pa = p1;
if ((*p1) == '/')
ps = p1 + 1;
p1++;
}
if (pa < ps)
pa = p1;
sz = (pa - ps) + 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
__alignof__(struct property));
if (!dryrun) {
pp->name = "name";
pp->length = sz;
pp->value = pp + 1;
*prev_pp = pp;
prev_pp = &pp->next;
memcpy(pp->value, ps, sz - 1);
((char *)pp->value)[sz - 1] = 0;
}
}
/* 填充device_node结构体中的name和type成员 */
if (!dryrun) {
*prev_pp = NULL;
np->name = of_get_property(np, "name", NULL);
np->type = of_get_property(np, "device_type", NULL);

if (!np->name)
np->name = "<NULL>";
if (!np->type)
np->type = "<NULL>";
}

old_depth = depth;
*poffset = fdt_next_node(blob, *poffset, &depth);
if (depth < 0)
depth = 0;
/* 递归调用node节点下面的子节点 */
while (*poffset > 0 && depth > old_depth)
mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
fpsize, dryrun);

if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
pr_err("unflatten: error %d processing FDT\n", *poffset);

/*
* Reverse the child list. Some drivers assumes node order matches .dts
* node order
*/
if (!dryrun && np->child) {
struct device_node *child = np->child;
np->child = NULL;
while (child) {
struct device_node *next = child->sibling;
child->sibling = np->child;
np->child = child;
child = next;
}
}

if (nodepp)
*nodepp = np;

return mem;
}


通过以上函数处理就得到了所有的struct device_node结构体，为每一个node都会自动添加一个名称为“name”的property，property.length的值为当前node的名称取最后一个“/”和“@”之间的子串（包括‘\0’）。例如：/serial@e2900800，则length = 7，property.value = device_node.name = “serial”。
6. platform_device和device_node绑定
经过以上解析，Device Tree的数据已经全部解析出具体的struct device_node和struct property结构体，下面需要和具体的device进行绑定。首先讲解platform_device和device_node的绑定过程。在arch/arm/kernel/setup.c文件中，customize_machine()函数负责填充struct platform_device结构体。函数调用过程如图8所示。    代码分析如下：

const struct of_device_id of_default_bus_match_table[] = {
{ .compatible = "simple-bus", },
{ .compatible = "simple-mfd", },
#ifdef CONFIG_ARM_AMBA
{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};

int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
struct device_node *child;
int rc = 0;

/* 获取根节点 */
root = root ? of_node_get(root) : of_find_node_by_path("/");
if (!root)
return -EINVAL;

/* 为根节点下面的每一个节点创建platform_device结构体 */
for_each_child_of_node(root, child) {
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc) {
of_node_put(child);
break;
}
}
/* 更新device_node flag标志位 */
of_node_set_flag(root, OF_POPULATED_BUS);

of_node_put(root);
return rc;
}

static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
const struct of_dev_auxdata *auxdata;
struct device_node *child;
struct platform_device *dev;
const char *bus_id = NULL;
void *platform_data = NULL;
int rc = 0;

/* 只有包含"compatible"属性的node节点才会生成相应的platform_device结构体 */
/* Make sure it has a compatible property */
if (strict && (!of_get_property(bus, "compatible", NULL))) {
return 0;
}
/* 省略部分代码 */
/*
* 针对节点下面得到status = "ok" 或者status = "okay"或者不存在status属性的
* 节点分配内存并填充platform_device结构体
*/
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
if (!dev || !of_match_node(matches, bus))
return 0;

/* 递归调用节点解析函数，为子节点继续生成platform_device结构体，前提是父节点
* 的“compatible” = “simple-bus”，也就是匹配of_default_bus_match_table结构体中的数据
*/
for_each_child_of_node(bus, child) {
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
if (rc) {
of_node_put(child);
break;
}
}
of_node_set_flag(bus, OF_POPULATED_BUS);
return rc;
}


总的来说，当of_platform_populate()函数执行完毕，kernel就为DTB中所有包含compatible属性名的第一级node创建platform_device结构体，并向平台设备总线注册设备信息。如果第一级node的compatible属性值等于“simple-bus”、“simple-mfd”或者”arm,amba-bus”的话，kernel会继续为当前node的第二级包含compatible属性的node创建platform_device结构体，并注册设备。Linux系统下的设备大多都是挂载在平台总线下的，因此在平台总线被注册后，会根据of_root节点的树结构，去寻找该总线的子节点，所有的子节点将被作为设备注册到该总线上。
7. i2c_client和device_node绑定
8. Device_Tree与sysfs
kernel启动流程为start_kernel()→rest_init()→kernel_thread():kernel_init()→do_basic_setup()→driver_init()→of_core_init()，在of_core_init()函数中在sys/firmware/devicetree/base目录下面为设备树展开成sysfs的目录和二进制属性文件，所有的node节点就是一个目录，所有的property属性就是一个二进制属性文件。
展开全文
• 用于Python LibCST的具体语法（CST）解析器和序列化程序库将Python 3.7源代码解析为保留所有格式详细信息（注释，空格，括号等）的CST。 对于自动构建针对Python LibCST的具体语法（CST）解析器和序列化程序库...
• 解析的结果通常是一个呈现文档结构的节点树，一般称之为解析树或句法树。 例如，对表达式“2+3-1” 的解析会得到以下树结构 解析语法规则 解析过程是基于文档内容所遵从的语法规则的，也就是以某种语言或格式写...
因为解析是渲染引擎中一个非常重要的过程，所以我觉得有必要更加深入的了解一下解析过程。首先来介绍一下浏览器解析的规则。  所谓解析文档其实就是将文档内容转化为代码可以理解和使用的结构。解析的结果通常是一个呈现文档结构的节点树，一般称之为解析树或句法树。  例如，对表达式“2+3-1” 的解析会得到以下树结构    解析语法规则  解析过程是基于文档内容所遵从的语法规则的，也就是以某种语言或格式写成的内容。  解析可以被分解为两个子过程-词法分析（lexical）与句法（syntax）分析。  词法分析过程就是将数据拆分成一些标记，即所使用的语言词汇。举例来说，人类语言就是由字典中的词汇所构成的。  句法分析则是语言语法规则的应用。具体解析过程如下图    解析是一个可迭代的过程，解析器通常会先从词法器获取一个新的标记然后使用语法规则之一匹配对应的标记，如果该语法规则匹配成功，就会生成一个与标记对应 的节点，节点添加到解析树后解析器再重新获取另一个标记。  通常情况下解析树并不是最后的所得结果，解析只是用来将开发者输入的文档转化为另一种格式，典型的如程序的编译过程。编译器将源代码编译成机器码，第一步就是就是构造解析树然后将其转化为机器码文档，如下图所示：    解析示例  我先来创建一个简单的数值语言，然后看一下是怎么进行解析的  词汇：我们的语言单位包括整型，加号和减号，  语法规则：1、使用表达式（expression）、 术语（term）和操作符（operation）表示语句  2、我们的语句可以含有任意多个表达式  3、一个操作对象（term）跟一个操作符再跟上另一个操作对象构成一个表达式  4、操作要么是加法要么是减法  5、一个操作对象就是一个整数（integer）标识或一个表达式。  分析表达式“2+3-1”，第一个符合语法规则的标记就是“2”，根据第5条规则，它属于整数。第二个匹配对象就是“2+3”，它符合第3条规则。最后一条匹配的就是“2+3-1”了，“2+3”我们已知是一个操作对象，那么“2+3-1”就符合第3条规则了。而“2++”根据我们定义的语法规则就是一种无效的输入。  词法通常用正则表达式表示。  例如使我们的语言定义为：  integer：0|[1-9]|[0-9]*  PLUS:+  MINUS:-  如上所示，integers由正则表达式定义  语法通常有一种称之为BNF的格式来表示，我们的语言可以像如下来定义：  expression：=term operation term  operation:=PLUS|MINUS  term:=INTEGER|expression  我们说如果某种语法是上下文自由切换的它就能被规律解析器解析。对上下文自由的语法的直接定义就是该语法能用BNF格式直接表示。如果想获取对该语法的正式化定义可以访问http：en.wikipedia.org/wiki/Context-free-grammar  解析类型  主要存在两种类型的解析-自上而下或自下而上。直观一点来说就是自上而下是先从高层级语法开始匹配，自下而上则是先开始匹配基础语法，只有在基础级语法验证成功后才开始过渡到高层级语法。  在这里我还是举出一个例子来说明这两种类型的解析是如何进行的。  还是以表达式“2+3-1”为例，自上而下解析时，先从高级语法规则开始，他会将“2+3”视作一个表达式，然后将“2+3-1”也看做一个表达式。  自下而上的解析则会先遍历输入一直到找到某条输入满足某个规则。然后继续向后遍历。前面匹配完成所得的结果会放置在一个解析栈（stack）中，具体见下表    这种自下而上的解析也称为移出解析。因为这类解析操作看起来就像是输入内容的逐步右移（可以想象有一个指针指向输入头部然后逐步向后移动）并由语法匹配完成后逐步出栈。  自动生成解析器  有一类工具可以用来生成解析器，一般称之为解析生成器，使用该工具时只需告知其语法规则，它就会生成一个运行中的解析器。如果你想自己建立一个解析器的话就需要你对解析过程有非常深入的了解，而且手动创建一个性能良好的解析器并不是一件容易的事，所以解析生成器是一种非常有用的工具。  webkit使用了两种非常著名的解析生成器-flex，用于创建词法器以及Bison，用于创建解析器。flex输入是一个包含标识符正则表达式定义的文件。bison输入则是以BNF格式写成的语言的语法规则。
展开全文
• 其次，提出了一种丰富的解析树结构，以从解析树中很好地导出必要的结构信息，例如适当的潜在注释。 对ACE RDC语料库的评估表明，新的树核和丰富的解析树结构都对R，DC做出了重要贡献，并且我们的树核方法远远优于...
• ## hive解析树

千次阅读 2016-11-17 15:19:09
Hive的ParseDriver类中，通过antlr生成的语法AST。 例子：Select name,ip from zpc where age > 10 and area in (select area from city) (TOK_QUERY  (TOK_FROM  (TOK_TABREF  (TOK_TABNAME zpc)...
• 形json数据不同于一般的json数据，其大多有多层嵌套。如果用Android Studio中的GsonFormat或者其他工具往往不能正确生成数据格式。//形结构[ { "name": "a", "size": "4&...
• ## 数据结构习题及解析一

千次阅读 多人点赞 2018-12-20 09:51:36
数据结构习题解析解析：本题考点是顺序表的基本特点。 顺序表是在计算机内存中以数组的形式保存的线性表，是指用一组地址连续的存储单元依次存储数据元素的线性结构。线性表采用顺序存储的方式存储就称之为顺序表。...
• [ { "id": "1", "pid": "0", "name": "1989-01-12", "children": [ { "id": "2", "pid": "1", "name": "企划分部二", ...有这么一个json的形结构，想用java去解析他，有没有什么好方法？
• ## js 解析 json 生成树

热门讨论 2010-03-23 14:35:59
都已经封装好了，只需要在test.html里面传递JSON的格式数据就可以了,节点动态生成。
• 用于算术表达式的递归下降解析器，具有解析树和测试的可视化 任务： 为算术表达式（带加号、二元和一元减号、乘法和括号）设计一个简单的上下文无关文法，以消除左递归和右分支 开发一个词法分析器（lexer），跳过...
• 然后使用nltk对这些句子进行语法树解析，打印最终语法列表的长度即可： from nltk import load_parser sentences=['fall leaves fall', 'fall leaves fall and spring leaves spring', 'the fall leaves left'...
• 浏览器解析HTML文档生成DOM的过程 以下是一段HTML代码，以此为例来分析解析HTML文档的原理 &lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt;  &lt;meta ...

...