Skip to content

Commit 4d91050

Browse files
authored
Add ocaml-jupyter kernel to conformance tests (#42)
* Add ocaml-jupyter kernel to conformance tests - Add OCaml test snippets to Rust harness (print, expressions, etc.) - Add OCamlIcon to frontend with Catppuccin peach color - Add ocaml-jupyter to CI workflow using OPAM and setup-ocaml action * Fix ocaml-jupyter: copy kernelspec directly instead of using jupyter CLI
1 parent f63c8ea commit 4d91050

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

.github/workflows/conformance.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,15 @@ jobs:
103103
- name: xeus-octave
104104
install: micromamba install -y xeus-octave -c conda-forge
105105
kernel-name: xoctave
106+
- name: ocaml-jupyter
107+
install: |
108+
eval $(opam env)
109+
opam install -y jupyter
110+
echo '#use "topfind";;' >> ~/.ocamlinit
111+
ocaml-jupyter-opam-genspec
112+
mkdir -p ~/.local/share/jupyter/kernels/ocaml-jupyter
113+
cp -r "$(opam var share)/jupyter"/* ~/.local/share/jupyter/kernels/ocaml-jupyter/
114+
kernel-name: ocaml-jupyter
106115

107116
steps:
108117
- uses: actions/checkout@v4
@@ -152,6 +161,18 @@ jobs:
152161
python=3.12
153162
jupyter_client
154163
164+
- name: Set up OCaml
165+
if: matrix.kernel.name == 'ocaml-jupyter'
166+
uses: ocaml/setup-ocaml@v3
167+
with:
168+
ocaml-compiler: '5.2'
169+
170+
- name: Install OCaml dependencies
171+
if: matrix.kernel.name == 'ocaml-jupyter'
172+
run: |
173+
sudo apt-get update
174+
sudo apt-get install -y libzmq5-dev
175+
155176
- name: Install jupyter for Ark
156177
if: matrix.kernel.name == 'ark'
157178
run: python -m pip install jupyter

site/src/components/LanguageIcons.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,18 @@ export function OctaveIcon({ className = 'h-5 w-5' }: IconProps) {
125125
);
126126
}
127127

128+
export function OCamlIcon({ className = 'h-5 w-5' }: IconProps) {
129+
return (
130+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" className={className}>
131+
<g fill="none" stroke="#fab387" strokeLinecap="round" strokeLinejoin="round">
132+
<path d="M1.5 8V3c0-.83.67-1.5 1.5-1.5h10c.83 0 1.5.67 1.5 1.5v10c0 .83-.67 1.5-1.5 1.5H9" />
133+
<path d="m1.5 8 1.14-2.3q.09-.21.36-.24a.8.8 0 01.44.13c.18.12.23.53.28.64.06.1.64 1.23.85 1.23.2 0 .71-1.47.71-1.47s.37-.49.72-.49.55.32.67.49c.12.16.24 1.76.46 2.01s1.32.87 1.67.73c.34-.13.53-.4.63-.73.1-.34-.14-.75 0-1a1.1 1.1 0 011.02-.55c.56.03 2.05.56 2.05 1.05q0 .75-1.5.75c-.48 1.33.28 2.22-3 2.25l1 4" />
134+
<path d="m4.5 14.5 1.5-4 1 4zm-2 0 1.5-4-1.5-.5-1 1.54V14l1 .49Z" />
135+
</g>
136+
</svg>
137+
);
138+
}
139+
128140
// Map kernel names/languages to their icons
129141
export function getLanguageIcon(kernelName: string, language: string): React.ComponentType<IconProps> {
130142
const name = kernelName.toLowerCase();
@@ -142,6 +154,7 @@ export function getLanguageIcon(kernelName: string, language: string): React.Com
142154
if (name.includes('haskell') || lang === 'haskell') return HaskellIcon;
143155
if (name.includes('lua') || lang === 'lua') return LuaIcon;
144156
if (name.includes('octave') || lang === 'octave' || lang === 'matlab') return OctaveIcon;
157+
if (name.includes('ocaml') || lang === 'ocaml') return OCamlIcon;
145158

146159
// Default fallback
147160
return PythonIcon;

src/snippets.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ impl LanguageSnippets {
5555
"lua" => Self::lua(),
5656
"haskell" => Self::haskell(),
5757
"octave" => Self::octave(),
58+
"ocaml" => Self::ocaml(),
5859
_ => Self::generic(&lang),
5960
}
6061
}
@@ -336,6 +337,27 @@ xcpp::display(h);"#,
336337
}
337338
}
338339

340+
fn ocaml() -> Self {
341+
// OCaml Jupyter kernel - uses Jupyter_notebook module for rich output
342+
Self {
343+
language: "ocaml".to_string(),
344+
print_hello: r#"print_endline "hello""#,
345+
print_stderr: r#"prerr_endline "error""#,
346+
simple_expr: "1 + 1",
347+
simple_expr_result: "2",
348+
incomplete_code: "let foo (",
349+
complete_code: "let x = 1",
350+
syntax_error: "let let",
351+
input_prompt: "read_line ()",
352+
sleep_code: "Unix.sleep 2",
353+
completion_var: "test_variable_for_completion",
354+
completion_setup: "let test_variable_for_completion = 42",
355+
completion_prefix: "test_variable_for_",
356+
display_data_code: r#"#require "jupyter.notebook";; Jupyter_notebook.display "text/html" "<b>bold</b>""#,
357+
update_display_data_code: "(* OCaml jupyter doesn't support update_display_data *)",
358+
}
359+
}
360+
339361
/// Generic fallback for unknown languages
340362
fn generic(language: &str) -> Self {
341363
Self {

0 commit comments

Comments
 (0)