
编程语言编译器【免费下载链接】purescriptA strongly-typed language that compiles to JavaScript项目地址https://gitcode.com/gh_mirrors/pu/purescript点击查看免费下载purs ide是 PureScript 编译器自带的编辑器与工具链支持模块旧称 psc-ide其核心工作方式是通过一个简单而严格的JSON-over-TCP 协议与编辑器插件通信。本文以仓库中的 psc-ide/PROTOCOL.md 为骨架结合 app/Command/Ide.hs 等源码实现完整讲解协议的数据格式、服务器启动参数、全部命令、过滤器与匹配器体系帮助读者从协议层面掌握补全、类型查询、case split、导入管理、增量重建等 IDE 能力的实现原理并能够据此为自己的编辑器插件或命令行工具编写客户端。协议模型一行 JSON、一次连接、一个响应purs ide server与客户端之间的通信走TCP 连接服务器监听一个可配置的端口接受一行JSON 输入以换行符结尾处理完毕后返回一个 JSON 响应并关闭连接。也就是说每一次查询都是一次独立的短连接而不是长驻的会话式流。在 app/Command/Ide.hs 中服务器通过listenOnLocalhost绑定在127.0.0.1上主循环acceptCommand读取一行文本后即进入handleCommand处理随后把编码后的结果写回并hClose关闭连接见startServer的loop。连接被客户端提前关闭时服务器会打印[Error] psc-ide-server tried to interact with the handle...并继续监听下一条连接。purs ide client是这条 TCP 连接的官方命令行包装它从stdin 读取一行 JSON转发给服务器然后把服务器的响应打印到stdout后退出对应client函数的实现T.hPutStrLn h T.getLine再BS8.putStrLn BS8.hGetLine h。因此任何语言只要能用一行 JSON 完成收发就能与purs ide交互。无论成功还是失败响应都需要从一层wrapper中解包它把成功与失败区分开{ resultType: success|error, result: Result|Error }resultType固定为success或errorresult在成功时承载具体命令的返回数据失败时则是错误信息字符串该 wrapper 的编码逻辑位于 src/Language/PureScript/Ide/Types.hs 的encodeSuccess成功时resultType . success。启动与配置服务器参数与前置条件在 psc-ide/README.md 中给出了服务器的启动方式purs ide server [SOURCEGLOBS]其中SOURCEGLOBS为可选的、匹配 PureScript 源文件的 glob 通配符。服务器支持以下选项选项说明默认值-p/--port指定监听端口4242-d/--directory指定项目顶层目录当前目录--output-directory在项目目录内指定编译输出查找位置output/相对当前目录或-d指定的目录--log-level日志级别可取all、none、debug、perf之一默认级别--version输出purs ide版本号—从 app/Command/Ide.hs 的serverOptions解析器还可以看到两个未写入 README 的实用参数--glob-input-file从文件读取 glob与排除文件的 globSharedCLI.excludeFiles。日志级别在源码中的完整映射为debug - LogDebug、perf - LogPerf、all - LogAll、none - LogNone其余值回退到LogDefault见parseLogLevel。需要特别注意的是三个已废弃且被忽略的开关--editor-mode、--polling、--no-watch。服务器启动时会分别打印提示说明编辑器模式现在已是默认行为而purs ide不再使用文件系统监视器改为依赖客户端主动通知更新并通过检查时间戳来自行失效缓存。另一个关键前置条件purs ide期望在output/目录中读到项目编译后生成的 externs 文件——即先运行pulp build或purs compile。若输出目录不存在服务器会提示 This usually means you didnt compile your project yet.。此外usages命令依赖启动时传入的源文件 glob因为它的实现直接扫描解析过的源文件 AST见下文。如果修改了服务器端口purs ide client也要用-p指定同样的端口才能连通。核心命令详解Load加载模块load命令把指定模块加载进服务器供补全与类型查询使用。如果省略params对象load会尝试自动检测项目中所有已编译模块并全部加载。参数modules :: (可选) [ModuleName]要加载的模块列表purs ide server会尝试解析这些模块内的所有声明。{ command: load, params: (optional) { modules: (optional)[Module.Name1, Module.Name2] } }结果返回一个字符串概括加载过程的情况。从 src/Language/PureScript/Ide/Command.hs 的FromJSON Command实例可见load时若params缺失则构造Load []此时服务器端isLoadAll判定为 true表示加载全部。Type查询标识符的类型type命令根据给定的标识符查找其类型同时返回定义位置如果能从已传入的源文件中找到。参数search :: String要查找的标识符只做相等匹配。filters :: (可选) [Filter]查找前应用的过滤器。多个过滤器以AND组合候选必须满足全部条件才算合格。currentModule :: (可选) String含义与complete命令相同见下。{ command: type, params: { search: filterM, filters: [{..}], currentModule: Main } }结果可能的类型以与补全结果相同的格式返回字段见complete命令的 Result 一节。实现上type对应Command数据构造器中的Type { typeSearch, typeFilters, typeCurrentModule }处理时经由getExactCompletions在完成过滤后精确匹配。Complete获取补全与修正complete命令查询可能的补全项/修正项。参数filters :: [Filter]与type命令一致候选必须匹配全部过滤器。matcher :: (可选) Matcher过滤之后用于匹配候选的策略。结果在内部打分并按得分降序返回第 n 项优于第 n1 项。若不提供 matcher则返回所有通过过滤器的候选顺序不定。currentModule :: (可选) String当前模块名。模块成功 rebuild 之后传入该参数即可看到模块私有函数——前提是与 rebuild 缓存匹配。rebuild 缓存通过rebuild命令填充。options :: (可选) CompletionOptions应用于补全结果的选项见下文 CompletionOptions 一节。{ command: complete, params: { filters: [{..}, {..}], matcher: {..}, currentModule: Main, options: { maxResults: 50, groupReexports: true } } }结果返回一个补全对象数组字段如下[ { module: Data.Array, identifier: filter, type: forall a. (a - Boolean) - Array a - Array a, expandedType: forall a. (a - Boolean) - Array a - Array a, definedAt: { name: /path/to/file, start: [1, 3], end: [3, 1] }, documentation: A filtering function, exportedFrom: [Data.Array], declarationType: value, } ]definedAt定义位置、documentation文档注释以及declarationType声明类型字段可能为null——当它们无法从源文件中提取时即是如此。declarationType的所有可能取值与用法见下文「Declaration type filter」一节。start/end均为[行, 列]形式的二元数组。上述 JSON 字段名与 src/Language/PureScript/Ide/Types.hs 中Completion的ToJSON实例一一对应module、identifier、type、expandedType、definedAt、documentation、exportedFrom、declarationType。补全的处理管线在 src/Language/PureScript/Ide/Completion.hs 的getCompletions中清晰可见applyFilters应用过滤器→matchesFromModules展开为匹配→runMatcher运行匹配器并排序→applyCompletionOptions截断/分组→completionFromMatch转成 JSON 补全。对于运算符声明type字段会退化为显示其 fixity如infixl 6 $ as map而对模块声明则固定为module字符串。CaseSplit按类型拆分用例caseSplit命令接收一行源码与这行代码中的一个区域把该区域替换为给定类型的所有构造模式。参数annotations用于控制是否为构造器字段生成类型标注。{ command: caseSplit, params: { line: elem a as, begin: 8, end: 10, annotations: true, type: List } }结果返回替换建议列表[ elem a Nil, elem a (Cons (_ :: a) (_ :: List a)) ]拿到结果后编辑器应把受影响的那行代码替换为其中的新建议。实现层面src/Language/PureScript/Ide/CaseSplit.hs先解析type参数拆出类型构造器与参数再从 externs 文件中找到对应data声明的类型变量与全部构造器然后把类型实参替换进每个构造器生成模式annotations: true对应explicitAnnotationsfalse对应noAnnotations。AddClause为类型声明生成函数模板addClause命令接收一条类型声明为给定类型生成函数模板。annotations选项控制是否为函数参数生成类型标注。{ command: addClause, params: { line: elem :: forall a. (Eq a) a - List a, annotations: true } }结果[ elem :: forall a. (Eq a) a - List a, elem ( _ :: a) ?elem ]同样地把受影响行替换为建议即可。注意模板中函数体以?elem这个typed hole占位便于后续继续使用编译器补全推进实现。Usages查找标识符的所有使用位置usages命令接收模块名、命名空间、标识符三元组它唯一确定一个声明并返回该标识符在所有已加载文件中的使用位置。由于实现直接基于解析后的源文件因此必须在服务器启动时传入源文件 glob才能使用此命令。{ command: usages, params: { module: Data.Array, namespace: value|type|kind, identifier: filter } }结果返回SourceSpan位置列表格式与definedAt相同[ { name: /path/to/file , start: [1, 3] , end: [3, 1] } , { name: /path/to/file , start: [5, 6] , end: [5, 8] } ]从 src/Language/PureScript/Ide/Usage.hs 的findUsages可以看清算法流程① 找出所有重导出该声明的模块② 找出所有从这些模块导入的模块并在遍历导入的同时构建标识符如何在该模块中出现的搜索规格Search类型③ 应用搜索规格并汇总结果。需要注意文档示例中的kind命名空间在当前仓库源码Types.hs 的IdeNamespace中实际可解析的值是value、type、module编写客户端时请以源码为准。Import自动管理导入语句目前所有导入相关命令都基于文件系统上的文件工作通过file参数指定。若提供可选参数outfile输出会直接写入该文件客户端返回一条信息消息若不提供outfile服务器返回一个字符串列表把这些字符串按行插入文件即可得到应用变更后的模块内容。通用参数file :: Stringoutfile :: Maybe Stringfilters :: Maybe [Filter]{ command: import, params: { file: /home/creek/Documents/chromacannon/src/Main.purs, outfile: /home/creek/Documents/chromacannon/src/Main.purs, filters: [{ filter: modules, params: { modules: [My.Module] } }], importCommand: { yadda: yadda } } }具体的导入动作由importCommand子命令决定共有三种子命令addImplicitImport仅添加一个指定模块的非限定导入。参数module :: String{ command: import, params: { file: /home/creek/Documents/chromacannon/src/Main.purs, importCommand: { importCommand: addImplicitImport, module: Data.Array.LOL } } }子命令addQualifiedImport添加指定模块及限定符的导入。参数module :: Stringqualifier :: String{ command: import, params: { file: /home/creek/Documents/chromacannon/src/Main.purs, importCommand: { importCommand: addQualifiedImport, module: Data.Array, qualifier: Array } } }子命令addImport接收一个标识符并在当前已加载模块中搜索它找不到任何匹配 → 返回Error恰好找到一个匹配 → 添加导入并返回找到多个匹配 → 以与complete命令相同的补全格式返回这些候选。可以在顶层命令而非importCommand内部提供一组与补全一致的过滤器借此把搜索范围收窄到某个模块从而消解多个匹配的歧义。参数identifier :: Stringqualifier :: String可选{ command: import, params: { file: /home/creek/Documents/chromacannon/src/Demo.purs, outfile: /home/creek/Documents/chromacannon/src/Demo.purs, importCommand: { importCommand: addImport, identifier: bind } } }带限定符与过滤器的例子{ command: import, params: { file: /home/creek/Documents/chromacannon/src/Demo.purs, outfile: /home/creek/Documents/chromacannon/src/Demo.purs, importCommand: { importCommand: addImport, identifier: length, qualifier: Array }, filters: [{ filter: modules, params: { modules: [Data.Array] } }] } }Rebuild单模块快速重建rebuild命令为单个模块提供快速重建但不会重新编译整个项目——模块的所有依赖必须已加载。成功的 rebuild 结果会被缓存从而允许补全模块私有标识符。参数file :: String要重建的模块文件路径或以data:前缀传入该模块的完整源码直接内联源码进行重建。actualFile :: Maybe String用于位置信息与解析错误的实际路径。当用临时文件作为 rebuild 来源时这个参数很有用。codegen :: Maybe [String]指定 rebuild 应产出的 codegen 目标目标名与命令行编译器的 target 名一致。默认只产出 JS 输出源码Command.hs中的默认值为[js]。{ command: rebuild, params: { file: /path/to/file.purs, actualFile: /path/to/actualFile.purs, codegen: [js, corefn] } }结果成功返回编译器 JSON 格式的警告列表失败返回编译器 JSON 格式的错误信息。List列出模块与导入loadedModules已废弃list类型为loadedModules时列出所有已加载模块即可以被搜索、补全的那些。该命令会在 0.13 之后的下一个破坏性版本中移除请改用带模块过滤器的complete命令。{ command: list, params: { type: loadedModules } }响应字符串列表。availableModuleslist类型为availableModules时列出所有可用模块基本等价于output/目录的内容。{ command: list, params: { type: availableModules } }响应字符串列表。import列出某文件的导入list也可以列出指定文件的全部导入{ command: list, params: { type: import, file: /home/kritzcreek/Documents/psc-ide/examples/Main.purs } }响应返回解析出的模块名及导入列表{ moduleName: MyModule, imports: [Import] }不同类型的导入以如下 JSON 呈现与 Types.hs 的encodeImport输出一致隐式导入import Data.Array{ module: Data.Array, importType: implicit }带限定符的隐式导入import Data.Array as A{ module: Data.Array, importType: implicit, qualifier: A }显式导入import Data.Array (filter, filterM, join){ module: Data.Array, importType: explicit, identifiers: [filter, filterM, join] }带限定符的显式导入import Data.Array (filter, filterM, join) as A{ module: Data.Array, importType: explicit, identifiers: [filter, filterM, join], qualifier: A }隐藏导入import Data.Array hiding (filter, filterM, join){ module: Data.Array, importType: hiding, identifiers: [filter, filterM, join] }带限定符的隐藏导入import Data.Array hiding (filter, filterM, join) as A{ module: Data.Array, importType: hiding, identifiers: [filter, filterM, join], qualifier: A }Cwd / Quit / Resetcwd返回服务器的工作目录应为项目根目录。quit退出服务器。reset重置所有已加载模块。{ command: cwd|quit|reset }结果这些命令均返回字符串。过滤器Filter体系过滤器用于在查询前筛掉不合格的声明多个过滤器按AND组合。在 src/Language/PureScript/Ide/Filter.hs 中applyFilters先通过optimizeFilters把多个模块过滤器用集合交集合并foldr Set.intersection再逐条应用声明过滤器因此不同维度的条件可以组合出精确的查询。Exact filter精确匹配只保留与搜索词相等的标识符。{ filter: exact, params: { search: filterM } }Prefix filter前缀匹配保留以搜索词为前缀的标识符/模块/data 声明。{ filter: prefix, params: { search: filt } }Module filter模块过滤只保留出现在所列模块中的标识符。{ filter: modules, params: { modules: [My.Module] } }Dependency filter依赖过滤只保留出现在所列模块中、或由其任一导入带入作用域的标识符。模块文本只需提供到导入区结束为止的部分即可。参数moduleText :: Stringqualifier :: String可选{ filter: dependencies, params: { moduleText: module My.Module where\nimport Foo as F\n, qualifier: F } }实现上服务器通过sliceImportSection解析moduleText的导入区得到当前模块名与导入列表后构造Dependencies过滤器当目标模块就是当前模块且未限定符时直接放行本模块内部可见否则用matchImport判断声明是否被任一导入带入作用域。Namespace filter命名空间过滤只保留出现在所列命名空间中的标识符。有效命名空间为value、type和kind。{ filter: namespace, params: { namespaces: [value, type, kind] } }Declaration type filter声明类型过滤按声明类型过滤。合法的声明类型为value、type、synonym、dataconstructor、typeclass、valueoperator、typeoperator、kind、module。{ filter: declarations, params: [ value , type , synonym , dataconstructor , typeclass , valueoperator , typeoperator , kind , module ] }这些标签与 src/Language/PureScript/Ide/Filter/Declaration.hs 的DeclarationType一一对应Value / Type / Synonym / DataConstructor / TypeClass / ValueOperator / TypeOperator / Module。补全结果中的declarationType字段正是此类型的 JSON 序列化编辑器可用它做图标区分或分组展示。匹配器Matcher体系匹配器在过滤器之后工作负责对候选进行打分排序。若省略 matcher则所有通过过滤器的候选原样返回、顺序不定。Flex matcher模糊匹配匹配搜索字符串在候选中的任意出现允许字符穿插intersections。打分衡量匹配跨度覆盖字符串的程度越紧凑得分越高随后按得分从高到低排序。示例flMa匹配flexMatcher得分 14.28sons匹配sortCompletions得分 6.25。{ matcher: flex, params: { search: filt } }其打分公式在 src/Language/PureScript/Ide/Matcher.hs 的flexScore中实现100.0 / ((1 start) * (end - start 1))。实现会把搜索词转成交错模式如abcd→a.*b.*c.*d.*并对正则特殊字符做转义匹配失败返回Nothing即被淘汰。Distance matcher编辑距离匹配为拼写错误提供修正。它计算搜索词与已加载标识符之间的编辑距离。{ matcher: distance, params: { search: dilterM, maximumDistance: 3 } }实现采用Text.EditDistance的levenshteinDistancedefaultEditCosts距离不超过maximumDistance的候选保留并按1 / distance打分距离越近得分越高。CompletionOptions补全结果的配置补全选项用于配置返回的补全结果数量与形态maxResults :: Maybe Int指定后限制补全结果数量不指定则返回全部结果。groupReexports :: Maybe Boolean默认false设为true时把某个标识符的所有重导出按它的来源模块分组原始导出也视作一次重导出。这些重导出会填充补全结果的exportedFrom字段而module字段则改为存放来源模块名。示例complete命令的options字段{ options: { maxResults: 50, groupReexports: true } }在 src/Language/PureScript/Ide/Completion.hs 中defaultCompletionOptions CompletionOptions { coMaxResults Nothing, coGroupReexports False }applyCompletionOptions先决定是否按groupCompletionReexports分组再按take (coMaxResults co)截断。分组逻辑使用annExportedFrom标注追溯声明真正的来源模块。错误格式与客户端注意事项当前协议中错误就是一段错误字符串。换行符以\n形式转义应由编辑器插件负责还原与展示。也就是说resultType为error时result是一个经过转义的文本串其产生路径可见于 app/Command/Ide.hs 中对解析失败命令的GeneralError errMsg编码以及IdeError的编码逻辑。编写客户端的三个要点每次查询发送一行JSONUTF-8换行符结尾等待一行 JSON 响应后连接即被关闭——不要复用旧连接解析响应前先读resultType分别处理成功数据与错误字符串先发送load再查询服务器要求先加载模块以及先完成一次pulp build/purs compile生成 externscomplete与type才有数据可用如需补全模块私有标识符还需先对当前模块执行rebuild填充缓存。结语从协议到实现的一条完整链路把整条链路串起来看编辑器插件通过 TCP/purs ide client发送一行 JSON → 服务器在 app/Command/Ide.hs 中解析decodeTFromJSON Command字段解析细节见 src/Language/PureScript/Ide/Command.hs→handleCommand依据命令类型分别走补全、类型、重建、导入、使用查找等处理模块 → 结果经encodeSuccess包装成{resultType, result}写回。理解这份协议后无论是开发新的编辑器插件、编写脚本化的 IDE 工具还是调试现有 PureScript 编辑器集成都能精准定位到对应的命令、过滤器与匹配器组合。赞分享编程语言编译器【免费下载链接】purescriptA strongly-typed language that compiles to JavaScript项目地址https://gitcode.com/gh_mirrors/pu/purescript点击查看免费下载相关推荐PureScript 编译器自带 IDE 工具链purs ide 服务器协议、查询命令与架构深度解析PureScript 编译器自带 IDE 工具链purs ide 服务器协议、查询命令与架构深度解析 purs ide 是 PureScript 编译器仓库中编程语言编译器终极simplewall协议过滤指南TCP/UDP/ICMP配置全解析想要全面掌控Windows网络连接simplewall协议过滤工具是您的完美选择这款基于Windows过滤平台 WFP 的轻量级应用让您轻松配置TCP、U网络安全Wireshark抓包过滤器TCP协议字段参考指南Wireshark抓包过滤器TCP协议字段参考指南 你是否在分析网络问题时面对海量数据包无从下手是否想精准捕获TCP流量却不知如何编写过滤器本文将系统讲网络通信上一篇Presto 0.229 版本特性详解地理空间函数、内部通信 HTTPS 配置与 Pinot 连接器下一篇Diem AccountCreationScripts 账户创建脚本完全指南五类链上角色账户的创建机制与源码解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考