ctrl+shift+p filters: :st2 :st3 :win :osx :linux
浏览

SBNF

用于编写 sublime-syntax 文件的一种 BNF 语法的语言

详细资料

  • 0.6.4
  • github.com
  • 10 个月前
  • 26 分钟前
  • 8 个月前

安装次数

  • 总数 36
  • Win 15
  • Mac 11
  • Linux 10
8 月 6 日 8 月 5 日 8 月 4 日 8 月 3 日 8 月 2 日 8 月 1 日 7 月 31 日 7 月 30 日 7 月 29 日 7 月 28 日 7 月 27 日 7 月 26 日 7 月 25 日 7 月 24 日 7 月 23 日 7 月 22 日 7 月 21 日 7 月 20 日 7 月 19 日 7 月 18 日 7 月 17 日 7 月 16 日 7 月 15 日 7 月 14 日 7 月 13 日 7 月 12 日 7 月 11 日 7 月 10 日 7 月 9 日 7 月 8 日 7 月 7 日 7 月 6 日 7 月 5 日 7 月 4 日 7 月 3 日 7 月 2 日 7 月 1 日 6 月 30 日 6 月 29 日 6 月 28 日 6 月 27 日 6 月 26 日 6 月 25 日 6 月 24 日 6 月 23 日
Windows 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
Mac 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Linux 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

README

源代码
raw.githubusercontent.com

SBNF

Crate

用于编写 sublime-syntax 文件的一种 BNF 语法的语言。

现在在 实时游乐场 尝试它!

SBNF 目前用于 SWI-Prolog

动机和目标

编写语法定义容易出错,且结果难以维护。虽然添加了 branch_point 作为一个很好的特征,但在使用时显著增加了复杂性和重复。

SBNF 尝试做到以下几点:* 提供一种可维护的、声明性的语言来编写 sublime 语法定义* 快速编译以实现快速迭代* 编译到效率高的语法,可媲美手动制作的语法

安装

请注意,为了使用生成的语法,您至少需要 Sublime Text 版本 4077 及其对 Sublime Syntax 版本 2 的支持。

Sublime 包

导航到 发布页面,然后下载 SBNF.sublime-package 文件。这是一个 zip 文件。您需要将 zip 文件的内容提取到 Sublime Text 数据目录 中的 Packages/SBNF 文件夹。

Cargo

rust 安装后,您可以使用以下命令下载、构建和安装 SBNF 的最新发布版本:

$ cargo install sbnfc

或者,如果您想使用最新功能,克隆此存储库,然后使用以下命令构建和安装:

$ cargo install --path cli

Sublime 语法

SBNF 的语法定义在 sbnf/sbnf.sbnf 中。要编译它,只需运行 sbnf sbnf/sbnf.sbnf,然后您可以将 sbnf/ 目录作为符号链接或复制到您的用户包。

示例

以下是一个用于简化版 C 的 sbnf 语法。它仅允许全局/局部变量声明、函数定义和简单函数调用。即使这个简化版也极具挑战性,难以正确解析所需 meta.functionmeta.function-call scopes,因为函数定义和函数调用都需要分支点。

错误:语言“sbnf”不受支持
NAME = `simplec`

prototype : ( ~comment )* ;

comment : '(//+).*\n?'{comment.line, 1: punctuation.definition.comment} ;

main : ( variable-declaration | function-definition )* ;

IDENTIFIER = '\b[A-Za-z_]+\b'

function-definition{meta.function}
: type
  IDENTIFIER{entity.name.function}
  `(`
  `)`
  block
;

block{meta.block} : '{' statement* '}' ;

statement : variable-declaration
          | value ';'
          | block
          ;

variable-declaration : type IDENTIFIER{variable} ( '=' value )? ';' ;

type : IDENTIFIER{storage.type} ;

value : '[0-9]+'{constant.numeric}
      | function-call
      ;

# Function calls don't have arguments :)
function-call{meta.function-call}
: IDENTIFIER{variable.function meta.path} `(` `)` ;

以上语法编译成以下内容

%YAML 1.2
---
# https://text.sublime.net.cn/docs/syntax.html
version: 2
name: simplec
scope: source.simplec
contexts:
  # Rule: block
  block|0:
    - meta_content_scope: meta.block.simplec
    - match: '{'
      scope: meta.block.simplec
      set: block|1
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  # Rule: block
  block|1:
    - meta_content_scope: meta.block.simplec
    - include: include!block@1
    - match: '[0-9]+'
      scope: meta.block.simplec constant.numeric.simplec
      push: [block|meta, statement|0]
    - match: '{'
      scope: meta.block.simplec meta.block.simplec
      push: [block|meta, block|1]
    - match: '}'
      scope: meta.block.simplec
      pop: true
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  # Rule: block
  #  For branch point 'block@1'
  block|2|block@1:
    - match: '\b[A-Za-z_]+\b'
      scope: meta.block.simplec variable.simplec
      set: [block|meta, variable-declaration|2]
    - match: '\S'
      fail: block@1
  # Rule: block
  #  For branch point 'block@1'
  block|3|block@1:
    - match: '\('
      scope: meta.block.simplec meta.function-call.simplec
      set: [block|meta, statement|0, function-call|1]
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  # Meta scope context for block
  block|meta:
    - meta_content_scope: meta.block.simplec
    - match: ''
      pop: true
  # Rule: function-call
  function-call|0:
    - meta_content_scope: meta.function-call.simplec
    - match: '\('
      scope: meta.function-call.simplec
      set: function-call|1
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  # Rule: function-call
  function-call|1:
    - meta_content_scope: meta.function-call.simplec
    - match: '\)'
      scope: meta.function-call.simplec
      pop: true
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  function-call|2|block@1:
    - meta_include_prototype: false
    - match: '\b[A-Za-z_]+\b'
      scope: meta.function-call.simplec variable.function.simplec meta.path.simplec
      push: block|3|block@1
      pop: true
  # Rule: function-definition
  function-definition|0:
    - meta_content_scope: meta.function.simplec
    - match: '\)'
      scope: meta.function.simplec
      set: [function-definition|meta, block|0]
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  # Meta scope context for function-definition
  function-definition|meta:
    - meta_content_scope: meta.function.simplec
    - match: ''
      pop: true
  # Include context for branch point block@1
  include!block@1:
    - match: '(?=\b[A-Za-z_]+\b)'
      branch_point: block@1
      branch:
        - type|2|block@1
        - function-call|2|block@1
  # Include context for branch point main@1
  include!main@1:
    - match: '(?=\b[A-Za-z_]+\b)'
      branch_point: main@1
      branch:
        - type|0|main@1
        - type|1|main@1
  # Rule: main
  main:
    - include: include!main@1
    - match: '\S'
      scope: invalid.illegal.simplec
  # Rule: main
  #  For branch point 'main@1'
  main|0|main@1:
    - match: '\b[A-Za-z_]+\b'
      scope: variable.simplec
      push: main|2|main@1
      pop: true
    - match: '\S'
      fail: main@1
  # Rule: main
  #  For branch point 'main@1'
  main|1|main@1:
    - match: '\b[A-Za-z_]+\b'
      scope: meta.function.simplec entity.name.function.simplec
      push: main|3|main@1
      pop: true
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  # Rule: main
  #  For branch point 'main@1'
  main|2|main@1:
    - match: '='
      set: variable-declaration|0
    - match: ';'
      pop: true
    - match: '\S'
      fail: main@1
  # Rule: main
  #  For branch point 'main@1'
  main|3|main@1:
    - match: '\('
      scope: meta.function.simplec
      set: function-definition|0
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  # Rule: prototype
  prototype:
    - match: '(//+).*\n?'
      scope: comment.line.simplec
      captures:
        1: punctuation.definition.comment.simplec
  # Rule: statement
  statement|0:
    - match: ';'
      pop: true
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  type|0|main@1:
    - meta_include_prototype: false
    - match: '\b[A-Za-z_]+\b'
      scope: storage.type.simplec
      push: main|0|main@1
      pop: true
  type|1|main@1:
    - meta_include_prototype: false
    - match: '\b[A-Za-z_]+\b'
      scope: meta.function.simplec storage.type.simplec
      push: main|1|main@1
      pop: true
  type|2|block@1:
    - meta_include_prototype: false
    - match: '\b[A-Za-z_]+\b'
      scope: storage.type.simplec
      push: block|2|block@1
      pop: true
  # Rule: variable-declaration
  variable-declaration|0:
    - match: '[0-9]+'
      scope: constant.numeric.simplec
      set: variable-declaration|1
    - match: '\b[A-Za-z_]+\b'
      scope: meta.function-call.simplec variable.function.simplec meta.path.simplec
      set: [variable-declaration|1, function-call|0]
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  # Rule: variable-declaration
  variable-declaration|1:
    - match: ';'
      pop: true
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true
  # Rule: variable-declaration
  variable-declaration|2:
    - match: '='
      set: variable-declaration|0
    - match: ';'
      pop: true
    - match: '\S'
      scope: invalid.illegal.simplec
      pop: true

使用方法

SBNF文件包含两种类型的元素:条款和规则。条款提供语法元数据,如文件扩展名以及一些元编程。规则是定义语法解析和作用域的bnf风格的规则。

SBNF中的注释从#开始,到下一行结束。

请查看sbnf.sbnf以获取完整的语法示例。

条款

条款形式为 <name> <parameters> = <value>。名称必须遵循SCREAMING_SNAKE_CASE。以下名称保留用于元数据

  • NAME:语法的名称。默认为SBNF文件的基名称。
  • EXTENSIONS:空格分隔的文件扩展名列表。相当于sublime-syntax中的file_extensions
  • FIRST_LINE:用于匹配文件第一行的正则表达式。相当于sublime-syntax中的first_line_match
  • SCOPE:语法的默认作用域。默认为source后跟语法的小写字符串名称。
  • SCOPE_POSTFIX:添加到语法中所有作用域的后缀(不包括SCOPE条款)。默认为小写字符串名称。可以留空以不添加后缀。
  • HIDDEN:语法是否在Sublime Text菜单中显示。

示例

错误:语言“sbnf”不受支持
NAME = `SBNF`
EXTENSIONS = `sbnf`
# Don't need this, as this is already the default
# SCOPE = `source.sbnf`

规则

规则形式为<name> <parameters> <options> : <expression> ;。名称必须遵循kebab-case

与sublime-syntax文件一样,SBNF语法有两个入口点:mainprototype。它们的行为与sublime-syntax文件中的行为相同。只有直接或间接从入口点使用的规则会被编译。

规则可以可选地包含参数和选项。参数用于元编程,选项用于sublime-syntax特定的选项。

示例

错误:语言“sbnf”不受支持
a : 'a' ;
b{source.b} : 'b' ;
c[S] : 'c'{#[S]} ;
d[S]{text.d} : a b c[S] ;

表达式

表达式可以采用以下任何一种形式

  • `<literal>` <options>:一个匹配文本的字面值终端。
  • '<regex>' <options>:一个根据正则表达式匹配文本的终端。
  • <identifier> <arguments>:一个匹配另一个规则的非终止符。
  • <expr> | <expr>:表达式的选择。语法匹配左或右表达式。这可以用作列表,例如:'a' | 'b' | 'c'
  • <expr> <expr>:表达式的连接。语法匹配左表达式后跟右表达式。这可以用作列表,例如:'a' 'b' 'c'
  • (<expr>):分组。
  • <expr>?:一个可选表达式。语法匹配无或表达式。
  • <expr>*:一个重复表达式。语法匹配表达式任意次,包括0次。
  • ~<expr>:一个被动表达式。语法匹配任何文本直到表达式匹配。

选项

选项的形式如下:{<param>, <key>: <value>}。其中<param><key><value>可以包含任何文本,但不能包含,:`或`}。可以有任意数量的选项,具体取决于每个选项允许的数量。如果没有选项,则`{}`是可选的。

以下选项允许用于规则

  • <meta-scope>:规则的超作用域。相当于sublime-syntax中的`meta_scope`或`meta_content_scope`。

字面值和正则表达式终端允许以下参数

  • <scope>:终端的作用域。
  • <capture>: <scope>:正则表达式捕获组的范围。<capture>必须是一个整数。

参数

规则和条款的参数形式为[<value>, <value>]。其中<value>可以是正则表达式终端、字面值终端或标识符。可以多次使用相同的名称为具有不同参数集的规则或条款。

当使用时,具有参数的规则被实例化。匹配基于每个参数的类型和值。终端参数根据正则表达式等价性进行匹配,而规则参数按名称匹配。

不引用规则的标识符是规则作用域的唯一自由变量。它可以匹配任何参数,并且可以传入和/或嵌入。

可以使用以下语法插入变量:#[]。这可以在任何终端或选项内部完成。

示例

错误:语言“sbnf”不受支持
main
: a['a'] # instantiates rule 1
| a[a]   # instantiates rule 2
| a['b'] # instantiates rule 3
| b['b'] # error: Ambiguous instantiation
;

# Rule 1.
a['a'] : 'a' ;

# Rule 2.
a[a] : 'a' ;

# Rule 3.
a[A] : 'a' ;

b[A] : 'a' ;
b[B] : 'b' ;

还存在一组全局参数,它们是从命令行传入的。这些参数的格式与其他参数相同,并且应放在文件的顶部。它们可能只包含变量,并且可以在全局范围内使用,包括子句。

示例

错误:语言“sbnf”不受支持
# Declares a single global parameter
[TYPE]

# Can be used in clauses
NAME = 'd-#[TYPE]'

# As well as rules
main : '#[TYPE]' ;
# 'dmd' is passed to TYPE when compiled
$ sbnf syntax.sbnf dmd

包含/嵌入

SBNF 还支持包含/嵌入其他 Sublime 语法。这只可以在具有后缀 %include[<with_prototype>]{<syntax>} 的文本或正则表达式终端表达式上执行,用于包含语法,或者使用 %embed[<regex>]{<syntax>} 用于嵌入。

请注意,这些功能直接对应于 Sublime 语法中的包含/嵌入功能,因此具有相同的限制。

示例

错误:语言“sbnf”不受支持
# This is a basic implementation of the html script tag embedding the javascript
# syntax.
script
: '<script>'{tag.begin.script}
  %embed['</script>']{scope:source.js, embedded.js, 0: tag.end.script}
;
# The above translates to the following context
script:
  - match: '<script>'
    scope: tag.begin.script.example
    embed: scope:source.js
    embed_scope: embedded.js.example
    escape: '</script>'
    escape_captures:
      0: tag.end.script.example
    pop: true
  - match: '\S'
    scope: invalid.illegal.example
错误:语言“sbnf”不受支持
# This is a basic implementation of a regex string. It has a prototype rule that
# extends the regex syntax with an escape sequence for the string.

regex-prototype{include-prototype: false}
: ( ~`\'`{constant.character.escape} )*
  # A lookahead is required here, as otherwise we would only pop one context
  # The same is required in a sublime-syntax file
  ~'(?=\')'
;

regex-string{string.quoted}
: `'`{punctuation.definition.string.begin}
  %include[regex-prototype]{scope:source.regexp}
  `'`{punctuation.definition.string.end}
;
# The above translates to the following contexts
regex-string:
  - meta_content_scope: string.quoted.example
  - match: ''''
    scope: string.quoted.example punctuation.definition.string.begin.example
    set: [regex-string|0, regex-string|1]
  - match: '\S'
    scope: invalid.illegal.example
regex-string|0:
  - meta_content_scope: string.quoted.example
  - match: ''''
    scope: string.quoted.example punctuation.definition.string.end.example
    pop: true
  - match: '\S'
    scope: invalid.illegal.example
    pop: true
regex-string|1:
  - meta_include_prototype: false
  - match: ''
    set: scope:source.regexp
    with_prototype:
      - include: regex-prototype|0
regex-prototype|0:
  - meta_include_prototype: false
  - match: '\\'''
    scope: constant.character.escape.example
  - match: '(?='')'
    pop: true

命令行

$ sbnf --help
SBNF compiler 0.4.0

USAGE:
    sbnf [FLAGS] [OPTIONS] <INPUT> [ARGS]...

FLAGS:
    -g               Compile with debug scopes
    -h, --help       Prints help information
    -q               Do not display warnings
    -V, --version    Prints version information

OPTIONS:
    -o <output>        The file to write the compiled sublime-syntax to. Defaults to $INPUT.sublime-syntax if left out. Use a single dash `-` to write to stdout instead.

ARGS:
    <INPUT>      The SBNF file to compile
    <ARGS>...    Arguments to pass to the main and prototype rules

限制

正则表达式等价性

在确定是否在 sublime-syntax 中创建分支点时,SBNF 必须考虑正则表达式是否重叠。以下是一个例子

错误:语言“sbnf”不受支持
main : 'aa?'{scope1} 'b'
     | 'a'{scope2} 'c'
     ;

正则表达式 'aa?''a' 都可以匹配 a,这意味着需要一个分支点来正确解析这个语法。SBNF 不会 在这里创建分支点。由于正则表达式的复杂性,只有等价的正则表达式才会创建分支点。重写这个例子以便 SBNF 可以按预期工作会产生以下结果

错误:语言“sbnf”不受支持
main : 'aa'{scope1} 'b'
     | 'a'{scope1} 'b'
     | 'a'{scope2} 'c'
     ;

这在未来可能不会改变,因为 SBNF 不尝试理解任何正则表达式。

待办事项

  • 在编译器中修复已知的边缘情况。在几个地方,我们使用了 panic!() 而不是提供实现。
  • 当在非弹出循环中使用分支时添加警告。
  • 修复规则引用自身时的无限循环/递归。