Advertisement

李泽帆_如何创造一门上万人使用的语言 TWEB

Oct. 31, 2021
Advertisement

More Related Content

Recently uploaded(20)

Advertisement

李泽帆_如何创造一门上万人使用的语言 TWEB

  1. 腾讯问卷DSL实践之路 李泽帆 LZANE 腾讯CDC⾼级研发⼯程师 如何创造⼀门上万⼈使⽤的语⾔ 赞 助 商 腾讯前端技术委员会 主 办 方
  2. Lzane.com - 李泽帆 产品技术团队 腾讯CDC
  3. 1. 我们做了⼀个什么东西? 2. 适合DSL落地的场景 3. 编译器实现 4. 语法设计 5. 配套设施 ⽬录
  4. 我们做了⼀个什么东西? 01
  5. 腾讯问卷⾃定义逻辑
  6. 腾讯问卷⾃定义逻辑
  7. 腾讯问卷⾃定义逻辑
  8. 腾讯问卷⾃定义逻辑
  9. 腾讯问卷⾃定义逻辑 if Q1A1 then show Q2 第1题选了第一个选项,显示第二题 if Q1A1 and Q2A2 then show Q3 第一题选了第一个选项,并且第二题选中了第二个选项,显示第三题 if Q1A1 then branch from Q1 to END 第一题选了第一个选项,跳转到结束页(甄别题) random show 1 from Q1~3 第1-3题中随机抽取1道题显示 shuffle Q1~3 第1-3题随机排序 replace "XXX" in Q2 title with Q1 第二道题题目中的XXX文本,替换为第一道题的答案 DSL语句 功能效果
  10. 腾讯问卷⾃定义逻辑
  11. 适合DSL落地的场景 02
  12. 复杂的问卷逻辑
  13. • 每份定制问卷花费3⼈天,花费⼤量研发资源 定制开发 编译构建 构建产物 定制问卷
  14. (卧槽,又来)好啊,说一下这次的需求 enen,这些还好 好的,这次是一份调查K12补习班的问卷,首先会在第一题询 问有没有孩子,如果没有孩子,就直接跳转到结束页;然后 第二题询问小孩上过补习班没,没有也跳转到结束页。 接着第三题问使用过哪些平台,选项是各种各样的平台,包 括数学、语文、英语等,这里同一种类的选项要随机排序, 比如说数学1、2、3要随机出现; 然后选择综合就要显示下面 第一组题目,选择数学显示下面第二组题目,依此类推;但 是怕用户用户需要答的题目太多,这几组题目一个用户只会 随机出现2组,出现的概率为1:5:2:10,能理解么? Hey, 帮我搞一份定制问卷 ⼀份定制问卷制作的故事
  15. 好的,那我再解释一下,就是如果不做限制,那用户如果数 学、语文、音乐都选了,那问卷就会变得很长,所以我们一 个用户只会随机出现2组题目,然后按照概率出现;然后接下 来这道题目过没有选数学的选项的话,就会显示为什么不上 数学这道题;然后后续对每个平台的打分,比如说音乐1小于3 分时,就显示询问不喜欢的原因这道题。这些能理解不? 这个地方没懂 这个地方没懂 这个地方没懂 这个地方没懂 这个地方没懂 这个地方没懂 这个地方没懂 这个地方没懂 这个地方没懂 这个地方没懂 好的,那我再解释一下 好的,那我再解释一下 好的,那我再解释一下 好的,那我再解释一下 好的,那我再解释一下 好的,那我再解释一下 ⼀份定制问卷制作的故事
  16. 沟通成本巨⼤ 领域专家清楚的知道逻辑细节,但他不会写代码
  17. ⽤研 编写问卷 ⽤研 > 开发 沟通需求 开发 理解并开发 测试 测试逻辑 ⽤研 投放问卷 开发 构建并发版 定制问卷的全流程
  18. 定制问卷的全流程 涉及的⼈员多,沟通成本⼤ ⽤研 编写问卷 ⽤研 > 开发 沟通需求 开发 理解并开发 测试 测试逻辑 ⽤研 投放问卷 开发 构建并发版
  19. 定制问卷的全流程 ⽤研 编写问卷 ⽤研 > 开发 沟通需求 开发 理解并开发 测试 测试逻辑 ⽤研 投放问卷 开发 构建并发版 重复性⼯作多,每次都要重新定制
  20. 定制问卷的全流程 ⽤研 编写问卷 ⽤研 > 开发 沟通需求 开发 理解并开发 测试 测试逻辑 ⽤研 投放问卷 开发 构建并发版 每次都要重新构建,希望能从“构建时”后移到“运⾏时”
  21. 定制问卷的全流程 ⽤研 编写问卷 ⽤研 > 开发 沟通需求 开发 理解并开发 测试 测试逻辑 ⽤研 投放问卷 开发 构建并发版 ⽤研 编写问卷 ⽤研 DSL ⽤研 投放问卷
  22. • 重复性⼯作多 • 沟通成本⼤,参与⾓⾊多 • “构建时”后移到“运⾏时” • 领域专家很清楚逻辑细节 • 过多的GUI操作 适合DSL的落地场景
  23. GUI普通逻辑 DSL VS 243次⿏标操作 1⾏DSL 适合DSL的落地场景
  24. DSL编译器实现 03
  25. 编译器 Compiler 代码 ? 编译器
  26. 编译器 Compiler c++ 0011 编译器
  27. 编译器 Compiler ES6 ES5 编译器
  28. ES6 ES5 解析器 Parser 转换器 Transformer 代码生成器 CodeGen AST AST 编译器
  29. 解析器生成器 parser generator 语法 grammar 解析器 Parser 解析器⽣成器 parser generator
  30. 案例:表达2分钟之前 JS: new Date(Date.now()-2*60*1000) DSL: 2 mins ago
  31. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ "mins" _ "ago" Integer "integer" = _ [0-9]+ _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) console.log(result)
  32. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ "mins" _ "ago" Integer "integer" = _ [0-9]+ _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) console.log(result)
  33. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ "mins" _ "ago" Integer "integer" = _ [0-9]+ _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) console.log(result)
  34. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ "mins" _ "ago" Integer "integer" = _ [0-9]+ _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) console.log(result)
  35. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ "mins" _ "ago" Integer "integer" = _ [0-9]+ _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) console.log(result) $ node demo.js "2 mins ago" [ [ [], [ '2' ] ], [ ' ' ], 'mins', [ ' ' ], 'ago' ]
  36. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ "mins" _ "ago" Integer "integer" = _ [0-9]+ _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) console.log(result) $ node demo.js "2 mins ago" [ [ [], [ '2' ] ], [ ' ' ], 'mins', [ ' ' ], 'ago' ]
  37. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ "mins" _ "ago" Integer "integer" = _ [0-9]+ { return parseInt(text(), 10) } _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) console.log(result)
  38. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ "mins" _ "ago" Integer "integer" = _ [0-9]+ { return parseInt(text(), 10) } _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) console.log(result) $ node demo.js "2 mins ago" [ 2, [ ' ' ], 'mins', [ ' ' ], 'ago' ]
  39. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ "mins" _ "ago" { return new Date(Date.now()-i*60*1000) } Integer "integer" = _ [0-9]+ { return parseInt(text(), 10) } _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) console.log(result)
  40. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ "mins" _ "ago" { return new Date(Date.now()-i*60*1000) } Integer "integer" = _ [0-9]+ { return parseInt(text(), 10) } _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) console.log(result) $ node demo.js "2 mins ago" 2021-06-20T12:07:03.960Z
  41. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ u:Unit _ "ago"{ return new Date(Date.now()-i*u) } Unit "unit" = "mins" { return 60*1000 } / "hours" { return 60*60*1000 } Integer "integer" = _ [0-9]+ { return parseInt(text(), 10) } _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2])
  42. const PEG = require("pegjs") const grammar = ` Start = i:Integer _ u:Unit _ "ago"{ return new Date(Date.now()-i*u) } Unit "unit" = "mins" { return 60*1000 } / "hours" { return 60*60*1000 } Integer "integer" = _ [0-9]+ { return parseInt(text(), 10) } _ "whitespace" = [ ]* ` const parser = PEG.generate(grammar) const result = parser.parse(process.argv[2]) $ node demo.js "2 hours ago" 2021-06-20T10:38:11.304Z
  43. https://pegjs.org/online
  44. 解析器生成器 parser generator 语法 grammar DSL AST 问卷逻辑对象 解析器 parser 转换器 transformer 运行器 runner 问卷 编译器的实现
  45. 实体模型 问卷逻辑对象 业务逻辑代码 DSL 开发顺序?先有DSL or 先有实体模型
  46. 业务逻辑代码 DSL 隔离 开发顺序?先有DSL or 先有实体模型 实体模型 问卷逻辑对象
  47. DSL语法设计 04
  48. 题目id ${q-1-KsxT::o-2-gaN6} => show(q-3-oMjX) 选项id 函数调用 取值 条件语句
  49. 领域专家优先原则
  50. if Q1A2 then branch from Q1 to END ${q-1-abcd:o-2-abcd} => branchTo(q-1-abcd, END) if Q4A1 or Q4A2 then show Q5~8 ${q-4-abcd:o-1-abcd} || ${q-4-abcd:o-2-abcd}
=> show([q-5-abcd, q-6- abcd, q-7-abcd, q-8-abcd]) shuffle Q1A1~3 shuffle(q-1-abcd, [o-1-abcd, o-2-abcd, o-3-abcd]) replace "XXX" in Q2 title with Q1 replace($[q-2-abcd].title, "XXX", ${q-1-abcd}) 新语法 旧语法 最终的语法
  51. 业务逻辑代码 DSL 隔离 开发顺序?先有DSL or 先有实体模型 实体模型 问卷逻辑对象
  52. test('if statement', function () { const ast = { "type": "SurveyLogic", "body": [ { "type": "ConditionStatement", "condition": { "type": "GetValue", "item": { "type": "Question", "id": "q-4-lTp9" } }, "action": { "type": "HideAction", "param": [{ "type": "Question", "id": "q-3-ql6u" }] } } ] }; expect(parser.parse('${q-4-lTp9} => hide(q-3-ql6u)')).toEqual(ast); expect(parser.parse('if `q-4-lTp9` then hide `q-3-ql6u`')).toEqual(ast); })
  53. test('if statement', function () { const ast = { "type": "SurveyLogic", "body": [ { "type": "ConditionStatement", "condition": { "type": "GetValue", "item": { "type": "Question", "id": "q-4-lTp9" } }, "action": { "type": "HideAction", "param": [{ "type": "Question", "id": "q-3-ql6u" }] } } ] }; expect(parser.parse('${q-4-lTp9} => hide(q-3-ql6u)')).toEqual(ast); expect(parser.parse('if `q-4-lTp9` then hide `q-3-ql6u`')).toEqual(ast); })
  54. DSL配套设施 05
  55. 编辑器
  56. 详尽⽂档
  57. 语法⾼亮 / ⾃动补全 / 错误提⽰
  58. 仅仅需要⼀个解析器?
  59. 案例:表达2分钟之前 JS: new Date(Date.now()-2*60*1000) (2).mins().ago() 2 mins ago 内部DSL 外部DSL 内部DSL?
  60. > (2).mins().ago() Uncaught TypeError: 2.mins is not a function 内部DSL
  61. > (2).mins().ago() 2021-06-20T09:54:32.365Z Number.prototype.mins = function(){ return this*60*1000 } Number.prototype.ago = function(){ return new Date(Date.now()-this) }
  62. • 适合DSL落地的场景 • 解析器⽣成器 Parser generator • DSL语法设计 • 配套设施的搭建 总结
  63. 克制
  64. 克制 除了DSL没有其他⾼效解决问题的⽅案 能不能够使⽤内部DSL 领域内是否已有标准的语法
  65. 感谢倾听 ⼤会官⽹ 赞 助 商 腾讯前端技术委员会 主 办 方 欢迎加⼊CDC
Advertisement