diff --git a/TODO.md b/TODO.md index 91a9636a..0e1db41e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,8 +1,8 @@ -[syntax] use "@cicada-lang/partech" to implement new syntax +`"head" let` instead of `(let head)` `Node` with optionally named port -- `NetRenderer` show free port names +`defnode` instead of `defcons` and `defelim` `NetRenderer` -- format to simple text representation of graph @@ -12,6 +12,8 @@ change the `render` command to simple `run` command [maybe] quit using `ActiveEdge` +`NetRenderer` show free port names + # the paper phase space and monoid -- understand the model theory of linear logic diff --git a/docs/notes/design-of-new-syntax.md b/docs/notes/design-of-new-syntax.md index 61975ad5..3bed276a 100644 --- a/docs/notes/design-of-new-syntax.md +++ b/docs/notes/design-of-new-syntax.md @@ -59,13 +59,12 @@ defrule add1 add end ``` -`defn` is a short hand for simple `defrule`, -to define rules that works like a function. +`defru` is a short hand for simple `defrule`. ```inet -defn zero add end +defru zero add end -defn add1 add add add1 end +defru add1 add add add1 end ``` ```inet @@ -111,12 +110,12 @@ defrule cons append return-(append) end -defn null append end +defru null append end # the syntax for `let` and `get` is yet to be designed. # I use `<...>` and description for work-in-progress syntax design. -defn cons append +defru cons append append cons end @@ -158,13 +157,13 @@ defrule diff diff_open (diff)-left return-(diff_open) end -defn diff diff_append +defru diff diff_append diff_open diff end -defn diff diff_open +defru diff diff_open connect diff --git a/docs/tests/diff-list.inet b/docs/tests/diff-list.inet index fb80a6cb..a065a31a 100644 --- a/docs/tests/diff-list.inet +++ b/docs/tests/diff-list.inet @@ -1,29 +1,33 @@ -(defcons sole 0 1) +defcons sole 0 1 end -(defcons null 0 1) -(defcons cons 2 1) +defcons null 0 1 end +defcons cons 2 1 end -(defcons diff 2 1) +defcons diff 2 1 end -(defelim diff-append 2 1) +defelim diff_append 2 1 end -(defrule (diff diff-append) - (let diff-list end start) - end diff-list diff-open start diff) +defru diff diff_append + (let start) (let fin) (let diff_list) + fin diff_list diff_open start diff +end -(defelim diff-open 2 1) +defelim diff_open 2 1 end -(defrule (diff diff-open) - (let list end start) - list end connect - start) +defru diff diff_open + (let start) (let fin) (let list) + list fin connect + start +end -(defnet one-two-soles +defnet one_two_soles wire sole cons diff wire sole cons sole cons diff - diff-append) + diff_append +end -(defnet two-two-soles +defnet two_two_soles wire sole cons sole cons diff wire sole cons sole cons diff - diff-append) + diff_append +end diff --git a/docs/tests/diff-list.inet.one-two-soles.finial.txt b/docs/tests/diff-list.inet.one_two_soles.finial.txt similarity index 100% rename from docs/tests/diff-list.inet.one-two-soles.finial.txt rename to docs/tests/diff-list.inet.one_two_soles.finial.txt diff --git a/docs/tests/diff-list.inet.one-two-soles.initial.txt b/docs/tests/diff-list.inet.one_two_soles.initial.txt similarity index 66% rename from docs/tests/diff-list.inet.one-two-soles.initial.txt rename to docs/tests/diff-list.inet.one_two_soles.initial.txt index eb6166c3..1465870c 100644 --- a/docs/tests/diff-list.inet.one-two-soles.initial.txt +++ b/docs/tests/diff-list.inet.one_two_soles.initial.txt @@ -16,18 +16,18 @@ cons#8 diff#9 -- cons#8 [label="0-2"] cons#8 diff#9 -diff-append#10 -- diff#3 [label="1-2"] +diff_append#10 -- diff#3 [label="1-2"] diff#3 -diff-append#10 +diff_append#10 cons#2 -- diff#3 [label="1-1"] diff#3 cons#2 cons#6 -- diff#9 [label="1-1"] diff#9 cons#6 -diff-append#10 -- diff#9 [label="0-2" color=red] +diff_append#10 -- diff#9 [label="0-2" color=red] diff#9 -diff-append#10 -diff-append#10(2) -- diff-append#10 -diff-append#10 -diff-append#10(2) \ No newline at end of file +diff_append#10 +diff_append#10(2) -- diff_append#10 +diff_append#10 +diff_append#10(2) \ No newline at end of file diff --git a/docs/tests/diff-list.inet.two-two-soles.finial.txt b/docs/tests/diff-list.inet.two_two_soles.finial.txt similarity index 100% rename from docs/tests/diff-list.inet.two-two-soles.finial.txt rename to docs/tests/diff-list.inet.two_two_soles.finial.txt diff --git a/docs/tests/diff-list.inet.two-two-soles.initial.txt b/docs/tests/diff-list.inet.two_two_soles.initial.txt similarity index 72% rename from docs/tests/diff-list.inet.two-two-soles.initial.txt rename to docs/tests/diff-list.inet.two_two_soles.initial.txt index 19ac3460..3425c168 100644 --- a/docs/tests/diff-list.inet.two-two-soles.initial.txt +++ b/docs/tests/diff-list.inet.two_two_soles.initial.txt @@ -22,18 +22,18 @@ cons#24 diff#25 -- cons#24 [label="0-2"] cons#24 diff#25 -diff-append#26 -- diff#19 [label="1-2"] +diff_append#26 -- diff#19 [label="1-2"] diff#19 -diff-append#26 +diff_append#26 cons#16 -- diff#19 [label="1-1"] diff#19 cons#16 cons#22 -- diff#25 [label="1-1"] diff#25 cons#22 -diff-append#26 -- diff#25 [label="0-2" color=red] +diff_append#26 -- diff#25 [label="0-2" color=red] diff#25 -diff-append#26 -diff-append#26(2) -- diff-append#26 -diff-append#26 -diff-append#26(2) \ No newline at end of file +diff_append#26 +diff_append#26(2) -- diff_append#26 +diff_append#26 +diff_append#26(2) \ No newline at end of file diff --git a/docs/tests/list.inet b/docs/tests/list.inet index 8c8c0fe9..4ad268f1 100644 --- a/docs/tests/list.inet +++ b/docs/tests/list.inet @@ -1,16 +1,17 @@ -(defcons sole 0 1) +defcons sole 0 1 end -(defcons null 0 1) +defcons null 0 1 end -(defcons cons 2 1) +defcons cons 2 1 end -(defelim append 2 1) +defelim append 2 1 end -(defrule (null append)) +defru null append end -(defrule (cons append) (let head) append head cons) +defru cons append (let head) append head cons end -(defnet six-soles +defnet six_soles null sole cons sole cons sole cons null sole cons sole cons sole cons - append) + append +end \ No newline at end of file diff --git a/docs/tests/list.inet.six-soles.finial.txt b/docs/tests/list.inet.six_soles.finial.txt similarity index 100% rename from docs/tests/list.inet.six-soles.finial.txt rename to docs/tests/list.inet.six_soles.finial.txt diff --git a/docs/tests/list.inet.six-soles.initial.txt b/docs/tests/list.inet.six_soles.initial.txt similarity index 100% rename from docs/tests/list.inet.six-soles.initial.txt rename to docs/tests/list.inet.six_soles.initial.txt diff --git a/docs/tests/nat.inet b/docs/tests/nat.inet index 4a9bf8f4..9471d6dd 100644 --- a/docs/tests/nat.inet +++ b/docs/tests/nat.inet @@ -1,17 +1,19 @@ -(defcons zero 0 1) +defcons zero 0 1 end -(defcons add1 1 1) +defcons add1 1 1 end -(defelim add 2 1) +defelim add 2 1 end -(defrule (zero add)) +defru zero add end -(defrule (add1 add) add add1) +defru add1 add add add1 end -(defnet two +defnet two zero add1 zero add1 - add) + add +end -(defnet four - two two add) +defnet four + two two add +end \ No newline at end of file diff --git a/docs/todo/diff-list.inet b/docs/todo/diff-list.inet deleted file mode 100644 index 064aaf63..00000000 --- a/docs/todo/diff-list.inet +++ /dev/null @@ -1,33 +0,0 @@ -(defcons sole 0) - -(defcons null 0) -(defcons cons 2) - -(defcons diff 2) - -(defelim diff-append 2) - -(defrule - (diff-append (diff start end) diff-list) - (diff start (diff-open diff-list end))) - -(defelim diff-open 2) - -(defrule - (diff-open (diff start end) list) - (connect end list) - start) - -(defnet one-two-soles - (let-wire x) - (let-wire y) - (diff-append - (diff (cons sole x) x) - (diff (cons sole (cons sole y)) y))) - -(defnet two-two-soles - (let-wire x) - (let-wire y) - (diff-append - (diff (cons sole (cons sole x)) x) - (diff (cons sole (cons sole y)) y))) diff --git a/package-lock.json b/package-lock.json index 0c367f2b..9949feef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.12", "license": "GPL-3.0-or-later", "dependencies": { - "@cicada-lang/sexp": "^0.0.36", + "@cicada-lang/partech": "^0.2.5", "@xieyuheng/command-line": "^0.0.10", "@xieyuheng/ty": "^0.1.22" }, @@ -24,13 +24,14 @@ "typescript": "^5.1.6" } }, - "node_modules/@cicada-lang/sexp": { - "version": "0.0.36", - "resolved": "https://registry.npmjs.org/@cicada-lang/sexp/-/sexp-0.0.36.tgz", - "integrity": "sha512-ogbz4SfwfkA8PRVmRcretSs3avN5QlsNPzuVghgchIyFQNI0Xs5T11MQsTuhIbQmW/uMd1WujeLCHn/IZ6D1zA==", + "node_modules/@cicada-lang/partech": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@cicada-lang/partech/-/partech-0.2.5.tgz", + "integrity": "sha512-Ns5L/tqbmlrkFOaG2I6NHkxmTR1hafH0WMQByEOvVAZ1kElMg0vHXvMbLwLaPqWsBz4qzT6kQ9fRKjIDDEUNyg==", "dependencies": { "fast-deep-equal": "^3.1.3", - "picocolors": "^1.0.0" + "picocolors": "^1.0.0", + "regexp-match-indices": "^1.0.2" } }, "node_modules/@nodelib/fs.scandir": { @@ -569,6 +570,22 @@ "node": ">=0.12" } }, + "node_modules/regexp-match-indices": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", + "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==", + "dependencies": { + "regexp-tree": "^0.1.11" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", diff --git a/package.json b/package.json index 6c23c475..ca20bd11 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "typescript": "^5.1.6" }, "dependencies": { - "@cicada-lang/sexp": "^0.0.36", + "@cicada-lang/partech": "^0.2.5", "@xieyuheng/command-line": "^0.0.10", "@xieyuheng/ty": "^0.1.22" }, diff --git a/src/console/commands/RenderCommand.ts b/src/console/commands/RenderCommand.ts index 41837874..455ebf26 100644 --- a/src/console/commands/RenderCommand.ts +++ b/src/console/commands/RenderCommand.ts @@ -1,4 +1,4 @@ -import { ParsingError } from "@cicada-lang/sexp/lib/errors" +import { ParsingError } from "@cicada-lang/partech/lib/errors" import { Command, CommandRunner } from "@xieyuheng/command-line" import ty from "@xieyuheng/ty" import fs from "fs" @@ -68,7 +68,7 @@ export class RenderCommand extends Command { } } catch (error) { if (error instanceof ParsingError) { - const report = error.span.report(text) + const report = error.report(text) console.error(report) } diff --git a/src/lang/errors/ParsingError.ts b/src/lang/errors/ParsingError.ts index 414a6083..379e6b6f 100644 --- a/src/lang/errors/ParsingError.ts +++ b/src/lang/errors/ParsingError.ts @@ -1 +1 @@ -export { ParsingError } from "@cicada-lang/sexp/lib/errors" +export { ParsingError } from "@cicada-lang/partech/lib/errors" diff --git a/src/lang/span/index.ts b/src/lang/span/index.ts index 1a07a001..3c6037a3 100644 --- a/src/lang/span/index.ts +++ b/src/lang/span/index.ts @@ -1 +1 @@ -export { Span } from "@cicada-lang/sexp/lib/span" +export { Span } from "@cicada-lang/partech/lib/span" diff --git a/src/lang/syntax/Parser.ts b/src/lang/syntax/Parser.ts deleted file mode 100644 index ef329f99..00000000 --- a/src/lang/syntax/Parser.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Parser as SexpParser } from "@cicada-lang/sexp/lib/parser" -import { Stmt } from "../stmt" -import { matchStmt } from "./matchStmt" - -export class Parser extends SexpParser { - constructor() { - super({ - quotes: [ - { mark: "'", symbol: "quote" }, - { mark: ",", symbol: "unquote" }, - { mark: "`", symbol: "quasiquote" }, - ], - parentheses: [ - { start: "(", end: ")" }, - { start: "[", end: "]" }, - ], - comments: [";"], - }) - } - - parseStmts(text: string): Array { - return this.parseSexps(text).map(matchStmt) - } -} diff --git a/src/lang/syntax/grammars/index.ts b/src/lang/syntax/grammars/index.ts new file mode 100644 index 00000000..6e51834e --- /dev/null +++ b/src/lang/syntax/grammars/index.ts @@ -0,0 +1,10 @@ +import * as pt from "@cicada-lang/partech" + +export const zero_or_more = pt.grammars.zero_or_more +export const one_or_more = pt.grammars.one_or_more +export const optional = pt.grammars.optional +export const dashline = pt.grammars.dashline + +export * from "./name" +export * from "./stmt" +export * from "./word" diff --git a/src/lang/syntax/grammars/name.ts b/src/lang/syntax/grammars/name.ts new file mode 100644 index 00000000..4f6a99e1 --- /dev/null +++ b/src/lang/syntax/grammars/name.ts @@ -0,0 +1,24 @@ +// NOTE Preserve keywords for JSON. + +const preserved = ["end"] + +export const variable_name = { + $pattern: [ + "identifier", + `(^(?!(${preserved.join("|")})$)([_A-Za-z][_\\p{Letter}0-9]*))`, + ], +} + +export const variable_names = { + $grammar: { + "variable_names:variable_names": [ + { + variable_names: { + $ap: ["zero_or_more", "variable_name", '","'], + }, + }, + { last_name: "variable_name" }, + { $ap: ["optional", '","'] }, + ], + }, +} diff --git a/src/lang/syntax/grammars/stmt.ts b/src/lang/syntax/grammars/stmt.ts new file mode 100644 index 00000000..5312f7a5 --- /dev/null +++ b/src/lang/syntax/grammars/stmt.ts @@ -0,0 +1,37 @@ +export const stmt = { + $grammar: { + "stmt:defcons": [ + '"defcons"', + { name: "variable_name" }, + { inputArity: { $pattern: ["number"] } }, + { outputArity: { $pattern: ["number"] } }, + '"end"', + ], + "stmt:defelim": [ + '"defelim"', + { name: "variable_name" }, + { inputArity: { $pattern: ["number"] } }, + { outputArity: { $pattern: ["number"] } }, + '"end"', + ], + "stmt:defru": [ + '"defru"', + { start: "variable_name" }, + { end: "variable_name" }, + { words: "words" }, + '"end"', + ], + "stmt:defnet": [ + '"defnet"', + { name: "variable_name" }, + { words: "words" }, + '"end"', + ], + }, +} + +export const stmts = { + $grammar: { + "stmts:stmts": [{ stmts: { $ap: ["zero_or_more", "stmt"] } }], + }, +} diff --git a/src/lang/syntax/grammars/word.ts b/src/lang/syntax/grammars/word.ts new file mode 100644 index 00000000..cc6019a9 --- /dev/null +++ b/src/lang/syntax/grammars/word.ts @@ -0,0 +1,12 @@ +export const word = { + $grammar: { + "word:call": [{ name: "variable_name" }], + "word:let": ['"("', '"let"', { name: "variable_name" }, '")"'], + }, +} + +export const words = { + $grammar: { + "words:words": [{ words: { $ap: ["zero_or_more", "word"] } }], + }, +} diff --git a/src/lang/syntax/index.ts b/src/lang/syntax/index.ts index e1edf1fb..9ec56324 100644 --- a/src/lang/syntax/index.ts +++ b/src/lang/syntax/index.ts @@ -1,8 +1,18 @@ -import { Stmt } from "../stmt" -import { Parser } from "./Parser" -import { matchStmt } from "./matchStmt" - -export function parseStmts(text: string): Array { - const parser = new Parser() - return parser.parseSexps(text).map(matchStmt) -} +import * as pt from "@cicada-lang/partech" +import * as grammars from "./grammars" +import * as matchers from "./matchers" + +/** + + TODO We should use `camelCase` naming convention, + but "@cicada-lang/partech" is using `snake_case`, + we follow the library for now (will change eventually). + +**/ + +export const parseStmts = pt.gen_parse({ + preprocess: pt.preprocess.erase_comment, + lexer: pt.lexers.common, + grammar: pt.grammar_start(grammars, "stmts"), + matcher: matchers.stmts_matcher, +}) diff --git a/src/lang/syntax/matchStmt.ts b/src/lang/syntax/matchStmt.ts deleted file mode 100644 index aaaf5932..00000000 --- a/src/lang/syntax/matchStmt.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { - match, - matchList, - matchNumber, - matchSymbol, -} from "@cicada-lang/sexp/lib/match" -import { list, v } from "@cicada-lang/sexp/lib/pattern-exp" -import { Sexp } from "@cicada-lang/sexp/lib/sexp" -import { Stmt } from "../stmt" -import * as Stmts from "../stmts" -import { Word } from "../word" -import * as Exps from "../words" - -export function matchStmt(sexp: Sexp): Stmt { - return match(sexp, [ - [ - ["defcons", v("name"), v("inputArity"), v("outputArity")], - ({ name, inputArity, outputArity }) => - new Stmts.Defcons( - matchSymbol(name), - matchNumber(inputArity), - matchNumber(outputArity), - sexp.span, - ), - ], - [ - ["defelim", v("name"), v("inputArity"), v("outputArity")], - ({ name, inputArity, outputArity }) => - new Stmts.Defelim( - matchSymbol(name), - matchNumber(inputArity), - matchNumber(outputArity), - sexp.span, - ), - ], - [ - list(["defnet", v("name")], v("words")), - ({ name, input, output, words }) => - new Stmts.Defnet(matchSymbol(name), matchExps(words), sexp.span), - ], - [ - list(["defrule", [v("start"), v("end")]], v("words")), - ({ start, end, words }) => - new Stmts.Defrule( - matchSymbol(start), - matchSymbol(end), - matchExps(words), - sexp.span, - ), - ], - ]) -} - -function matchExps(sexp: Sexp): Array { - return matchList(sexp, matchExp) -} - -function matchExp(sexp: Sexp): Word { - return match(sexp, [ - [ - list(["let"], v("names")), - ({ names }) => new Exps.Let(matchList(names, matchSymbol), sexp.span), - ], - [v("name"), ({ name }) => new Exps.Call(matchSymbol(sexp), sexp.span)], - ]) -} diff --git a/src/lang/syntax/matchers/index.ts b/src/lang/syntax/matchers/index.ts new file mode 100644 index 00000000..55351fa3 --- /dev/null +++ b/src/lang/syntax/matchers/index.ts @@ -0,0 +1,3 @@ +export * from "./name_matcher" +export * from "./stmt_matcher" +export * from "./word_matcher" diff --git a/src/lang/syntax/matchers/name_matcher.ts b/src/lang/syntax/matchers/name_matcher.ts new file mode 100644 index 00000000..79175a76 --- /dev/null +++ b/src/lang/syntax/matchers/name_matcher.ts @@ -0,0 +1,10 @@ +import * as pt from "@cicada-lang/partech" + +export function variable_names_matcher(tree: pt.Tree): Array { + return pt.matcher({ + "variable_names:variable_names": ({ variable_names, last_name }) => [ + ...pt.matchers.zero_or_more_matcher(variable_names).map(pt.str), + pt.str(last_name), + ], + })(tree) +} diff --git a/src/lang/syntax/matchers/stmt_matcher.ts b/src/lang/syntax/matchers/stmt_matcher.ts new file mode 100644 index 00000000..c20dfd1e --- /dev/null +++ b/src/lang/syntax/matchers/stmt_matcher.ts @@ -0,0 +1,39 @@ +import * as pt from "@cicada-lang/partech" +import type { Stmt } from "../../stmt" +import * as Stmts from "../../stmts" +import * as matchers from "../matchers" + +export function stmt_matcher(tree: pt.Tree): Stmt { + return pt.matcher({ + "stmt:defcons": ({ name, inputArity, outputArity }, { span }) => + new Stmts.Defcons( + pt.str(name), + Number(pt.str(inputArity)), + Number(pt.str(outputArity)), + span, + ), + "stmt:defelim": ({ name, inputArity, outputArity }, { span }) => + new Stmts.Defelim( + pt.str(name), + Number(pt.str(inputArity)), + Number(pt.str(outputArity)), + span, + ), + "stmt:defru": ({ start, end, words }, { span }) => + new Stmts.Defrule( + pt.str(start), + pt.str(end), + matchers.words_matcher(words), + span, + ), + "stmt:defnet": ({ name, words }, { span }) => + new Stmts.Defnet(pt.str(name), matchers.words_matcher(words), span), + })(tree) +} + +export function stmts_matcher(tree: pt.Tree): Array { + return pt.matcher({ + "stmts:stmts": ({ stmts }) => + pt.matchers.zero_or_more_matcher(stmts).map(stmt_matcher), + })(tree) +} diff --git a/src/lang/syntax/matchers/word_matcher.ts b/src/lang/syntax/matchers/word_matcher.ts new file mode 100644 index 00000000..b522ede9 --- /dev/null +++ b/src/lang/syntax/matchers/word_matcher.ts @@ -0,0 +1,17 @@ +import * as pt from "@cicada-lang/partech" +import { Word } from "../../word" +import * as Words from "../../words" + +export function word_matcher(tree: pt.Tree): Word { + return pt.matcher({ + "word:call": ({ name }, { span }) => new Words.Call(pt.str(name), span), + "word:let": ({ name }, { span }) => new Words.Let([pt.str(name)], span), + })(tree) +} + +export function words_matcher(tree: pt.Tree): Array { + return pt.matcher({ + "words:words": ({ words }) => + pt.matchers.zero_or_more_matcher(words).map(word_matcher), + })(tree) +}