编译原理三:词法分析

news2024/11/16 3:47:31

概念

  • 在编译器工作流程中,词法分析是将源代码分解为一系列词法单元的过程。

  • 词法单元包括标识符关键字运算符等。词法分析器会读取源代码的每一个字符根据预定义的规则将它们组成一系列词法单元

  • 词法分析器通常使用有限状态机来实现。有限状态机是一种计算模型,它可以接受一串输入并根据一组状态转移规则进行状态转移,最终输出一个结果。在词法分析器中,有限状态机通常用于匹配输入的字符串。

  • 词法分析器可以快速匹配输入的字符串,并且不需要回溯的操作,因此能够有效提高编译器的词法分析效率

  • 词法分析器通常是编译器的第一个组件,它会将源代码分解为一系列词法单元,然后将这些词法单元传递给语法分析器进行语法分析。词法分析器的设计和实现对编译器的性能和正确性都有重要的影响。

例子:实现一个基本的词法分析器

需求:将<h1 id="title"><span>hello</span>world</h1>转换成一系列词法单元。
最终效果:

在这里插入图片描述

  • 定义类型
// tokenTypes.js
const Punctuator = "Punctuator"; // < > / 等一系列标识符
const JSXIdentifier = "JSXIdentifier"; // html标签
const AttributeKey = "AttributeKey"; // 标签属性的key(如:id, name等)
const AttributeStringValue = "AttributeStringValue"; // 标签属性的value(如:id="title"中的title)
const JSXText = "JSXText"; // 文本(如标签内部的文字)
const BackSlash = "BackSlash"; // 反斜杠(结束标签的/)

// 将类型导出
module.exports = {
  Punctuator,
  JSXIdentifier,
  AttributeKey,
  AttributeStringValue,
  JSXText,
  BackSlash,
};
  • 具体实现
// tokenizer.js

// 导入类型
const {
  Punctuator,
  JSXIdentifier,
  AttributeKey,
  AttributeStringValue,
  JSXText,
  BackSlash,
} = require("./tokenTypes");

// 初始的token
let currentToken = { type: "", value: "" }; 
// 分词数组,最终生成的分词数据都会放在这里
let tokens = []; 
// 定义正则,符合大写字母、小写字母、数字,则做什么事情
const LETTER = /[a-zA-Z0-9]/;

// 主函数
function tokenlizier(input) {
  // 开始状态
  let state = start;
  debugger;
  // 遍历收到的字符
  for (let char of input) {
    state = state(char);
  }
  return tokens;
}

// 首次执行时,匹配到 <
function start(char) {
  if (char === "<") {
    // 将类型及值发射出去,存到 tokens 中
    emit({ type: Punctuator, value: "<" });
    // 找到 < 之后继续收集
    return foundLeftParentheses;
  }
  // 未找到 < 则抛出异常
  throw new Error("第一个字符必须是<");
}

function foundLeftParentheses(char) {
  // char: h1
  if (LETTER.test(char)) {
    currentToken.type = JSXIdentifier;
    currentToken.value += char;
    // 继续收集标识符
    return jsxIdentifier;
  } else if (char === "/") {
    // 区分开始标签和结束标签
    emit({ type: BackSlash, value: "/" });
    return foundLeftParentheses;
  }
  throw TypeError("Error");
}

function jsxIdentifier(char) {
  // char如果是字母或数字就接着增加
  if (LETTER.test(char)) {
    currentToken.value += char;
    // 继续收集标识符
    return jsxIdentifier;
  } else if (char === " ") {
    // 遇到了空格, 标识符结束,将当前收集的token发射出去
    emit(currentToken);
    // 开始收集属性
    return attribute;
  } else if (char === ">") {
    // 开始标签结束了
    emit(currentToken);
    emit({ type: Punctuator, value: ">" });
    // 找到>后继续收集
    return foundRightParentheses;
  }
  throw TypeError("Error");
}

function attribute(char) {
  // char=i
  if (LETTER.test(char)) {
    currentToken.type = AttributeKey;
    currentToken.value += char;
    // 继续收集属性
    return attributeKey;
  }
  throw TypeError("Error");
}

function attributeKey(char) {
  // char=d
  if (LETTER.test(char)) {
    currentToken.value += char;
    // 继续收集
    return attributeKey;
  } else if (char === "=") {
    emit(currentToken);
    // 继续收集属性值
    return attributeValue;
  }
  throw TypeError("Error");
}

function attributeValue(char) {
  // cahr="
  if (char === '"') {
    currentToken.type = AttributeStringValue;
    currentToken.value += char;
    // 开始收集字符串属性值
    return attributeStringValue;
  }
  throw TypeError("Error");
}

function attributeStringValue(char) {
  // char=title
  if (LETTER.test(char)) {
    currentToken.value += char;
    return attributeStringValue;
  } else if (char === '"') {
    emit(currentToken);
    return tryLeaveAttribute;
  }
  throw TypeError("Error");
}

// 属性结束后,可能有新属性,也可以是 >
function tryLeaveAttribute(char) {
  if (char === " ") {
    // 如果是空格的话,说明后面还有一个新属性,再次开始收集属性
    return attribute;
  } else if (char === ">") {
    // 如果是 >,则继续收集>
    emit({ type: Punctuator, value: ">" });
    // 找到 > 后继续收集
    return foundRightParentheses;
  }
  throw TypeError("Error");
}

function foundRightParentheses(char) {
  if (char === "<") {
    emit({ type: Punctuator, value: "<" });
    // 找到 < 之后继续收集
    return foundLeftParentheses;
  } else {
    currentToken.type = JSXText;
    currentToken.value += char;
    return jsxText;
  }
}

function jsxText(char) {
  if (char === "<") {
    emit(currentToken);
    emit({ type: Punctuator, value: "<" });
    return foundLeftParentheses;
  } else {
    currentToken.value += char;
    return jsxText;
  }
}

function emit(token) {
  // 每次发射 token 后就将其加入到 tokens 数组中,并将 currentToken 清空
  currentToken = { type: "", value: "" };
  tokens.push(token);
}

// 测试用例
let sourceCode = `<h1 id="title"><span>hello</span>world</h1>`; // 要分词的 jsx
console.log(tokenlizier(sourceCode));

module.exports = {
  tokenlizier,
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/700942.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何解决在vue3+vite项目中加载不了.tga文件的问题

因项目需求所以需要加载 .tga 类型的文件 import backgroundImage from "/assets/combined_image.tga" import { TGALoader } from "three/addons/loaders/TGALoader.js";const loader new TGALoader(); const texture1 loader.load(backgroundImage);但…

操作系统5:线程的概念和线程实现

目录 1、线程的概念 &#xff08;1&#xff09;进程和进程并发是所需要付出的时空开销 &#xff08;2&#xff09;线程和进程的比较 &#xff08;3&#xff09;线程的状态和线程的控制块 2、线程的实现 &#xff08;1&#xff09;线程的实现方式 &#xff08;2&#xff…

2.编程语言的代码规则和规范

**格式和规范**只需要记住最原始的c和c的语言规则就可以了不要把所有的方法都掌握了 比如孔乙己 每个语法之间要有空格或分隔符 2。“”英文双引号里面是不需要的&#xff0c;这个双引号里面输入什么都可以 3.预处理的命令 必须一条占一行

传统美业如何拓客引流?如何借力小程序实现低成本引流与锁客

​传统门店的经营方式主要靠广告投入和口碑传播&#xff0c;然而&#xff0c;门店客流寥寥无几&#xff0c;如今&#xff0c;移动互联网时代&#xff0c;新零售的盛行&#xff0c;传统实体门店急需转型&#xff0c;接轨新的商业营销模式来为门店引流。 处在小程序爆发的风口&a…

《PyTorch深度学习实践》第四讲 反向传播

b站刘二大人《PyTorch深度学习实践》课程第四讲反向传播笔记与代码&#xff1a;https://www.bilibili.com/video/BV1Y7411d7Ys?p4&vd_sourceb17f113d28933824d753a0915d5e3a90 对于上述简单的模型可以用解析式来做&#xff0c;但是对于复杂模型而言&#xff0c;如下图每个…

详解线程池的作用和实际应用以及拒绝策略

目录 线程池的作用&#xff1f; 线程池的意义&#xff1a; 线程池的参数 ​编辑 线程池任务执行的顺序 线程池拒绝策略 四种策略 应用场景分析 AbortPolicy DiscardPolicy DiscardOldestPolicy CallerRunsPolicy 线程池的作用&#xff1f; 优化系统架构通常包括在时间…

亚马逊平台买家注册流程

在亚马逊平台注册买家号是比较简单的。以下是亚马逊买家注册流程&#xff1a; 1、打开亚马逊网站&#xff1a;访问亚马逊的官方网站&#xff0c;如果要注册美国买家号&#xff0c;那么网址就是www.amazon.com。 2、点击"注册"&#xff1a;在亚马逊首页的右上角&…

LLM - 搭建 ProteinGPT 结合蛋白质结构 PDB 知识的行业 ChatGPT 系统

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/131403263 论文&#xff1a;ProteinChat: Towards Enabling ChatGPT-Like Capabilities on Protein 3D Structures 工程&#xff1a;ht…

C语言学习(二十九)---内存操作函数

在上一节内容中&#xff0c;我们学习了有关字符串操作的函数&#xff0c;其中分为了限制长度和不限制长度两种方式&#xff0c;虽然上节内容已经在很大程度上有助于程序的实现&#xff0c;但是其有一个致命的缺陷&#xff0c;聪明的你一定已经猜到了吧&#xff0c;对的&#xf…

Linux 网络通信C/S、TCP/IP、Socket 最全详解( 9 ) -【Linux通信架构系列 】

系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 期待你的关注哦&#xff01;&#xff01;&#xff01; 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the…

【算法题】动态规划中级阶段之不同的二叉搜索树、交错字符串

动态规划中级阶段 前言一、不同的二叉搜索树1.1、思路1.2、代码实现 二、不同的二叉搜索树 II2.1、思路2.2、代码实现 三、交错字符串3.1、思路3.2、代码实现 总结 前言 动态规划&#xff08;Dynamic Programming&#xff0c;简称 DP&#xff09;是一种解决多阶段决策过程最优…

Pycharm中成功配置PyQt5(External Tools),设计好界面直接生成python代码

1、安装PyQt5和PyQt5-tools 在Pycharm中设置好Python环境&#xff0c;点击File-Settings-Project-Python Interpreter 设置好后退出&#xff0c;点击窗口下的Terminal&#xff0c;输入 # 直接安装输入pip install pyqt5&#xff0c;如果太慢可以用国内镜像源&#xff0c;若出…

【C++实现二叉树的遍历】

目录 一、二叉树的结构二、二叉树的遍历方式三、源码 一、二叉树的结构 二、二叉树的遍历方式 先序遍历&#xff1a; 根–>左–>右中序遍历&#xff1a; 左–>根–>右后序遍历&#xff1a;左–>右–>根层次遍历&#xff1a;顶层–>底层 三、源码 注&am…

SpringBoot04:JSR303数据校验及多环境切换

目录 一、JSR303数据校验 1、如何使用&#xff1f; 2、常见参数 二、多环境切换 1、多配置文件 2、yaml的多文档块 3、配置文件加载位置 一、JSR303数据校验 1、如何使用&#xff1f; SpringBoot中可以用Validated来校验数据&#xff0c;如果数据异常则会统一抛出异常…

python篇---统计列表中每个数字的出现次数

python篇—统计列表中每个数字的出现次数 # -*- coding: utf-8 -*- from collections import Counterlst [1, 2, 3, 3, 4, 1, 2, 5, 5, 5] count Counter(lst) print(每个数字在列表中的出现次数&#xff1a;, count) # 再将collections.Counter格式转换成dict print(dict(c…

C# 难点语法讲解之虚方法(virtual)和隐藏方法的区别---从应用需求开始讲解

这里不单独讲虚方法和隐藏方法是什么&#xff0c;很多文章都有讲&#xff0c;这里只讲他们的区别和应用理解。 另外&#xff1a;如果你不懂MonoBehaviour就别管他&#xff0c; Debug.Log就是Console.WriteLine <一>、隐藏方法 一、隐藏方法的背景故事 从前有个了不起…

C++ | 多线程资源抢占bug解决

多线程资源抢占bug解决 文章目录 多线程资源抢占bug解决bug说明原因排查解决经验>>>>> 欢迎关注公众号【三戒纪元】 <<<<< bug说明 最近调试程序&#xff0c;程序在Release版本下可运行&#xff0c;一直没有问题&#xff0c;在Debug模式下编译后…

Leetcode42 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图&#xf…

【javascript】防止内容被复制

在JavaScript中&#xff0c;我们可以使用onselectstart事件来防止页面内容被选取。此时无法选取所要的内容。 代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><script>window.onload function() {document.bod…

计算机网络————网络层

文章目录 网络层设计思路IP地址IP地址分类IP地址与硬件地址 协议ARP和RARPIP划分子网和构造超网划分子网构造超网&#xff08;无分类编址CIDR&#xff09; ICMP 虚拟专用网VPN和网络地址转换NATVPNNAT 网络层设计思路 网络层向上只提供简单灵活的、无连接的、尽最大努力交付的数…