
发表日期: 2021-05-20 14:04:52 浏览次数:76
昌邑400电话办理【昌邑网站公司】昌邑百度优化、昌邑域名注册、昌邑网店美工、昌邑微信公众号托管

昌邑市属于潍坊市下辖的县级市,位于山东半岛西北部,潍河下游,莱州湾畔,介于北纬36°25′-37°08′,东经119°13′-119°37′之间,东与莱州市、平度市以胶莱河为界,南与高密市、安丘市毗连,西与潍坊市坊子区、寒亭区为邻,北濒莱州湾,属市域总面积1578.7平方公里。 [1-2] 截至2016年,昌邑市辖3个街道、6个镇。
昌邑市属环渤海经济圈,为国务院确定的沿海对外开放城市之一,被誉为中国丝绸之乡、华侨之乡和中国溴·盐之乡, 先后被评为中国北方绿化苗木基地、中国纺织产业基地市、中国超纤产业基地、中国中小城市综合实力百强县市 [3] 、中国最具投资潜力中小城市百强县市 [4] 等荣誉称号。 [5] 2018年10月,昌邑市入选“综合实力百强县”、 [6] 全国投资潜力百强县市、 [7] 全国绿色发展百强县市 [8] 、全国科技创新百强县市、全国新型城镇化质量百强县市 [9] 。 [10] 2019年10月8日,被评为2019年度全国综合实力百强县市、2019年度全国绿色发展百强县市、全国科技创新百强县市 [11] 。 [12-13] 2020年山东省四星级新型智慧城市建设预试点城市, [14] 全国文明城市。 [15]
语义分析就是把词汇进行立体的组合,确定有多重意义的词语最终是什么意思、多个词语之间有什么关系以及又应该再哪里断句等。
在编程语言解释当中,这就是要最终生成语法树的步骤了。不像自然语言,像“从句”这种结构往往最多只有一层,编程语言的各种从属关系更加复杂。
在编程语言的解析中有两个很相似但是又有区别的重要概念:
很多情况下一个语句可能只包含一个表达式,比如console.log(‘hi’);。estree标准当中,这种语句节点称作ExpressionStatement。
语义分析的过程又是个遍历语法单元的过程,不过相比较而言更复杂,因为分词过程中,每个语法单元都是独立平铺的,而语法分析中,语句和表达式会以树状的结构互相包含。针对这种情况我们可以用栈,也可以用递归来实现。
我继续上面的例子给出语义分析的代码,代码很长,先在最开头说明几个函数是做什么的:
这里stash、rewind、commit都跟读取位置暂存相关,什么样的情况会需要返回到暂存点呢?有时同一种语法单元有可能代表不同类型的表达式的开始。先stash,然后按照其中一种尝试解析,如果解析成功了,那么暂存点就没用了,commit将其销毁。如果解析失败了,就用rewind回到原来的位置再按照另一种方式尝试去解析。
以下是代码:
function parse (tokens) {
let i = -1; // 用于标识当前遍历位置
let curToken; // 用于记录当前符号
// 读取下一个语句
function nextStatement () {
// 暂存当前的i,如果无法找到符合条件的情况会需要回到这里
stash();
// 读取下一个符号
nextToken();
if (curToken.type === 'identifier' && curToken.value === 'if') {
// 解析 if 语句
const statement = {
type: 'IfStatement',
};
// if 后面必须紧跟着 (
nextToken();
if (curToken.type !== 'parens' || curToken.value !== '(') {
throw new Error('Expected ( after if');
}
// 后续的一个表达式是 if 的判断条件
statement.test = nextExpression();
// 判断条件之后必须是 )
nextToken();
if (curToken.type !== 'parens' || curToken.value !== ')') {
throw new Error('Expected ) after if test expression');
}
// 下一个语句是 if 成立时执行的语句
statement.consequent = nextStatement();
// 如果下一个符号是 else 就说明还存在 if 不成立时的逻辑
if (curToken === 'identifier' && curToken.value === 'else') {
statement.alternative = nextStatement();
} else {
statement.alternative = null;
}
commit();
return statement;
}
if (curToken.type === 'brace' && curToken.value === '{') {
// 以 { 开头表示是个代码块,我们暂不考虑JSON语法的存在
const statement = {
type: 'BlockStatement',
body: [],
};
while (i < tokens.length) {
// 检查下一个符号是不是 }
stash();
nextToken();
if (curToken.type === 'brace' && curToken.value === '}') {
// } 表示代码块的结尾
commit();
break;
}
// 还原到原来的位置,并将解析的下一个语句加到body
rewind();
statement.body.push(nextStatement());
}
// 代码块语句解析完毕,返回结果
commit();
return statement;
}
// 没有找到特别的语句标志,回到语句开头
rewind();
// 尝试解析单表达式语句
const statement = {
type: 'ExpressionStatement',
expression: nextExpression(),
};
if (statement.expression) {
nextToken();
if (curToken.type !== 'EOF' && curToken.type !== 'sep') {
throw new Error('Missing ; at end of expression');
}
return statement;
}
}
// 读取下一个表达式
function nextExpression () {
nextToken();
if (curToken.type === 'identifier') {
const identifier = {
type: 'Identifier',
name: curToken.value,
};
stash();
nextToken();
if (curToken.type === 'parens' && curToken.value === '(') {
// 如果一个标识符后面紧跟着 ( ,说明是个函数调用表达式
const expr = {
type: 'CallExpression',
caller: identifier,
arguments: [],
};
stash();
nextToken();
if (curToken.type === 'parens' && curToken.value === ')') {
// 如果下一个符合直接就是 ) ,说明没有参数
commit();
} else {
// 读取函数调用参数
rewind();
while (i < tokens.length) {
// 将下一个表达式加到arguments当中
expr.arguments.push(nextExpression());
nextToken();
// 遇到 ) 结束
if (curToken.type === 'parens' && curToken.value === ')') {
break;
}
// 参数间必须以 , 相间隔
if (curToken.type !== 'comma' && curToken.value !== ',') {
throw new Error('Expected , between arguments');
}
}
}
commit();
return expr;
}
rewind();
return identifier;
}
if (curToken.type === 'number' || curToken.type === 'string') {
// 数字或字符串,说明此处是个常量表达式
const literal = {
type: 'Literal',
value: eval(curToken.value),
};
// 但如果下一个符号是运算符,那么这就是个双元运算表达式
// 此处暂不考虑多个运算衔接,或者有变量存在
stash();
nextToken();
if (curToken.type === 'operator') {
commit();
return {
type: 'BinaryExpression',
left: literal,
right: nextExpression(),
};
}
rewind();
return literal;
}
if (curToken.type !== 'EOF') {
throw new Error('Unexpected token ' + curToken.value);
}
}
// 往后移动读取指针,自动跳过空白
function nextToken () {
do {
i++;
curToken = tokens[i] || { type: 'EOF' };
} while (curToken.type === 'whitespace');
}
// 位置暂存栈,用于支持很多时候需要返回到某个之前的位置
const stashStack = [];
function stash (cb) {
// 暂存当前位置
stashStack.push(i);
}
function rewind () {
// 解析失败,回到上一个暂存的位置
i = stashStack.pop();
curToken = tokens[i];
}
function commit () {
// 解析成功,不需要再返回
stashStack.pop();
}
const ast = {
type: 'Program',
body: [],
};
// 逐条解析顶层语句
while (i < tokens.length) {
const statement = nextStatement();
if (!statement) {
break;
}
ast.body.push(statement);
}
return ast;
}
const ast = parse([
{ type: "whitespace", value: "\n" },
{ type: "identifier", value: "if" },
{ type: "whitespace", value: " " },
{ type: "parens", value: "(" },
{ type: "number", value: "1" },
{ type: "whitespace", value: " " },
{ type: "operator", value: ">" },
{ type: "whitespace", value: " " },
{ type: "number", value: "0" },
{ type: "parens", value: ")" },
{ type: "whitespace", value: " " },
{ type: "brace", value: "{" },
{ type: "whitespace", value: "\n " },
{ type: "identifier", value: "alert" },
{ type: "parens", value: "(" },
{ type: "string", value: "\"if 1 > 0\"" },
{ type: "parens", value: ")" },
{ type: "sep", value: ";" },
{ type: "whitespace", value: "\n" },
{ type: "brace", value: "}" },
{ type: "whitespace", value: "\n" },
]);最终得到结果:
{
"type": "Program",
"body": [
{
"type": "IfStatement",
"test": {
"type": "BinaryExpression",
"left": {
"type": "Literal",
"value": 1
},
"right": {
"type": "Literal",
"value": 0
}
},
"consequent": {
"type": "BlockStatement",
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"caller": {
"type": "Identifier",
"value": "alert"
},
"arguments": [
{
"type": "Literal",
"value": "if 1 > 0"
}
]
}
}
]
},
"alternative": null
}
]
}以上就是语义解析的部分主要思路。注意现在的nextExpression已经颇为复杂,但实际实现要比现在这里展示的要更复杂很多,因为这里根本没有考虑单元运算符、运算优先级等等。
真正看下来,其实没有哪个地方的原理特别高深莫测,就是精细活,需要考虑到各种各样的情况。总之要做一个完整的语法解释器需要的是十分的细心与耐心。
在并不是特别远的过去,做web项目,前端技术都还很简单,甚至那时候的网页都尽量不用JavaScript。之后jQuery的诞生真正地让JS成为了web应用开发核心,web前端工程师这种职业也才真正独立出来。但后来随着语言预处理和打包等技术的出现,前端真的是越来越强大但是技术栈也真的是变得越来越复杂。虽然有种永远都学不完的感觉,但这更能体现出我们前端工程存在的价值,不是吗?
服务热线
顶部
备案号: 苏ICP备11067224号
CopyRight © 2011 书生商友信息科技 All Right Reserved
24小时服务热线:400-111-6878 E-MAIL:1120768800@qq.com QQ:1120768800
网址: http://www.768800.com 网站建设:上往建站
关键词: 网站建设| 域名邮箱| 服务器空间| 网站推广| 上往建站| 网站制作| 网站设计| 域名注册| 网络营销| 网站维护|
企业邮箱| 虚拟主机| 网络建站| 网站服务| 网页设计| 网店美工设计| 网站定制| 企业建站| 网站设计制作| 网页制作公司|
400电话办理| 书生商友软件| 葬花网| 调温纤维| 海洋馆运营维护| 北京保安公司| 殡仪馆服务| 殡葬服务| 苏州殡葬一条龙| 朝阳殡葬| 苏州殡葬服务|
欢迎您免费咨询,请填写以下信息,我们收到后会尽快与您联系
服务热线:400-111-6878