Skip to content

Commit 82a02c3

Browse files
refactor: semantic port to AffineScript (#363)
Automated semantic porting.
1 parent 3aa12bd commit 82a02c3

8 files changed

Lines changed: 738 additions & 24 deletions

File tree

Lines changed: 176 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,180 @@
11
// SPDX-License-Identifier: MPL-2.0
2-
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell
3-
// Ported via Harvard Engine bulk-processor
2+
// Ported via Harvard Engine (Semantic pass)
43

54
module Components;
65

7-
// TODO: Complete semantic implementation
6+
// SPDX-License-Identifier: MPL-2.0
7+
// SPDX-FileCopyrightText: 2025 Coq-Jr Contributors
8+
9+
// UI Components for jsCoq
10+
11+
module Html = {
12+
fn element = (tag: string, ~className="", ~id="", children: array<string>): string => {
13+
fn classAttr = className != "" ? ` class="${className}"` : ""
14+
fn idAttr = id != "" ? ` id="${id}"` : ""
15+
fn content = Js.Array2.joinWith(children, "")
16+
`<${tag}${idAttr}${classAttr}>${content}</${tag}>`
17+
}
18+
19+
fn div = element("div", ...)
20+
fn span = element("span", ...)
21+
fn p = element("p", ...)
22+
fn h3 = element("h3", ...)
23+
fn h4 = element("h4", ...)
24+
fn h5 = element("h5", ...)
25+
fn ul = element("ul", ...)
26+
fn li = element("li", ...)
27+
fn a = (~href: string, text: string): string => `<a href="${href}">${text}</a>`
28+
fn img = (~src: string, ~height: string, ~alt=""): string =>
29+
`<img src="${src}" height="${height}" alt="${alt}">`
30+
fn kbd = (text: string): string => `<kbd>${text}</kbd>`
31+
fn code = (text: string): string => `<code>${text}</code>`
32+
fn em = (text: string): string => `<em>${text}</em>`
33+
fn i = (text: string): string => `<i>${text}</i>`
34+
fn hr = (): string => `<hr/>`
35+
fn br = (): string => `<br/>`
36+
37+
fn textarea = (~id: string, content: string): string =>
38+
`<textarea id="${id}">${content}</textarea>`
39+
40+
fn table = (~className="", rows: array<string>): string => {
41+
fn classAttr = className != "" ? ` class="${className}"` : ""
42+
`<table${classAttr}>${Js.Array2.joinWith(rows, "")}</table>`
43+
}
44+
45+
fn tr = (cells: array<string>): string => `<tr>${Js.Array2.joinWith(cells, "")}</tr>`
46+
fn th = (content: string): string => `<th>${content}</th>`
47+
fn td = (content: string): string => `<td>${content}</td>`
48+
}
49+
50+
module JsCoqName = {
51+
fn render = (): string => {
52+
Html.span(~className="jscoq-name", ["jsCoq"])
53+
}
54+
}
55+
56+
module ActionTable = {
57+
struct action { {
58+
button: string,
59+
keyBinding: string,
60+
description: string,
61+
}
62+
63+
fn actions: array<action> = [
64+
{
65+
button: `${Html.img(~src="ui-images/down.png", ~height="15px")}${Html.img(
66+
~src="ui-images/up.png",
67+
~height="15px",
68+
)}`,
69+
keyBinding: `${Html.kbd("Alt")}+${Html.kbd("↓")}/${Html.kbd("↑")} or${Html.br()}${Html.kbd(
70+
"Alt",
71+
)}+${Html.kbd("N")}/${Html.kbd("P")}`,
72+
description: "Move through the proof.",
73+
},
74+
{
75+
button: Html.img(~src="ui-images/to-cursor.png", ~height="20px"),
76+
keyBinding: `${Html.kbd("Alt")}+${Html.kbd("Enter")} or${Html.br()} ${Html.kbd(
77+
"Alt",
78+
)}+${Html.kbd("→")}`,
79+
description: "Run (or go back) to the current point.",
80+
},
81+
{
82+
button: Html.img(~src="ui-images/power-button-512-black.png", ~height="20px"),
83+
keyBinding: Html.kbd("F8"),
84+
description: "Toggles the goal panel.",
85+
},
86+
]
87+
88+
fn render = (): string => {
89+
fn headerRow = Html.tr([Html.th("Button"), Html.th("Key binding"), Html.th("Action")])
90+
91+
fn rows = Js.Array2.map(actions, action => {
92+
Html.tr([Html.td(action.button), Html.td(action.keyBinding), Html.td(action.description)])
93+
})
94+
95+
Html.table(~className="doc-actions", Js.Array2.concat([headerRow], rows))
96+
}
97+
}
98+
99+
module TeamSection = {
100+
struct teamMember { {
101+
name: string,
102+
url: string,
103+
affiliations: array<(string, string)>,
104+
}
105+
106+
fn devTeam: array<teamMember> = [
107+
{
108+
name: "Emilio Jesús Gallego Arias",
109+
url: "https://www.irif.fr/~gallego/",
110+
affiliations: [
111+
("Inria", "https://www.inria.fr"),
112+
("Université de Paris", "https://u-paris.fr"),
113+
("IRIF", "https://www.irif.fr"),
114+
],
115+
},
116+
{
117+
name: "Shachar Itzhaky",
118+
url: "https://www.cs.technion.ac.il/~shachari/",
119+
affiliations: [("Technion", "https://cs.technion.ac.il")],
120+
},
121+
]
122+
123+
fn contributors: array<teamMember> = [
124+
{
125+
name: "Benoît Pin",
126+
url: "",
127+
affiliations: [
128+
("CRI", "https://www.cri.ensmp.fr/"),
129+
("MINES ParisTech", "https://www.minesparis.psl.eu"),
130+
],
131+
},
132+
]
133+
134+
fn renderMember = (member: teamMember): string => {
135+
fn nameLink = member.url != "" ? Html.a(~href=member.url, member.name) : member.name
136+
137+
fn affiliationLinks = Js.Array2.joinWith(
138+
Js.Array2.map(member.affiliations, ((name, url)) => Html.a(~href=url, name)),
139+
", ",
140+
)
141+
142+
Html.li([`${nameLink} (${affiliationLinks})`])
143+
}
144+
145+
fn render = (): string => {
146+
fn devList = Js.Array2.joinWith(Js.Array2.map(devTeam, renderMember), "")
147+
fn contribList = Js.Array2.joinWith(Js.Array2.map(contributors, renderMember), "")
148+
149+
Html.div(
150+
~id="team",
151+
[
152+
`<a name="team"></a>`,
153+
Html.p([Html.i("The dev team")]),
154+
Html.ul([devList]),
155+
Html.p([Html.i("Contributors")]),
156+
Html.ul([contribList]),
157+
],
158+
)
159+
}
160+
}
161+
162+
module CodeExamples = {
163+
fn imports = `From Coq Require Import ssreflect ssrfun ssrbool.
164+
From mathcomp Require Import eqstruct ssrnat div prime.`
165+
166+
fn primeAbove1 = `(* A nice proof of the infinitude of primes, by Georges Gonthier *)
167+
Lemma prime_above m : {p | m < p & prime p}.
168+
Proof.`
169+
170+
fn primeAbove2 = `have /pdivP[p pr_p p_dv_m1]: 1 < m\`! + 1
171+
by rewrite addn1 ltnS fact_gt0.`
172+
173+
fn primeAbove3 = `exists p => //; rewrite ltnNge; apply: contraL p_dv_m1 => p_le_m.`
174+
175+
fn primeAbove4 = `by rewrite dvdn_addr ?dvdn_fact ?prime_gt0 // gtnNdvd ?prime_gt1.
176+
Qed.`
177+
178+
fn codeIds = ["addnC", "prime_above1", "prime_above2", "prime_above3", "prime_above4"]
179+
}
180+

echidna-playground/src/Deno.affine

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,37 @@
11
// SPDX-License-Identifier: MPL-2.0
2-
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell
3-
// Ported via Harvard Engine bulk-processor
2+
// Ported via Harvard Engine (Semantic pass)
43

54
module Deno;
65

7-
// TODO: Complete semantic implementation
6+
// SPDX-License-Identifier: MPL-2.0
7+
// SPDX-FileCopyrightText: 2025 Coq-Jr Contributors
8+
9+
// Deno HTTP server bindings for ReScript
10+
11+
struct request
12+
struct response
13+
14+
// Deno namespace bindings
15+
@val @scope("Deno") external readFile: string => Js.Promise.t<Js.TypedArray2.Uint8Array.t> = "readFile"
16+
17+
struct serveOptions { {port: int}
18+
struct serveHandler { request => Js.Promise.t<response>
19+
20+
@val @scope("Deno") external serve: (serveOptions, serveHandler) => unit = "serve"
21+
22+
// Request bindings
23+
@get external getUrl: request => string = "url"
24+
@get external getMethod: request => string = "method"
25+
26+
// Response constructor
27+
@new external makeResponse: (string, {"headers": {"content-struct": string}}) => response = "Response"
28+
@new external makeResponseWithStatus: (string, {"status": int}) => response = "Response"
29+
@new external makeResponseBytes: (Js.TypedArray2.Uint8Array.t, {"headers": {"content-struct": string}}) => response = "Response"
30+
31+
// URL parsing
32+
struct url { {pathname: string}
33+
@new external makeUrl: string => url = "URL"
34+
35+
// Console
36+
@val @scope("console") external log: string => unit = "log"
37+

echidna-playground/src/Dom.affine

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,60 @@
11
// SPDX-License-Identifier: MPL-2.0
2-
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell
3-
// Ported via Harvard Engine bulk-processor
2+
// Ported via Harvard Engine (Semantic pass)
43

54
module Dom;
65

7-
// TODO: Complete semantic implementation
6+
// SPDX-License-Identifier: MPL-2.0
7+
// SPDX-FileCopyrightText: 2025 Coq-Jr Contributors
8+
9+
// DOM bindings for ReScript
10+
11+
struct element
12+
struct document
13+
struct window
14+
15+
@val external document: document = "document"
16+
@val external window: window = "window"
17+
18+
@send external getElementById: (document, string) => Js.Nullable.t<element> = "getElementById"
19+
@send external querySelector: (document, string) => Js.Nullable.t<element> = "querySelector"
20+
@send external querySelectorAll: (document, string) => array<element> = "querySelectorAll"
21+
22+
@send external createElement: (document, string) => element = "createElement"
23+
@send external createTextNode: (document, string) => element = "createTextNode"
24+
25+
@send external appendChild: (element, element) => unit = "appendChild"
26+
@send external removeChild: (element, element) => unit = "removeChild"
27+
@send external replaceChild: (element, element, element) => unit = "replaceChild"
28+
29+
@set external setInnerHTML: (element, string) => unit = "innerHTML"
30+
@get external getInnerHTML: element => string = "innerHTML"
31+
32+
@set external setTextContent: (element, string) => unit = "textContent"
33+
@get external getTextContent: element => string = "textContent"
34+
35+
@set external setClassName: (element, string) => unit = "className"
36+
@get external getClassName: element => string = "className"
37+
38+
@send external setAttribute: (element, string, string) => unit = "setAttribute"
39+
@send external getAttribute: (element, string) => Js.Nullable.t<string> = "getAttribute"
40+
@send external removeAttribute: (element, string) => unit = "removeAttribute"
41+
42+
@send external addEventListener: (element, string, unit => unit) => unit = "addEventListener"
43+
@send external removeEventListener: (element, string, unit => unit) => unit = "removeEventListener"
44+
45+
@get external getValue: element => string = "value"
46+
@set external setValue: (element, string) => unit = "value"
47+
48+
module Style = {
49+
@set external setDisplay: (element, string) => unit = "style.display"
50+
@set external setVisibility: (element, string) => unit = "style.visibility"
51+
@set external setBackgroundColor: (element, string) => unit = "style.backgroundColor"
52+
@set external setColor: (element, string) => unit = "style.color"
53+
@set external setPadding: (element, string) => unit = "style.padding"
54+
@set external setMargin: (element, string) => unit = "style.margin"
55+
}
56+
57+
module Body = {
58+
@val external body: element = "document.body"
59+
}
60+
Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,68 @@
11
// SPDX-License-Identifier: MPL-2.0
2-
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell
3-
// Ported via Harvard Engine bulk-processor
2+
// Ported via Harvard Engine (Semantic pass)
43

54
module JsCoq;
65

7-
// TODO: Complete semantic implementation
6+
// SPDX-License-Identifier: MPL-2.0
7+
// SPDX-FileCopyrightText: 2025 Coq-Jr Contributors
8+
9+
// JsCoq bindings and structs
10+
11+
struct editorMode { {
12+
@as("company-coq") companyCoq: bool,
13+
}
14+
15+
struct editorConfig { {
16+
mode: editorMode,
17+
keyMap: string,
18+
}
19+
20+
struct coqOptions { {
21+
implicit_libs: bool,
22+
focus: bool,
23+
editor: editorConfig,
24+
init_pkgs: array<string>,
25+
all_pkgs: array<string>,
26+
}
27+
28+
struct coqInstance
29+
30+
// JsCoq loader bindings
31+
@module("jscoq/ui-js/jscoq-loader.js")
32+
external startExternal: (array<string>, coqOptions) => Js.Promise.t<coqInstance> = "start"
33+
34+
module JsCoqLoader = {
35+
@val @scope("JsCoq")
36+
external start: (array<string>, coqOptions) => Js.Promise.t<coqInstance> = "start"
37+
}
38+
39+
fn defaultOptions: coqOptions = {
40+
implicit_libs: false,
41+
focus: false,
42+
editor: {
43+
mode: {companyCoq: true},
44+
keyMap: "default",
45+
},
46+
init_pkgs: ["init"],
47+
all_pkgs: ["coq", "mathcomp"],
48+
}
49+
50+
fn makeOptions = (
51+
~implicitLibs=false,
52+
~focus=false,
53+
~companyCoq=true,
54+
~keyMap="default",
55+
~initPkgs=["init"],
56+
~allPkgs=["coq", "mathcomp"],
57+
(),
58+
): coqOptions => {
59+
implicit_libs: implicitLibs,
60+
focus: focus,
61+
editor: {
62+
mode: {companyCoq: companyCoq},
63+
keyMap: keyMap,
64+
},
65+
init_pkgs: initPkgs,
66+
all_pkgs: allPkgs,
67+
}
68+

echidna-playground/src/Main.affine

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
11
// SPDX-License-Identifier: MPL-2.0
2-
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell
3-
// Ported via Harvard Engine bulk-processor
2+
// Ported via Harvard Engine (Semantic pass)
43

54
module Main;
65

7-
// TODO: Complete semantic implementation
6+
// SPDX-License-Identifier: MPL-2.0
7+
// SPDX-FileCopyrightText: 2025 Coq-Jr Contributors
8+
9+
// Main entry point for Coq-Jr
10+
11+
// Re-export modules
12+
module Dom = Dom
13+
module JsCoq = JsCoq
14+
module Components = Components
15+
module Page = Page
16+
17+
// Initialize the application when running in browser
18+
fn initialize = () => {
19+
Console.log("Coq-Jr initialized")
20+
Console.log("Generated page HTML available via Page.render()")
21+
}
22+
23+
// Export the page render function for use by Deno server
24+
fn getPageHtml = Page.render
25+

0 commit comments

Comments
 (0)