MyLisp项目日志:解析用户输入与波兰表达式
文章目录
- 编程语言
- 模拟自然语言
- 定义名词和形容词
- 定义短语
- 定义句子
- 简化模拟过程
- 正则表达式
- 波兰表达式及其解析
- 波兰表达式
- 语法描述
- 波兰表达式语法解析
- 解析用户输入
- v0.0.2
编程语言
编程语言是类似于自然语言的,虽然我们是自然而然就学会了自己的母语,但实际上自然语言是建立在递归和重复的子结构之上的
在语文的学习过程中我们会学到一些语法,例如名词、动词、谓语、连词
他的结构体现在,例如谓语可以是形容词,是动词,是副词加动词
他的递归体现在,主语可以是名词,可以是形容词加名词,甚至是一个句子,再由这个主语去构成另一个句子
这些说白了是一种有限的规则,而由这些优先的规则延申出来的就是无限的句子
为了定义我们的编程语言(MyLisp),我们首先需要正确解析用户按照语法规则写的代码,因此我们就需要编写一个语法解析器,用于判断用户的输入是否合法,并且产生解析后的内部表示
但是这一步十分繁琐且枯燥,我们使用mpc库来帮助我们完成这个工作,这个库是一个解析器组合子库,可以为任何语言编写语法解析器,他极大的简化了原本枯燥无聊的工作,仅仅编写高层的抽象语法规则即可
mpc库的项目地址,非常感谢Daniel Holden大佬!!
模拟自然语言
下面我们将使用mpc的一些函数,尝试描述英语的部分语法,熟悉mpc的使用步骤,更多的函数请移步项目主页
我们只采用最基础的语法,以后如果想完善的话只需要添加规则即可
大致规则框架是这样的
- 句子(Sentence)包括一系列短语(Phrase)
- 短语(Phrase)由形容词(Adjective)和名词(Noun)构成
- 名词(Noun)包括以下几个符号:lisp,language,c,book,build
- 形容词(Adjective)包括以下几个符号:many,so,much
那我们就需要讲上述描述使用mpc库中的函数写出来,这样就能构建出一个解析系统了,只要满足上面的语法规则,我们就可以进行解析
定义名词和形容词
我们需要创建两个解析器,类型是mpc_parser_t*
,分别命名为Noun和Adjective,作为两个变量
再使用mpc_or
函数表示创建一个解析器,返回值是解析器,参数是解析的符号(单词),而这些单词之间是或的关系,只要出现一个即可,使用mpc_sym
函数,他会将字符串转换为符号(语句)
写成代码就是下面的样子
// 建立名词解析器
mpc_parser_t* Noun = mpc_or(4,mpc_sym("lisp"), mpc_sym("language"),mpc_sym("c"), mpc_sym("book"),mpc_sym("build"));// 建立形容词解析器
mpc_parser_t* Adjective = mpc_or(3,mpc_sym("many"), mpc_sym("so"), mpc_sym("much"));
定义短语
接下来我们就要使用名词解析器和形容词解析器来构造短语解析器
同理我们直接使用mpc_and
函数即可,然后我们再用mpcf_strfold
和free
指定各个语句的组织和删除方式,暂时先忽略他们
mpc_parser_t* Phrase = mpc_and(2, mpcf_strfold, Adjective, Noun, free);
定义句子
句子是由多个短语构成的,我们使用mpc_many
函数即可
mpc_parser_t* Sentence = mpc_many(mpcf_strfold, Phrase);
简化模拟过程
mpc有一个mpca_lang函数可以简化这个编写的过程,不用再重复的写许多mpc_sym函数,而是允许我们直接用字符串来表示
mpc_parser_t* Adjective = mpc_new("adjective");
mpc_parser_t* Noun = mpc_new("noun");
mpc_parser_t* Phrase = mpc_new("phrase");
mpc_parser_t* Sentence = mpc_new("sentence");mpca_lang(MPCA_LANG_DEFAULT," \adjective : \"such\" | \"many\" \| \"so\"; \noun : \"lisp\" | \"language\" \| \"book\" | \"build\" | \"c\"; \phrase : <adjective> <noun>; \sentence : <phrase>*; \",Adjective, Noun, Phrase, Sentence);mpc_cleanup(4, Adjective, Noun, Phrase, Sentence);
那整个定义解析的过程就可以分成三步
- mpc_new定义语法规则的名字
- mpca_lang定义具体的连接规则
- mpc_cleanup清理
字符串中有一些要求,一个字符串可以有很多语法规则,每一个语法规则分为左右两部分,左边是名称,右边是具体的内容,两项之间用竖划线分割开,使用分号作为标记结尾,需要注意的是,在这里我们只想使用字符串含义的双引号,因此要使用转义字符
正则表达式
我们当然可以使用字符串来表示语法,但是这样的输入始终是有限的,还有一些我们无法表达的开始输入,结束输入,可选字符,可选范围
这时候就需要我们使用正则表达式(Regular Expression)来定义,他是一直小型的语法规则,可以用于表达整数,浮点数等一些简单的规则
他的具体规则如下
语法表示 | 作用 |
---|---|
. | 要求任意字符 |
a | 要求字符 a |
[abcdef] | 要求 abcdef 中的任意一个 |
[a-f] | 要求按照字母顺序,a 到 f 中的任意一个 |
a? | 要求 a 字符或什么都没有,即 a 为可选的 |
a* | 要求有 0 个或多个字符 a |
a+ | 要求有 1 个或多个字符 a |
^ | 开始输入 |
$ | 结束输入 |
上面是一些我们会用到的规则,具体的完整教程仍需要额外学习
在这里,我们需要将正则表达式放在一对/
中
例如,整数的表达/-?[0-9]+/
波兰表达式及其解析
这里我们将会使用上面学习的内容和mpc库实现一个简单的波兰表达式的解析器
波兰表达式
波兰表达式也称之为前缀表达式,是一种数学标记语言,简单说就是他的运算符在操作数的前面
例如
普通表达式 | 波兰表达式 |
---|---|
1 + 2 + 6 | + 1 2 6 |
6 + (2 * 9) | + 6 (* 2 9) |
(10 * 2) / (4 + 2) | / (* 10 2) (+ 4 2) |
语法描述
我们可以直观的从表格中提取一些信息,一共有三类符号,操作符,数字,括号
我们可以细看括号内的部分,其实也就是表达式,而这一个整体也可以看作一个表达式
因此我们可以递归下去,得出以下规则
- 一个程序(波兰表达式)由一个操作符加上一个或多个表达式构成
- 一个表达式又由操作符加上数字或者表达式构成
具体下来就是这样的
名称 | 定义 |
---|---|
程序(Program ) | 开始输入 --> 操作符 --> 一个或多个表达式(Expression) --> 结束输入 |
表达式(Expression) | 数字、左括号 (、操作符 --> 一个或多个表达式 --> 右括号 ) |
操作符(Operator) | '+'、'-'、'*' 、 '/' |
数字(Number ) | 可选的负号 - --> 一个或多个 0 到 9 之间的字符 |
当然我们可以将这个操作符和数字做扩展,例如支持取模操作,最大最小值操作,数字加上浮点数,都是可以的
波兰表达式语法解析
接下来我们将使用mpc描述上面的语法规则,这里我直接加上了其他的操作符,包含了浮点数
// 创建解析器
mpc_parser_t* Number = mpc_new("number");
mpc_parser_t* Operator = mpc_new("operator");
mpc_parser_t* Expr = mpc_new("expr");
mpc_parser_t* MyLisp = mpc_new("mylisp");// 定义mpca_lang(MPCA_LANG_DEFAULT," \number : /-?[0-9]+(\\.[0-9]*)?/ ; \operator : '+' | '-' | '*' | '/' | '%' | /add|sub|mul|div/ ; \expr : <number> | '(' <operator> <expr>+ ')' ; \lispy : /^/ <operator> <expr>+ /$/ ; \",Number, Operator, Expr, MyLisp);mpc_cleanup(4, Number, Operator, Expr, MyLisp);
解析用户输入
这里我们先只进行语法解析,下一篇会进行表达式的计算
我们调用mpc_parse函数,将MyLisp解析器和用户输入input作为参数,将他的解析结果保存到ret中,如果解析成功,返回值为1,失败为0
// 尝试分析用户输入
mpc_result_t r;
if (mpc_parse("<stdin>", input, MyLisp, &r))
{// 成功打印ASTmpc_ast_print(r.output);mpc_ast_delete(r.output);
}
else
{// 其他打印失败的情况mpc_err_print(r.error);mpc_err_delete(r.error);
}
当解析成功,则会打印出AST(抽象语法树),这个东西不好解释,但是我们可以通过输出大致看出来语法之间的关系
当解析失败就会输出报错的信息
例如
MyLisp Version 0.0.2
By jasmine-leaf
Press Ctrl+c to ExitMyLisp> + 1 1
>regexoperator|char:1:1 '+'expr|number|regex:1:3 '1'expr|number|regex:1:5 '1'regex
MyLisp> * 1 ( % 2 2 )
>regexoperator|char:1:1 '*'expr|number|regex:1:3 '1'expr|>char:1:5 '('operator|char:1:7 '%'expr|number|regex:1:9 '2'expr|number|regex:1:11 '2'char:1:13 ')'regex
MyLisp> / 1.1 5
>regexoperator|char:1:1 '/'expr|number|regex:1:3 '1.1'expr|number|regex:1:7 '5'regex
MyLisp> hello
<stdin>:1:1: error: expected '+', '-', '*', '/', '%', 'a', 's', 'm' or 'd' at 'h'
MyLisp> 1jas
<stdin>:1:1: error: expected '+', '-', '*', '/', '%', 'a', 's', 'm' or 'd' at '1'
MyLisp> add 2 ( mul 2 2)
>regexoperator|regex:1:1 'add'expr|number|regex:1:5 '2'expr|>char:1:7 '('operator|regex:1:9 'mul'expr|number|regex:1:13 '2'expr|number|regex:1:15 '2'char:1:16 ')'regex
MyLisp>
v0.0.2
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include"mpc.h"
void PrintPrompt()
{// printf("MyLisp Version 0.0.1\n");printf("MyLisp Version 0.0.2\n");printf("By jasmine-leaf\n");printf("Press Ctrl+c to Exit\n\n\n");
}
// v0.0.1
// 实现了用户输入和读取功能
// v0.0.2
// 增加了波兰表达式的解析功能#ifdef _WIN32 // 为实现跨平台功能
// 在windows平台下定义实现editline和history的同名函数#define INPUT_MAX 2048 // 缓冲区最大值#include<string.h>
static char Buffer[INPUT_MAX]; // Buffer输入缓冲区char* readline(char* prompt) // 模拟实现readline
{fputs(prompt, stdout);fgets(Buffer, INPUT_MAX, stdin);char* tmp = malloc(strlen(Buffer) + 1);if (tmp != NULL){strcpy(tmp, Buffer);tmp[strlen(tmp) - 1] = '\0';}return tmp;
}void add_history(char* unused)
{}#else
#ifdef __linux__ // 在linux平台下
#include<editline/readline.h>
#include<editline.history.h>
#endif#ifdef __MACH__ // 在mac平台下
#include<editline/readline.h>
#endif
#endif void Lisp()
{// 创建解析器mpc_parser_t* Number = mpc_new("number");mpc_parser_t* Operator = mpc_new("operator");mpc_parser_t* Expr = mpc_new("expr");mpc_parser_t* MyLisp = mpc_new("mylisp");// 定义mpca_lang(MPCA_LANG_DEFAULT," \number : /-?[0-9]+(\\.[0-9]*)?/ ; \operator : '+' | '-' | '*' | '/' | '%' | /add|sub|mul|div/ ; \expr : <number> | '(' <operator> <expr>+ ')' ; \lispy : /^/ <operator> <expr>+ /$/ ; \",Number, Operator, Expr, MyLisp);while (1){char* input = readline("MyLisp> ");add_history(input);// 尝试分析用户输入mpc_result_t r;if (mpc_parse("<stdin>", input, MyLisp, &r)){// 成功打印ASTmpc_ast_print(r.output);mpc_ast_delete(r.output);}else{// 其他打印失败的情况mpc_err_print(r.error);mpc_err_delete(r.error);}free(input); // 释放在readline中malloc的空间}mpc_cleanup(4, Number, Operator, Expr, MyLisp);
}int main()
{PrintPrompt();Lisp();return 0;
}
相关文章:
MyLisp项目日志:解析用户输入与波兰表达式
文章目录 编程语言模拟自然语言定义名词和形容词定义短语定义句子 简化模拟过程正则表达式 波兰表达式及其解析波兰表达式语法描述波兰表达式语法解析解析用户输入 v0.0.2 编程语言 编程语言是类似于自然语言的,虽然我们是自然而然就学会了自己的母语,但…...

torch.backends.cudnn.benchmark 作用
相关参数 torch.backends.cudnn.enabled torch.backends.cudnn.benchmark torch.backends.cudnn.deterministictorch.backends.cudnn.benchmark True:将会让程序在开始时花费一点额外时间,为整个网络的每个卷积层搜索最适合它的卷积实现算法,…...
vue的$nextTick应用场景
文章目录 $nextTick有什么作用?一、NextTick是什么二、为什么要有nextTick? $nextTick有什么作用? 一、NextTick是什么 官方对其的定义 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的…...
springboot RestTemplate 发送xml、接收xml、pojo中的属性转为属性
背景 调用第三方接口时,它们的系统比较老,只支持接收xml而不支持json,默认的springboot RestTemplate不支持发送xml,添加依赖就可以解决这个问题。 添加jackson-dataformat-xml依赖 FasterXML/jackson-dataformat-xml是一个xml…...
Lua-Lua与C++的交互2
Lua与C的交互是指在C程序中使用Lua本语言,或者在Lua脚本中调用C代码的过程。这种交互可以实现C与Lua之间的数据传递和函数调用。 在C中与Lua交互的主要步骤如下: 引入Lua库:首先需要在C程序中引入Lua的头文件和库文件,以便能够使…...

学python新手如何安装pycharm;python小白如何安装pycharm
首先找到官网: Download PyCharm: The Python IDE for data science and web development by JetBrains 打开后选择下载,下图标红部分 点击exe程序,点击下一步! 选择安装路径,下一步 弹出界面全选 选择默认 然后直接…...

Oracle Primavera P6 数据库升级
前言 为了模拟各种P6测试,我常常会安装各种不同版本的p6系统,无论是P6服务,亦或是P6客户端工具Professional,在今天操作p6使用时,无意识到安装在本地的P6 数据库(21.12)出现了与Professional软…...
共享库的创建gcc选项“-shared -fPIC -WI”
共享库的创建非常简单,最关键的是gcc的几个参数: “-shared”: 表示输出结果是共享库类型。编译选项告诉编译器生成一个共享库(也称为动态链接库或 DLL)。共享库是一种包含可重用代码和数据的二进制文件,…...

微服务:Bot代码执行
每次要多传一个bot_id 判网关的时候判127.0.0.1所以最好改localhost 创建SpringCloud的子项目 BotRunningSystem 在BotRunningSystem项目中添加依赖: joor-java-8 可动态编译Java代码 2. 修改前端,传入对Bot的选择操作 package com.kob.botrunningsy…...

Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 3、线条平滑曲面但有条纹
环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata imp…...

Vue.js+SpringBoot开发数字化社区网格管理系统
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、开发背景四、系统展示五、核心源码5.1 查询企事业单位5.2 查询流动人口5.3 查询精准扶贫5.4 查询案件5.5 查询人口 六、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数字化社区网格管理系统…...

java SSM农产品订购网站系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计
一、源码特点 java SSM农产品订购网站系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采…...
vsto快速在excel中查找某个字符串
是的,使用foreach循环遍历 Excel.Range 可能会较慢,特别是在大型数据集上。为了提高效率,你可以考虑使用 Value 属性一次性获取整个范围的值,然后在内存中搜索文本。这样可以减少与 Excel 之间的交互次数,提高性能。 …...

Unity类银河恶魔城学习记录10-1 10-2 P89,90 Character stats - Stat script源代码
Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Stat.cs using System.Collections; using System.Collections.Generic; us…...

西门子TIA中配置Anybus PROFINET IO Slave 模块
1、所需产品 Siemens S7 PLC CPU 315-2 PN/DP 6ES7 315-2EH-0AB0 Siemens PLC 编程电缆 n.a. n.a. PC ,并安装Siemens PLC编程软件 TIA Portal V11 X-gateway Slave 接口的GSDML文件 根据网关的软件版本而定 Anybus Communicator GSD文件 GSDML-V1.0-HMS-ABCPRT-20050317.xl…...

在 Rust 中使用 Serde 处理json
在 Rust 中使用 Serde 处理json 在本文中,我们将讨论 Serde、如何在 Rust 应用程序中使用它以及一些更高级的提示和技巧。 什么是serde? Rust中的serde crate用于高效地序列化和反序列化多种格式的数据。它通过提供两个可以使用的traits来实现这一点&a…...
【数据库】数据库介绍
文章目录 一、数据库介绍二、SQL分类 一、数据库介绍 什么是数据库 存储数据用文件就可以了,为什么还要弄个数据库? 文件保存数据有以下几个缺点: 文件的安全性问题 文件不利于数据查询和管理 文件不利于存储海量数据 文件在程序中控制不方便 数据库存…...
python 第三方库(PyPinyin\shortuuid\json)
PyPinyin库 简介 PyPinyin库是一个支持中文转拼音输出的Python第三方库,它可以根据词组智能匹配最正确的拼音,并且支持多音字,简单的繁体, 注音,多种不同拼音/注音风格的转换。 安装 (framework-learn) C:\Users\zzg>pip …...
一文解读ISO26262安全标准:术语(二)
一文解读ISO26262安全标准:术语(二) 本文继续补充一些标准中的术语,方便后续文章内容的有效理解。 分支覆盖率 branch coverage 控制流分支覆盖的比率. 100%分支覆盖率意味着100%语句覆盖率,比如,一个if语句…...

【Datawhale学习笔记】从大模型到AgentScope
从大模型到AgentScope AgentScope是一款全新的Multi-Agent框架,专为应用开发者打造,旨在提供高易用、高可靠的编程体验! 高易用:AgentScope支持纯Python编程,提供多种语法工具实现灵活的应用流程编排,内置…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...