Skip to content

Commit 241a47e

Browse files
Copilothzhangxyz
andauthored
Add BNF support package for bidirectional DS/Dsp syntax conversion (#68)
* Initial plan * Add BNF support package with ANTLR grammars and bidirectional converters Co-authored-by: hzhangxyz <[email protected]> * Add examples for BNF conversion Co-authored-by: hzhangxyz <[email protected]> * Improve operator extraction logic and add grammar design notes Co-authored-by: hzhangxyz <[email protected]> * Add comprehensive implementation summary documentation Co-authored-by: hzhangxyz <[email protected]> * Restructure BNF package: move configs to root, rename to atsds-bnf/apyds-bnf Co-authored-by: hzhangxyz <[email protected]> * Remove examples, tests, and TypeScript; simplify to basic JavaScript and Python functionality Co-authored-by: hzhangxyz <[email protected]> * Address feedback: rename src to atsds_bnf, remove cli.py, use setuptools-scm, consolidate docs Co-authored-by: hzhangxyz <[email protected]> * Add package-lock.json. * Add uv.lock. * Add .gitignore. * Run pre-commit. * Update README.md. * Update bnf binding for js. * Refactor BNF package: 1:1 match JS/Python, add rollup, clean dependencies Co-authored-by: hzhangxyz <[email protected]> * Remove useless variables. * Fix python binding. * Update uv.lock. * Update package lock. * Update pyproject.toml. * Update package.json. * Move *.g4. * Update package.json. * Update package.json. * Update *.g4. * Update setup.py. --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: hzhangxyz <[email protected]> Co-authored-by: Hao Zhang <[email protected]>
1 parent 87d4662 commit 241a47e

File tree

13 files changed

+3283
-0
lines changed

13 files changed

+3283
-0
lines changed

bnf/Ds.g4

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
grammar Ds;
2+
3+
rule_pool
4+
: NEWLINE* (rule (NEWLINE+ rule)*)? NEWLINE* EOF
5+
;
6+
7+
rule
8+
: (term NEWLINE+)* RULE NEWLINE term
9+
;
10+
11+
term
12+
: SYMBOL # symbol
13+
| '(subscript' term* ')' # subscript
14+
| '(function' term* ')' # function
15+
| '(unary' SYMBOL term ')' # unary
16+
| '(binary' SYMBOL term term ')' # binary
17+
;
18+
19+
RULE
20+
: '--' '-'*
21+
;
22+
23+
WHITESPACE
24+
: [ \t]+ -> skip
25+
;
26+
27+
COMMENT
28+
: '//' ~[\r\n]* -> skip
29+
;
30+
31+
NEWLINE
32+
: [\r\n]
33+
;
34+
35+
SYMBOL
36+
: ~[ \t\r\n,()[\]]+
37+
;

bnf/Dsp.g4

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
grammar Dsp;
2+
3+
rule_pool
4+
: NEWLINE* (rule (NEWLINE+ rule)*)? NEWLINE* EOF
5+
;
6+
7+
rule
8+
: term
9+
| (term (',' term)*)? '->' term
10+
;
11+
12+
term
13+
: SYMBOL # symbol
14+
| '(' term ')' # parentheses
15+
| term '::' term # binary
16+
| term '.' term # binary
17+
| term '[' term (',' term)* ']' # subscript
18+
| term '(' (term (',' term)*)? ')' # function
19+
| <assoc=right> ('~' | '!' | '-' | '+' | '&' | '*') term # unary
20+
| term '.*' term # binary
21+
| term ('*' | '/' | '%') term # binary
22+
| term ('+' | '-') term # binary
23+
| term ('<<' | '>>') term # binary
24+
| term ('<' | '>' | '<=' | '>=') term # binary
25+
| term ('==' | '!=') term # binary
26+
| term '&' term # binary
27+
| term '^' term # binary
28+
| term '|' term # binary
29+
| term '&&' term # binary
30+
| term '||' term # binary
31+
| term '=' term # binary
32+
;
33+
34+
WHITESPACE
35+
: [ \t]+ -> skip
36+
;
37+
38+
COMMENT
39+
: '//' ~[\r\n]* -> skip
40+
;
41+
42+
NEWLINE
43+
: [\r\n]
44+
;
45+
46+
SYMBOL
47+
: ~[ \t\r\n,()[\]]+
48+
;

bnf/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# BNF Support Package for DS
2+
3+
This package provides bidirectional conversion between two syntax formats for the DS deductive system:
4+
5+
- **Ds**: The lisp-like syntax currently used in DS
6+
- **Dsp**: A traditional readable syntax with infix operators

bnf/apyds_bnf/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!__init__.py

bnf/apyds_bnf/__init__.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
__all__ = ["parse", "unparse"]
2+
3+
from antlr4 import InputStream, CommonTokenStream
4+
from .DspLexer import DspLexer
5+
from .DspParser import DspParser
6+
from .DspVisitor import DspVisitor
7+
from .DsLexer import DsLexer
8+
from .DsParser import DsParser
9+
from .DsVisitor import DsVisitor
10+
11+
12+
class ParseVisitor(DspVisitor):
13+
def visitRule_pool(self, ctx):
14+
return "\n\n".join(self.visit(r) for r in ctx.rule_())
15+
16+
def visitRule(self, ctx):
17+
result = [self.visit(t) for t in ctx.term()]
18+
if len(result) == 1:
19+
return f"----\n{result[0]}"
20+
else:
21+
conclusion = result.pop()
22+
length = max(len(premise) for premise in result)
23+
result.append("-" * max(length, 4))
24+
result.append(conclusion)
25+
return "\n".join(result)
26+
27+
def visitSymbol(self, ctx):
28+
return ctx.SYMBOL().getText()
29+
30+
def visitParentheses(self, ctx):
31+
return self.visit(ctx.term())
32+
33+
def visitSubscript(self, ctx):
34+
return f"(subscript {' '.join(self.visit(t) for t in ctx.term())})"
35+
36+
def visitFunction(self, ctx):
37+
return f"(function {' '.join(self.visit(t) for t in ctx.term())})"
38+
39+
def visitUnary(self, ctx):
40+
return f"(unary {ctx.getChild(0).getText()} {self.visit(ctx.term())})"
41+
42+
def visitBinary(self, ctx):
43+
return f"(binary {ctx.getChild(1).getText()} {self.visit(ctx.term(0))} {self.visit(ctx.term(1))})"
44+
45+
46+
class UnparseVisitor(DsVisitor):
47+
def visitRule_pool(self, ctx):
48+
return "\n".join(self.visit(r) for r in ctx.rule_())
49+
50+
def visitRule(self, ctx):
51+
result = [self.visit(t) for t in ctx.term()]
52+
conclusion = result.pop()
53+
return ", ".join(result) + " -> " + conclusion
54+
55+
def visitSymbol(self, ctx):
56+
return ctx.SYMBOL().getText()
57+
58+
def visitSubscript(self, ctx):
59+
return f"{self.visit(ctx.term(0))}[{', '.join(self.visit(t) for t in ctx.term()[1:])}]"
60+
61+
def visitFunction(self, ctx):
62+
return f"{self.visit(ctx.term(0))}({', '.join(self.visit(t) for t in ctx.term()[1:])})"
63+
64+
def visitUnary(self, ctx):
65+
return f"({ctx.getChild(0).getText()} {self.visit(ctx.term())})"
66+
67+
def visitBinary(self, ctx):
68+
return f"({self.visit(ctx.term(0))} {ctx.getChild(1).getText()} {self.visit(ctx.term(1))})"
69+
70+
71+
def parse(input):
72+
chars = InputStream(input)
73+
lexer = DspLexer(chars)
74+
tokens = CommonTokenStream(lexer)
75+
parser = DspParser(tokens)
76+
tree = parser.rule_pool()
77+
visitor = ParseVisitor()
78+
return visitor.visit(tree)
79+
80+
81+
def unparse(input):
82+
chars = InputStream(input)
83+
lexer = DsLexer(chars)
84+
tokens = CommonTokenStream(lexer)
85+
parser = DsParser(tokens)
86+
tree = parser.rule_pool()
87+
visitor = UnparseVisitor()
88+
return visitor.visit(tree)

bnf/atsds_bnf/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!index.js

bnf/atsds_bnf/index.mjs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { InputStream, CommonTokenStream } from "antlr4";
2+
import DspLexer from "./DspLexer.js";
3+
import DspParser from "./DspParser.js";
4+
import DspVisitor from "./DspVisitor.js";
5+
import DsLexer from "./DsLexer.js";
6+
import DsParser from "./DsParser.js";
7+
import DsVisitor from "./DsVisitor.js";
8+
9+
class ParseVisitor extends DspVisitor {
10+
visitRule_pool(ctx) {
11+
return ctx
12+
.rule_()
13+
.map((r) => this.visit(r))
14+
.join("\n\n");
15+
}
16+
17+
visitRule(ctx) {
18+
const result = ctx.term().map((t) => this.visit(t));
19+
if (result.length === 1) {
20+
return `----\n${result[0]}`;
21+
} else {
22+
const conclusion = result.pop();
23+
const length = Math.max(...result.map((premise) => premise.length));
24+
result.push("-".repeat(Math.max(length, 4)));
25+
result.push(conclusion);
26+
return result.join("\n");
27+
}
28+
}
29+
30+
visitSymbol(ctx) {
31+
return ctx.SYMBOL().getText();
32+
}
33+
34+
visitParentheses(ctx) {
35+
return this.visit(ctx.term());
36+
}
37+
38+
visitSubscript(ctx) {
39+
return `(subscript ${ctx
40+
.term()
41+
.map((t) => this.visit(t))
42+
.join(" ")})`;
43+
}
44+
45+
visitFunction(ctx) {
46+
return `(function ${ctx
47+
.term()
48+
.map((t) => this.visit(t))
49+
.join(" ")})`;
50+
}
51+
52+
visitUnary(ctx) {
53+
return `(unary ${ctx.getChild(0).getText()} ${this.visit(ctx.term())})`;
54+
}
55+
56+
visitBinary(ctx) {
57+
return `(binary ${ctx.getChild(1).getText()} ${this.visit(ctx.term(0))} ${this.visit(ctx.term(1))})`;
58+
}
59+
}
60+
61+
class UnparseVisitor extends DsVisitor {
62+
visitRule_pool(ctx) {
63+
return ctx
64+
.rule_()
65+
.map((r) => this.visit(r))
66+
.join("\n");
67+
}
68+
69+
visitRule(ctx) {
70+
const result = ctx.term().map((t) => this.visit(t));
71+
const conclusion = result.pop();
72+
return result.join(", ") + " -> " + conclusion;
73+
}
74+
75+
visitSymbol(ctx) {
76+
return ctx.SYMBOL().getText();
77+
}
78+
79+
visitSubscript(ctx) {
80+
return `${this.visit(ctx.term(0))}[${ctx
81+
.term()
82+
.slice(1)
83+
.map((t) => this.visit(t))
84+
.join(", ")}]`;
85+
}
86+
87+
visitFunction(ctx) {
88+
return `${this.visit(ctx.term(0))}(${ctx
89+
.term()
90+
.slice(1)
91+
.map((t) => this.visit(t))
92+
.join(", ")})`;
93+
}
94+
95+
visitUnary(ctx) {
96+
return `(${ctx.getChild(0).getText()} ${this.visit(ctx.term())})`;
97+
}
98+
99+
visitBinary(ctx) {
100+
return `(${this.visit(ctx.term(0))} ${ctx.getChild(1).getText()} ${this.visit(ctx.term(1))})`;
101+
}
102+
}
103+
104+
export function parse(input) {
105+
const chars = new InputStream(input);
106+
const lexer = new DspLexer(chars);
107+
const tokens = new CommonTokenStream(lexer);
108+
const parser = new DspParser(tokens);
109+
const tree = parser.rule_pool();
110+
const visitor = new ParseVisitor();
111+
return visitor.visit(tree);
112+
}
113+
114+
export function unparse(input) {
115+
const chars = new InputStream(input);
116+
const lexer = new DsLexer(chars);
117+
const tokens = new CommonTokenStream(lexer);
118+
const parser = new DsParser(tokens);
119+
const tree = parser.rule_pool();
120+
const visitor = new UnparseVisitor();
121+
return visitor.visit(tree);
122+
}

0 commit comments

Comments
 (0)