前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >手摸手实现一个编译器(上)

手摸手实现一个编译器(上)

作者头像
码农小余
发布于 2022-06-16 08:42:05
发布于 2022-06-16 08:42:05
80600
代码可运行
举报
文章被收录于专栏:码农小余码农小余
运行总次数:0
代码可运行

认识 PEG.js

PEG.js 是一个简单的 JavaScript 解析器生成器,可以生成具有出色错误报告的快速解析器。您可以使用它来处理复杂的数据或计算机语言,并轻松构建转换器、解释器编译器和其他工具。

我们先简单了解解释器和编译器的定义:

解释器interpreter)是一种计算机程序,能够把解释型语言解释执行。解释器就像一位“中间人”。解释器逐行边解释边执行,因此依赖于解释器的程序运行速度比较缓慢。解释器的好处是它不需要重新编译整个程序,从而减轻了每次程序更新后编译的负担。 编译器(compiler)是一种计算机程序,它会将某种编程语言写成的源代码(原始语言)转换成另一种编程语言(目标语言)。

二者的区别主要有:

  • 编译器将一个程序作为一个整体进行翻译,而解释器则是一行一行地翻译;
  • 在编译器的情况下生成中间代码或目标代码。而解释器不创建中间代码;
  • 编译器比解释器要快得多,因为编译器一次完成整个程序,而解释器则是依次编译每一行代码;
  • 由于要生成目标代码,编译器比解释器需要更多的内存
  • 在编译器中,当程序中出现错误时,它会停止翻译,并在删除错误后重新翻译整个程序。相反,当解释器中发生错误时,它会阻止其翻译,在删除错误后,翻译将继续;
  • 编译器用于编程语言,如 cc++c#Scala 等。另一个解释器用于 PHPRubyPythonJavaScript 等语言。
How?

PEG.js 可用于 node 和浏览器环境,安装就跟普通的包没有任何区别:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 通过 CLI 去生成编译器
npm install -g pegjs

# 通过 JavaScript API 去生成编译器时选择本地安装,因为你要引入包
npm install pegjs

本文就只演示 CLI 去生成编译器的用法,JavaScript API 在官方文档中有说明,参数都是一致的。新建一个 simple-arithmetics.pegjs 文件,写入官方 DEMO 的规则:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Simple Arithmetics Grammar
// ==========================
//
// Accepts expressions like "2 * (3 + 4)" and computes their value.

Expression
  = head:Term tail:(_ ("+" / "-") _ Term)* {
      return tail.reduce(function(result, element) {
        if (element[1] === "+") { return result + element[3]; }
        if (element[1] === "-") { return result - element[3]; }
      }, head);
    }

Term
  = head:Factor tail:(_ ("*" / "/") _ Factor)* {
      return tail.reduce(function(result, element) {
        if (element[1] === "*") { return result * element[3]; }
        if (element[1] === "/") { return result / element[3]; }
      }, head);
    }

Factor
  = "(" _ expr:Expression _ ")" { return expr; }
  / Integer

Integer "integer"
  = _ [0-9]+ { return parseInt(text(), 10); }

_ "whitespace"
  = [ \t\n\r]*

然后执行以下命令:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pegjs simple-arithmetics.pegjs

就会在当前目录下生成一个 simple-arithmetics.js 文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
 * Generated by PEG.js 0.10.0.
 *
 * http://pegjs.org/
 */

"use strict";

function peg$subclass(child, parent) {
  function ctor() { this.constructor = child; }
  ctor.prototype = parent.prototype;
  child.prototype = new ctor();
}

function peg$SyntaxError(message, expected, found, location) {
  // ...
}

peg$subclass(peg$SyntaxError, Error);

peg$SyntaxError.buildMessage = function(expected, found) {
   // ...
};

function peg$parse(input, options) {
  // ...
}

module.exports = {
  SyntaxError: peg$SyntaxError,
  parse:       peg$parse
};

省略了大部分核心代码,看下输出代码的结构,用 CJS 导出了 parseSyntaxError 函数。我们再新建一个 test.js 文件引用这个编译器去解析我们的表达式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const { parse } = require('./simple-arithmetics')
console.log(parse('2*(3+4)'))  // 14

到这里,一个支持简单算术运算的编译器就完成了。我们先在解读具体的语法和词法解析前,先来了解一下输出编译器的参数:

--allowed-start-rules

默认值以 Grammer 第一条规则作为起始解析。参数格式是数组,在 CLI 中用 , 连接多个规则开头名称,举个例子,我们有一下的 Grammer 定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
middle
 = end '*'

start
 = [a-z] middle

end
 = [1-9]

如果我们生成 parser 不传 --allowed-start-rules 时,即直接执行下面命令:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pegjs ./simple-arithmetics.pegjs

那么生成的解析器会以 middle 作为语法入口,我们测试一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const { parse } = require('./simple-arithmetics')
console.log(parse('1*'))  // [ '1', '*' ]
console.log(parse('a1*')) // peg$SyntaxError: Expected [1-9] but "a" found.

可以看出,解析是从第一行开始的(即 middle 规则)。如果想要达到我们的预期,比如 start-middle-end 顺序,那么我们可以加上 --allowed-start-rules 参数并且指定 start

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pegjs --allowed-start-rules start ./simple-arithmetics.pegjs

生成的解析器再来解析上述代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const { parse } = require('./simple-arithmetics')
// ⚠️ 这里的顺序跟上面有区别,因为解析失败之后会throw Error,所以正确的语法提上来
console.log(parse('a1*'))  // [ 'a', [ '1', '*' ] ]
console.log(parse('1*'))    // peg$SyntaxError: Expected [a-z] but "1" found.
--cache

让解析器缓存解析结果,优化性能。

--dependency

指定解析器的外部依赖。比如指定了 --dependency ast:./ast.js ,那么生成的解析器中就会引入 ast.js 文件,你可以使用模块中的导出的任意方法。

--export-var

当没有检测到模块加载器时解析器对象被分配到的全局变量的名称。

--extra-options

指定传给 peg.generate 函数的参数。

--extra-options-file

如果参数太多,在 CLI 中输入确实很不方便,也不够直观。这时通过指定一个 JSON 格式的文件作为 peg.generate 参数。

--format

指定生成器的格式,支持 amdcommonjsglobalsumd,其中 commonjs 是默认值。

--optimize

在优化生成的解析器的解析速度 ( speed) 或代码大小 ( size) 之间进行选择(默认值: speed

--plugin

指定 PEG.js 使用具体的插件。

--trace

配置这个参数之后,支持让解析器在解析的过程中显示详细的进度。

编译参数用的不多,简单了解一下即可。

语法和语义

下面我们来解读一下官方的算术解析,从而认识语法和语义和一些表达式的使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Simple Arithmetics Grammar
// ==========================
//
// Accepts expressions like "2 * (3 + 4)" and computes their value.

Expression
  = head:Term tail:(_ @("+" / "-") _ @Term)* {
      return tail.reduce(function(result, element) {
        if (element[0] === "+") return result + element[1];
        if (element[0] === "-") return result - element[1];
      }, head);
    }

Term
  = head:Factor tail:(_ @("*" / "/") _ @Factor)* {
      return tail.reduce(function(result, element) {
        if (element[0] === "*") return result * element[1];
        if (element[0] === "/") return result / element[1];
      }, head);
    }

Factor
  = "(" _ @Expression _ ")"
  / Integer

Integer "integer"
  = _ [0-9]+ { return parseInt(text(), 10); }

_ "whitespace"
  = [ \t\n\r]*

首先,定义了 5 个规则,每个规则都有自己的名称(比如 Expression 即表达式)和对应的解析表达式。输入文本如果匹配上了表达式,就会执行后面的 JS 函数。像 Integer "integer" 还有明确的错误消息,啥意思呢?举个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
middle "middle"
= end '*'

start 
= [a-z] middle

end
= [1-9]

基于上述规则生成一个 parser 去解析 "a1!",我们获取的错误信息是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
peg$SyntaxError: Expected middle but "1" found.

上述这个 Expected middle 就是我们设置的可读的错误信息。如果去掉 middle,那么就会报下面的错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
peg$SyntaxError: Expected "*" but "!" found.

这也是 PEG.js 的特性之一,它能准确的给出匹配表达式的错误。为了更好地学习表达式类型,上述算术的 Grammer 可能不太合适,接下来我们一起来看另外一个例子——解析 JSON串:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// JSON Grammar
// ============
//
// Based on the grammar from RFC 7159 [1].
//
// Note that JSON is also specified in ECMA-262 [2], ECMA-404 [3], and on the
// JSON website [4] (somewhat informally). The RFC seems the most authoritative
// source, which is confirmed e.g. by [5].
//
// [1] http://tools.ietf.org/html/rfc7159
// [2] http://www.ecma-international.org/publications/standards/Ecma-262.htm
// [3] http://www.ecma-international.org/publications/standards/Ecma-404.htm
// [4] http://json.org/
// [5] https://www.tbray.org/ongoing/When/201x/2014/03/05/RFC7159-JSON

// ----- 2. JSON Grammar -----

// value 的表达式是任意空格加value,处理函数直接返回value
// 函数体内的 value 是表达式 value:value 的前者,后者从其他规则中获取
JSON_text
  = ws value:value ws { return value; }

begin_array     = ws "[" ws
begin_object    = ws "{" ws
end_array       = ws "]" ws
end_object      = ws "}" ws
name_separator  = ws ":" ws
value_separator = ws "," ws

// ws 有一个别名 whitespace,在报错时更加语义化
ws "whitespace" = [ \t\n\r]*

// ----- 3. Values -----

// 表达式的 / 表示优先匹配 false
// 匹配不成功就匹配 null
// 再不成功就匹配 true
// ...依次匹配
// 匹配到 string 都没有匹配成功就认为失败
value
  = false
  / null
  / true
  / object
  / array
  / number
  / string

// 如果是以下字符串,则会做去字符串化 
false = "false" { return false; }
null  = "null"  { return null;  }
true  = "true"  { return true;  }

// ----- 4. Objects -----
// 匹配对象,优先匹配一个 {
// 内部结构体也就是 members 的匹配表达式是
// 先是一个 member,即 {name: "xx", value: "yy"} 结构
// 然后 * 表示匹配 0 或多次,就是说 {name: "xx", value: "yy"},{name: "xx2", value: "yy2"} 匹配多次
// 然后调用函数去转成 { "xx": "yy", "xx2": "yy2" } 的结构
// 再接下来就是一个 ?,表示尝试匹配表达式。如果匹配成功,返回匹配结果,否则返回null。与正则表达式不同,没有回溯。
// 最后就是 }
// 整个表达式再做 members 是否为空的判断,是的话置为 {}
object
  = begin_object
    members:(
      head:member
      tail:(value_separator m:member { return m; })*
      {
        var result = {};

        [head].concat(tail).forEach(function(element) {
          result[element.name] = element.value;
        });

        return result;
      }
    )?
    end_object
    { return members !== null ? members: {}; }

// 对象中的成员的匹配表达式,举例如: “name”: "小余"
// 一个字符串 + : + 一个 value 值
// 最后返回 {name, value} 的结构
member
  = name:string name_separator value:value {
      return { name: name, value: value };
    }

// ----- 5. Arrays -----
// 匹配数组的表达式 [1, 2, 3, a, b, c, {a: 1}]
// 先是一个 [ 
// 紧接着匹配类型是 value 的 head
// 然后匹配多次 :
array
  = begin_array
    values:(
      head:value
      tail:(value_separator v:value { return v; })*
      { return [head].concat(tail); }
    )?
    end_array
    { return values !== null ? values : []; }

// ----- 6. Numbers -----
// 匹配数字
// 如果有 - 号,负数情况
// 整数
// 小数点
// 指数位
// 返回匹配文本
number "number"
  = minus? int frac? exp? { return parseFloat(text()); }

// 小数点
decimal_point
  = "."

digit1_9
  = [1-9]

// 指数标记、e或者E
e
  = [eE]

// 指数位
exp
  = e (minus / plus)? DIGIT+

// 小数位
frac
  = decimal_point DIGIT+

// 整数,0 或者 1-9 再匹配 0-9 零次或多次
int
  = zero / (digit1_9 DIGIT*)

// 减号
minus
  = "-"

// 加号
plus
  = "+"

// 匹配 0
zero
  = "0"

// ----- 7. Strings -----
// 匹配字符串
// 双引号
// 零次或多次字符
// 双引号
// 返回将匹配到的 chars 结果拼接成字符串
string "string"
  = quotation_mark chars:char* quotation_mark { return chars.join(""); }

// 匹配字符
// 所有非转义字符、分隔符
char
  = unescaped
  / escape
    sequence:(
        '"'
      / "\\"
      / "/"
      / "b" { return "\b"; }
      / "f" { return "\f"; }
      / "n" { return "\n"; }
      / "r" { return "\r"; }
      / "t" { return "\t"; }
      / "u" digits:$(HEXDIG HEXDIG HEXDIG HEXDIG) {
          return String.fromCharCode(parseInt(digits, 16));
        }
    )
    { return sequence; }

// 转义字符
escape
  = "\\"

// 双引号
quotation_mark
  = '"'

// https://regex101.com/r/EAogfy/1
// 非转义字符
unescaped
  = [^\0-\x1F\x22\x5C]

// ----- Core ABNF Rules -----

// See RFC 4234, Appendix B (http://tools.ietf.org/html/rfc4234).
DIGIT  = [0-9]
// 十六进制
HEXDIG = [0-9a-f]i

上述 Grammer 基本覆盖了文档中 80% 以上的解析表达式类型。我们从上到下开始看:

"literal" | 'literal'

双引号或者单引号括起来的字面量都表示精确匹配,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
begin_array     = ws "[" ws

数组的开头匹配是 [,当然前后可以有空格。

expression1 / expression2 / ... / expressionn
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 表达式的 / 表示优先匹配 false
// 匹配不成功就匹配 null
// 再不成功就匹配 true
// ...依次匹配
// 匹配到 string 都没有匹配成功就认为失败
value
  = false
  / null
  / true
  / object
  / array
  / number
  / string

JSON 的值可以是上述这些规则,⚠️ 这里是有顺序的。前面的匹配不成功,才会匹配下一个。

expression { action }

这个就基本都会用上了,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
false = "false" { return false; }

字符串中包含 "false",则会返回布尔值的 false{ ... } 内的就是 JavaScript 代码,获取匹配结果,这里就做任何的转换操作。函数体内有四个可以调用的函数:

  • text:匹配表达式的文本内容;
  • expected:使解析器抛出异常,支持两个参数,分别是对当前位置预期内容的描述和可选的位置信息;
  • error:同样是使解析器抛出异常,支持两个参数,分别是错误消息和可选的位置信息;
  • location:返回位置信息,如下所示的对象:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  start: { offset: 23, line: 5, column: 6 },
  end:   { offset: 25, line: 5, column: 8 }
}
expression1 expression2 ... expressionn
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
frac
  = decimal_point DIGIT+

举个例子,比如 '.123' 匹配 frac 规则后,会返回 ['.', '123'] 数组。

label : expression

label 表达式也基本会用上,label 的值能够在函数体内去获取表达式。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
member
  = name:string name_separator value:value {
      // name 是 name:string 中的 label
      // value 是 value:value 中前面的 value 即 label 值,后面的 value 是规则中的 value
      return { name: name, value: value };
    }
expression ?

尝试匹配表达式。如果匹配成功,返回匹配结果,否则返回null。与正则表达式不同,没有回溯。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// ----- 6. Numbers -----
// 匹配数字
// 如果有 - 号,负数情况
// 整数
// 小数点
// 指数位
// 返回匹配文本
number "number"
  = minus? int frac? exp? { return parseFloat(text()); }

到这里就把 PEG.js 中才有的表达式结合 json.pegjs 过了一遍,了解完它们的基本用法。至于其他的匹配比如 [characters](expression)expression *等在正则中都有接触过,这里就不展开举例说明了。

总结

先是了解完解释器和编译器的定义以及它们的区别,让我们知道了 PEG.js 是一个 JavaScript 的解析器生成器。然后学习了它生成编译器的过程及参数,经常用到的参数比如 --allow-start-rules--dependency 等做了详细的举例说明。最后基于 json.pegjs 去详细分析了解析表达式的用法。

总而言之,写一个编译器,无非就 3 件事:

  • 基于输入字符串做解析表达式匹配(正则匹配);
  • 基于生成的结果做转换;
  • 输出结果;

PEG.js 只是简化了我们去执行上述动作的流程。站在巨人的肩膀上,下篇文章我们就来实现一个自己的编译器。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农小余 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Grails——赋能敏捷开发的利器
几年前,一个开餐厅的亲戚找我做一个网上订餐的网站(当时外卖平台还没有兴起)。一开始我是拒绝的,因为我的本职工作就是做软件开发的,业余时间真没兴趣再做。而且从头开始做一个网页应用,工程浩大,我也没有这个时间。 一个偶然的机会,我接触到了Grails,通过它几个小时就能构建一个专业的涵盖前、后端的Web应用,于是我尝试着开发那个订餐网站,结果,我利用几个周末仅用了半个人/月的功夫就做好了一个能上线接单并具备后台管理(含基本财务)的网站。刷新了我对软件开发的认知,原来开发一个复杂的含前、后端的Web应用也可以如此地快。 Grails是一个基于JVM的全栈快速Web应用开发框架,类似的框架有著名的Rails,但是它需要用Ruby语言,对于广大的Java开发者,要学习一门新语言显然不现实。于是有人基于Groovy做了Grails,可以理解为Grails = Rails on Groovy。Groovy是JVM三大衍生语言之一,相对于Closure和Scala,它可以视为是Java的简化版和脚本化,学习周期最短,上手只消半天,而且相对于有点老气的Java,动态语言Groovy编程要快速和灵活得多。所以Grails可以说是面向Java开发者的快速开发框架。
Criss@陈磊
2019/08/02
2K0
8.2 Spring Boot集成Groovy、Grails开发小结参考资料
本章介绍Spring Boot集成Groovy,Grails开发。我们将开发一个极简版的pms(项目管理系统)。
一个会写诗的程序员
2018/08/20
2.6K0
8.2 Spring Boot集成Groovy、Grails开发小结参考资料
1. 了解Groovy
最近,对Groovy脚本语言的兴趣越来越多了,刚巧对于java语言比较熟悉,了解和入手Groovy可以说丝毫不困难。
zinyan.com
2022/12/07
1.5K0
1. 了解Groovy
java程序员为什么使用Groovy?
一直听说java世界里有个Groovy,但是一直没时间去了解它究竟是一个怎么样子的。我们现在的项目里放了很多开源包,而且项目中做流程的时候,规则就是用Groovy实现的。近来闲来无事,于是开始认真的看看Groory究竟有什么好的。其实最初我接触它是因为Grails这一个框架,这两个都是为了实现一个目的,java的敏捷开发,与java的无缝对接。在某些情况下,java需要半天处理的事情,Groovy只需要几分钟,是的,几分钟…剩下来的时间,程序员终于有时间泡妹子了,^_^…….技术宅的兄弟,赶紧来看看吧。
秃顶的Java程序员
2020/04/01
2K0
《Groovy极简教程》第1章 Groovy简介《Groovy极简教程》第1章 Groovy简介参考资料
官网文档:http://www.groovy-lang.org/documentation.html Github源码:https://github.com/apache/groovy
一个会写诗的程序员
2018/08/20
1.4K0
认识groovy脚本
大家好,我是小利。今天分享一个工作中常用的脚本语言,就是Groovy脚本,下面就简单的介绍一下。
找Bug
2023/09/22
8530
认识groovy脚本
[技术地图] vue-cli
这是一个新开的’实验性’文章系列,如其名‘技术地图’,这个系列计划剖析一些前端开源项目,可能会探讨这些项目的设计和组织、整理他们使用到技术栈。 首先拿vue-cli小试牛刀,再决定后续要不要继续这个系列.
_sx_
2019/08/07
3.3K0
[技术地图] vue-cli
IntelliJ IDEA 系列教程(一)
JetBrains 是一家捷克的软件开发公司,成立于 2000 年,位于捷克的布拉格,并在俄罗斯的圣彼得堡及美国麻州波士顿都设有办公室,该公司最为人所熟知的产品是 Java 编程语言开发撰写时所用的集成开发环境:IntelliJ IDEA。JetBrains 主要有如下一系列产品:
村雨遥
2020/06/28
1.5K0
简洁、高效、灵活:探索 Spring 同级别的编程框架
作为一个Java开发者,Spring框架应该基本上都用过的,由于Spring框架太过于强大,导致我们可能只知道Spring框架,但其实还有很多优秀的框架可以供我们使用,本文将介绍6个和spring框架类似的框架。
索码理
2023/09/23
1.2K0
简洁、高效、灵活:探索 Spring 同级别的编程框架
【大牛经验】Java开源web框架汇总(152款)
“框架”犹如滔滔江水连绵不绝, 知道有它就好,先掌握自己工作和主流的框架; 在研究好用和新框架。 主流框架教程分享在Java帮帮-免费资源网 其他教程需要时间制作,会陆续分享!!! 152款框架,你还知道其他的吗? 留言你用过的web框架 Java开源web框架汇总 1 Struts2 Struts2是一个web应用框架。它不是一个Struts的新的发布版本,而是一个全新的框架。Struts2 是第二代基于Model-View-Controller (MVC)模型的web应用框架。 Struts2是java
Java帮帮
2018/03/15
6K0
【大牛经验】Java开源web框架汇总(152款)
IntelliJ IDEA入门教程之一
本系列教程从 IntelliJ IDEA 的安装、卸载、软件设置、项目配置等各个方面进行讲解。通过本系列教程的学习,也希望你能爱上 IntelliJ IDEA,爱上它的体贴。同时学完本系列教程对于你学习 JetBrains 公司下的其他产品也有好处,其他产品包括:
张哥编程
2024/12/17
1820
IntelliJ IDEA入门教程之一
java用什么软件_Java编程什么软件最好用?
大家好,又见面了,我是你们的朋友全栈君。 原标题:Java编程什么软件最好用? “工欲善其事必先利其器”,想要学好Java编程开发,除了要有好的学习资源之外,还要有一套适合自己的Java编程软件,好的
全栈程序员站长
2022/08/25
3.3K0
java用什么软件_Java编程什么软件最好用?
Groovy新手教程
简单地说,Groovy 是下一代的java语言,跟java一样,它也执行在 JVM 中。
全栈程序员站长
2022/07/12
2.1K0
人生苦短?试试Groovy进行单元测试
如果您今天正在编程,那么您很可能听说过单元测试或测试驱动的开发过程。我还没有遇到一个既没有听说过又没有听说过单元测试并不重要的程序员。在随意的讨论中,大多数程序员似乎认为单元测试非常重要。
FunTester
2019/11/21
1.1K0
Java之Java开发工具
Eclipse Eclipse是一个开放源码的项目,是著名的跨平台的自由集成开发环境(IDE),最初主要用来Java语言开发,后来通过安装不同的插件Eclipse可以支持不同的计算机语言,比如C++和Python等开发工具。Eclipse的本身只是一个框架平台,但是众多插件的支持使得Eclipse拥有其他功能相对固定的IDE软件很难具有的灵活性。许多软件开发商以Eclipse为框架开发自己的IDE。 IDEA IDEA 全称 IntelliJ IDEA,是java语言开发的集成环境,IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手、代码自动提示、重构、J2EE支持、各类版本工具(git、svn、github等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超常的。IDEA是JetBrains公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。它的旗舰版本还支持HTML,CSS,PHP,MySQL,Python等。免费版只支持Java等少数语言。 NetBeans NetBeans是Sun公司(2009年被甲骨文收购)在2000年创立的开放源代码供开发人员和客户社区的家园,旨在构建世界级的Java IDE。NetBeans当前可以在Solaris、Windows、Linux和Macintosh OS X平台上进行开发,并在SPL(Sun公用许可)范围内使用。 NetBeans包括开源的开发环境和应用平台,NetBeans IDE可以使开发人员利用Java平台能够快速创建Web、企业、桌面以及移动的应用程序,NetBeans IDE已经支持PHP、Ruby、JavaScript、Groovy、Grails和C/C++等开发语言。
全栈程序员站长
2022/06/30
1K0
我被 pgx 及其背后的 Rust 美学征服
知道我的人都了解,自 2018 年比较正式地学习 Rust 以来(在此要感谢张汉东老师的大力推荐),我慢慢被 Rust 征服,成为一名不折不扣的拥趸。我的业余项目,90% 都是用 Rust 写就的,另外 10% 基本被 typescript(前端)和 python(主要是 notebook)瓜分。我对 Rust 热爱也体现在我的公众号和 B 站上,近两年发布的内容,主要和 Rust 有关。然而,我很少直接吹捧 Rust,更多是通过 “show me the code” 来展示 Rust 的美妙。这个周末,在 reddit/rust 版,我无意发现了 pgx 这样一个使用 Rust 来撰写 postgres extension 的集成工具,在深入地了解其文档并写了几百行代码后,我立刻就被那种直击心灵的简约之美冲破了防线,不得不在此吹上一波。如此优雅地解决另一个生态系统(postgres)的扩展的问题,我就想说,除了 Rust,还有谁?
tyrchen
2022/12/05
1.3K0
我被 pgx 及其背后的 Rust 美学征服
Groovy classes are not available on the class path. ABORTING INITIALIZATION
在使用Groovy应用程序时,可能会遇到错误信息:“无法在类路径上找到Groovy类。初始化中断”。这个错误通常发生在Groovy类或依赖项没有正确配置或在项目的类路径中缺失时。 本文将讨论此错误可能的原因,并提供解决方案以解决该问题。
大盘鸡拌面
2023/11/13
2460
看了 Spring 官网脚手架真香,也撸一个 SpringBoot DDD 微服务的脚手架!
虽然市面上已经有了大量成熟稳定用于支撑系统建设的轮子,也就是服务、框架、组件、工具等,但对于一些较大型的公司来说,这些轮子可能并不一定能很好的支撑起系统需要承载的服务体量,这个时候就需要自建一些轮子。
小傅哥
2021/03/22
4.6K0
看了 Spring 官网脚手架真香,也撸一个 SpringBoot DDD 微服务的脚手架!
Java 、Groovy、 Scala 的未来
Groovy 是用于Java虚拟机的一种敏捷的动态语言,是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。使用该种语言不必编写过多的代码,同时又具有闭包和动态语言中的其他特性。
深圳java培训技术
2019/09/04
1.5K0
什么是软件开发脚手架?为什么需要脚手架?常用的脚手架有哪些?
作者 | 程序员高级码农II 来源 | https://www.toutiao.com/a7004430129946739232/?log_from=51ac19498426e_1630977308
程序猿DD
2021/10/14
9K0
推荐阅读
相关推荐
Grails——赋能敏捷开发的利器
更多 >
LV.1
OracleSoftware Engineer
目录
  • 认识 PEG.js
    • How?
    • --allowed-start-rules
    • --cache
    • --dependency
    • --export-var
    • --extra-options
    • --extra-options-file
    • --format
    • --optimize
    • --plugin
    • --trace
  • 语法和语义
    • "literal" | 'literal'
    • expression1 / expression2 / ... / expressionn
    • expression { action }
    • expression1 expression2 ... expressionn
    • label : expression
    • expression ?
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档