diff --git a/.gitignore b/.gitignore index f8abb00..bf49cee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ build/ +_build/ parser/ .cache/ vcpkg_installed/ __pycache__/ -test/.dev/ +.devbox/ diff --git a/bin/compiler/dune b/bin/compiler/dune new file mode 100644 index 0000000..fda3564 --- /dev/null +++ b/bin/compiler/dune @@ -0,0 +1,6 @@ +(executable + (name main) + (public_name main) + (package zane-compiler) + (libraries cst tree_graph) + (modules main)) diff --git a/bin/compiler/main.ml b/bin/compiler/main.ml new file mode 100644 index 0000000..86ec7c8 --- /dev/null +++ b/bin/compiler/main.ml @@ -0,0 +1,12 @@ +let read_file path = + In_channel.with_open_text path In_channel.input_all + +let () = + let input = read_file "test-parser/main.zn" in + match Cst.parse "test-parser/main.zn" input with + | Ok cst -> + let output = Cst.to_node cst in + Tree_graph.render output + | Error message -> + prerr_string message; + exit 1 diff --git a/bin/zane b/bin/zane deleted file mode 120000 index 7eb2d9e..0000000 --- a/bin/zane +++ /dev/null @@ -1 +0,0 @@ -../build/zane \ No newline at end of file diff --git a/devbox.json b/devbox.json index c3ac0c9..e10e822 100644 --- a/devbox.json +++ b/devbox.json @@ -10,14 +10,19 @@ "ninja@latest", "zig@latest", "clang-tools@latest", - "bison@latest", - "re2c@latest" + "opam@latest", + "ocaml@latest", + "gnumake@latest", + "pkg-config@latest", + "m4@latest", + "zstd@latest" ], "shell": { "init_hook": [ "echo 'Welcome to devbox!'", - "export PATH=$(realpath $DEVBOX_PROJECT_ROOT/bin):$PATH", - "export LLVM_CONFIG=$(realpath $DEVBOX_PROJECT_ROOT/.devbox/nix/profile/default/bin/llvm-config)" + "eval $(opam env)", + "export LLVM_CONFIG=$(realpath $DEVBOX_PROJECT_ROOT/.devbox/nix/profile/default/bin/llvm-config)", + "export LD_LIBRARY_PATH=/nix/store/s7vmxmhkq439cjb7ag9w198p6dk7kl0w-zstd-1.5.7/lib:$LD_LIBRARY_PATH" ], "scripts": { "test": [ diff --git a/devbox.lock b/devbox.lock index 09fc86e..1d60386 100644 --- a/devbox.lock +++ b/devbox.lock @@ -1,54 +1,6 @@ { "lockfile_version": "1", "packages": { - "bison@latest": { - "last_modified": "2026-04-23T13:07:47Z", - "resolved": "github:NixOS/nixpkgs/01fbdeef22b76df85ea168fbfe1bfd9e63681b30#bison", - "source": "devbox-search", - "version": "3.8.2", - "systems": { - "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/h3i7bj2a901ms72d6v92m5jdkk5mbv0w-bison-3.8.2", - "default": true - } - ], - "store_path": "/nix/store/h3i7bj2a901ms72d6v92m5jdkk5mbv0w-bison-3.8.2" - }, - "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/09i6bzy75cg042106zyf7w0f89ifm4g3-bison-3.8.2", - "default": true - } - ], - "store_path": "/nix/store/09i6bzy75cg042106zyf7w0f89ifm4g3-bison-3.8.2" - }, - "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/lvlg6r94p22mb9k0ws92p4fz4cjm3vx2-bison-3.8.2", - "default": true - } - ], - "store_path": "/nix/store/lvlg6r94p22mb9k0ws92p4fz4cjm3vx2-bison-3.8.2" - }, - "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/avysx19spq1m2kzs01nhhqh310zazk40-bison-3.8.2", - "default": true - } - ], - "store_path": "/nix/store/avysx19spq1m2kzs01nhhqh310zazk40-bison-3.8.2" - } - } - }, "clang-tools@latest": { "last_modified": "2026-04-23T13:07:47Z", "resolved": "github:NixOS/nixpkgs/01fbdeef22b76df85ea168fbfe1bfd9e63681b30#clang-tools", @@ -202,96 +154,204 @@ } }, "github:NixOS/nixpkgs/nixpkgs-unstable": { - "last_modified": "2026-04-30T11:26:30Z", - "resolved": "github:NixOS/nixpkgs/7aaa00e7cc9be6c316cb5f6617bd740dd435c59d?lastModified=1777548390&narHash=sha256-WacE23EbHTsBKvr8cu%2B1DFNbP6Rh1brHUH5SDUI0NQI%3D" + "last_modified": "2026-05-27T10:28:13Z", + "resolved": "github:NixOS/nixpkgs/4100e830e085863741bc69b156ec4ccd53ab5be0?lastModified=1779877693&narHash=sha256-NOF9NAREhxr50bbBfVcVOq%2BArCMSoe8dP79Pk2uyARk%3D" + }, + "gnumake@latest": { + "last_modified": "2026-05-23T11:35:32Z", + "resolved": "github:NixOS/nixpkgs/3d8f0f3f72a6cd4d93d0ad13203f2ea1cb7e1456#gnumake", + "source": "devbox-search", + "version": "4.4.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/8wwiw8pwyhrkzyq28hqzxfl4z84lks81-gnumake-4.4.1", + "default": true + }, + { + "name": "man", + "path": "/nix/store/wdm9prq35jlgwq6rjyhqxy4jzy833sfr-gnumake-4.4.1-man", + "default": true + }, + { + "name": "info", + "path": "/nix/store/6dbp1ysgxsrrn7xgjgf523faqrm605a5-gnumake-4.4.1-info" + }, + { + "name": "doc", + "path": "/nix/store/sl2ygvgjg4ml8dhyfxj5kh84x2bs1mhc-gnumake-4.4.1-doc" + } + ], + "store_path": "/nix/store/8wwiw8pwyhrkzyq28hqzxfl4z84lks81-gnumake-4.4.1" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/9ngw1ippk25jjj5fjxv36xbp6iq7rxdx-gnumake-4.4.1", + "default": true + }, + { + "name": "man", + "path": "/nix/store/dg3aa71vii59scbfhi02cdxnps64x6ql-gnumake-4.4.1-man", + "default": true + }, + { + "name": "debug", + "path": "/nix/store/mdv67w2xvchbs76zjdzppdkk64z5avki-gnumake-4.4.1-debug" + }, + { + "name": "doc", + "path": "/nix/store/bybbj9avk3g107mwhgvkzkpm6mkbl8gv-gnumake-4.4.1-doc" + }, + { + "name": "info", + "path": "/nix/store/bxdqxx2q13sj15ss8z5l33jd31x55j5l-gnumake-4.4.1-info" + } + ], + "store_path": "/nix/store/9ngw1ippk25jjj5fjxv36xbp6iq7rxdx-gnumake-4.4.1" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/60y2hs45l18g56ykw9789b5vhhi2ls5q-gnumake-4.4.1", + "default": true + }, + { + "name": "man", + "path": "/nix/store/m2ba4bwmfgh994xbg1iqm6zj1mdrq8h7-gnumake-4.4.1-man", + "default": true + }, + { + "name": "info", + "path": "/nix/store/apljbm6plyajbfrnwif0a2d4nx6w0cpf-gnumake-4.4.1-info" + }, + { + "name": "doc", + "path": "/nix/store/s9y438l347nky6rkl1wfk2y3b0d9i636-gnumake-4.4.1-doc" + } + ], + "store_path": "/nix/store/60y2hs45l18g56ykw9789b5vhhi2ls5q-gnumake-4.4.1" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/d3bwqm6bymhy3pdgbvf7vxjqfp31m3j1-gnumake-4.4.1", + "default": true + }, + { + "name": "man", + "path": "/nix/store/bmvqa5ym318mymhxlwwmxq8wxal958p4-gnumake-4.4.1-man", + "default": true + }, + { + "name": "debug", + "path": "/nix/store/q683z843wi7xs96pngh1kinnx88vn2if-gnumake-4.4.1-debug" + }, + { + "name": "doc", + "path": "/nix/store/wxg27bzsljjw4k72m9b2v6a0fx5a68s0-gnumake-4.4.1-doc" + }, + { + "name": "info", + "path": "/nix/store/03d41qgjsrhcr3fzq2z99ry07rahnj0v-gnumake-4.4.1-info" + } + ], + "store_path": "/nix/store/d3bwqm6bymhy3pdgbvf7vxjqfp31m3j1-gnumake-4.4.1" + } + } }, "just@latest": { - "last_modified": "2026-04-23T13:07:47Z", - "resolved": "github:NixOS/nixpkgs/01fbdeef22b76df85ea168fbfe1bfd9e63681b30#just", + "last_modified": "2026-05-12T01:56:55Z", + "resolved": "github:NixOS/nixpkgs/0a0bf409f3593f415d0a554f33acc63dc7dccb43#just", "source": "devbox-search", - "version": "1.50.0", + "version": "1.51.0", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/qxqsxk2614qpcb7l32svpjmmb1sm7ya1-just-1.50.0", + "path": "/nix/store/c9vv24f9bdnhnms10nwijn6q8wjz00ak-just-1.51.0", "default": true }, { "name": "man", - "path": "/nix/store/daf30m3si50cl7xga42g0cqgdwaxj6ys-just-1.50.0-man", + "path": "/nix/store/lxfs1naxd1a3934y9xsd1kid5yyw7jbn-just-1.51.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/9n71gassrpdy0nz4gfcrwvmkb4rwyd3g-just-1.50.0-doc" + "path": "/nix/store/c8jdzd2s7hbldm1q1glrjisavxy1vf54-just-1.51.0-doc" } ], - "store_path": "/nix/store/qxqsxk2614qpcb7l32svpjmmb1sm7ya1-just-1.50.0" + "store_path": "/nix/store/c9vv24f9bdnhnms10nwijn6q8wjz00ak-just-1.51.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ayqw7hgkjs9b0f7v1qh96k316i9jpdw8-just-1.50.0", + "path": "/nix/store/ljwadwqsib4zd6nvg6xi5rnzah9ad2q4-just-1.51.0", "default": true }, { "name": "man", - "path": "/nix/store/59z0bx178i7c3vwmhawgajgpfmv43h5c-just-1.50.0-man", + "path": "/nix/store/5m9rgkzibz19za2nmn83y9l5gsy2z1yh-just-1.51.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/gg298zgvg5vmvzk0lmn9zq7j6kkab3qs-just-1.50.0-doc" + "path": "/nix/store/q80cv2gjn2q640fvsqgdj6ap2iihxwg9-just-1.51.0-doc" } ], - "store_path": "/nix/store/ayqw7hgkjs9b0f7v1qh96k316i9jpdw8-just-1.50.0" + "store_path": "/nix/store/ljwadwqsib4zd6nvg6xi5rnzah9ad2q4-just-1.51.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/bmsgb4l7ldxj2zggcalfidzhkrgp03xq-just-1.50.0", + "path": "/nix/store/w0capgph40qykn19a9ghv38fjf06gg97-just-1.51.0", "default": true }, { "name": "man", - "path": "/nix/store/0pvkn1lzh85sav0cj3v36kpigxwypdhb-just-1.50.0-man", + "path": "/nix/store/97jyxj8d9d4qjq25681ng88jnbdak1dx-just-1.51.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/9d2i12jkmsyqw7bkcw3a9d0xki313xrp-just-1.50.0-doc" + "path": "/nix/store/p8pb01d5w82lf8z0ah4kcc6ziwfvxfkr-just-1.51.0-doc" } ], - "store_path": "/nix/store/bmsgb4l7ldxj2zggcalfidzhkrgp03xq-just-1.50.0" + "store_path": "/nix/store/w0capgph40qykn19a9ghv38fjf06gg97-just-1.51.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/vr4agmy8jw7f8kqynpizagdaqxy0ayw4-just-1.50.0", + "path": "/nix/store/zn7n9xnw5vx6cqf1az8b8p1pfsmrl9vw-just-1.51.0", "default": true }, { "name": "man", - "path": "/nix/store/6mrjsdff8skgnnn0ngyim2i7wabk0c8x-just-1.50.0-man", + "path": "/nix/store/mgc1wdhgkc9246s9a52j17ymw4745bal-just-1.51.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/nk0p7b0n1fqg0sa2vasgavrhgi7xy9rz-just-1.50.0-doc" + "path": "/nix/store/mkdym7bscwpn6y6dvzrblm5p87q9c2g0-just-1.51.0-doc" } ], - "store_path": "/nix/store/vr4agmy8jw7f8kqynpizagdaqxy0ayw4-just-1.50.0" + "store_path": "/nix/store/zn7n9xnw5vx6cqf1az8b8p1pfsmrl9vw-just-1.51.0" } } }, "llvm_21@latest": { - "last_modified": "2026-04-29T01:19:07Z", - "resolved": "github:NixOS/nixpkgs/ebc08544afa77957cc348ba72dc490ec73b87f68#llvm_21", + "last_modified": "2026-05-20T06:38:13Z", + "resolved": "github:NixOS/nixpkgs/d99b013d5d1931ad77fe3912ed218170dec5d9a4#llvm_21", "source": "devbox-search", "version": "21.1.8", "systems": { @@ -302,10 +362,6 @@ "path": "/nix/store/fib2wxwpvlc1q2qrxbhz2i961l2srcjw-llvm-21.1.8", "default": true }, - { - "name": "python", - "path": "/nix/store/6avba57frkpxqlw7d5mljr5a6ymvlygy-llvm-21.1.8-python" - }, { "name": "dev", "path": "/nix/store/x0msv7ql9xm50fvz3x5p2gb01grnyqln-llvm-21.1.8-dev" @@ -313,6 +369,10 @@ { "name": "lib", "path": "/nix/store/4fk7xkk9faias8kqzh1kagdhb87l5kcw-llvm-21.1.8-lib" + }, + { + "name": "python", + "path": "/nix/store/6avba57frkpxqlw7d5mljr5a6ymvlygy-llvm-21.1.8-python" } ], "store_path": "/nix/store/fib2wxwpvlc1q2qrxbhz2i961l2srcjw-llvm-21.1.8" @@ -324,6 +384,10 @@ "path": "/nix/store/dr58nil7xb6v4di6mmlvki3s1d8iwdym-llvm-21.1.8", "default": true }, + { + "name": "dev", + "path": "/nix/store/0hf3wfrd60fhgh7y1x7857pf7a1hypnm-llvm-21.1.8-dev" + }, { "name": "lib", "path": "/nix/store/lr9kz2ky0ys12cx0xz91rnkmbadaqvj0-llvm-21.1.8-lib" @@ -331,16 +395,26 @@ { "name": "python", "path": "/nix/store/45y119pkm1p76bqn7lhp2i0nx0yk2x44-llvm-21.1.8-python" - }, - { - "name": "dev", - "path": "/nix/store/0hf3wfrd60fhgh7y1x7857pf7a1hypnm-llvm-21.1.8-dev" } ], "store_path": "/nix/store/dr58nil7xb6v4di6mmlvki3s1d8iwdym-llvm-21.1.8" + }, + "x86_64-linux": { + "outputs": [ + { + "path": "/nix/store/jp45dqzv8mjpqsvhj99c93pc4vlmhy16-llvm-21.1.8", + "default": true + } + ] } } }, + "m4@latest": { + "last_modified": "2023-02-24T09:01:09Z", + "resolved": "github:NixOS/nixpkgs/7d0ed7f2e5aea07ab22ccb338d27fbe347ed2f11#m4", + "source": "devbox-search", + "version": "1.4.19" + }, "meson@latest": { "last_modified": "2026-04-23T13:07:47Z", "resolved": "github:NixOS/nixpkgs/01fbdeef22b76df85ea168fbfe1bfd9e63681b30#meson", @@ -453,51 +527,199 @@ } } }, - "re2c@latest": { - "last_modified": "2026-04-23T13:07:47Z", - "resolved": "github:NixOS/nixpkgs/01fbdeef22b76df85ea168fbfe1bfd9e63681b30#re2c", + "ocaml@latest": { + "last_modified": "2026-05-21T08:15:18Z", + "resolved": "github:NixOS/nixpkgs/4a29d733e8a7d5b824c3d8c958a946a9867b3eb2#ocaml", + "source": "devbox-search", + "version": "5.4.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/81yy39njdh1d5796qn84inbqq56fh71v-ocaml-5.4.1", + "default": true + } + ], + "store_path": "/nix/store/81yy39njdh1d5796qn84inbqq56fh71v-ocaml-5.4.1" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/zrdg31hnnr0lv3xndwvh74fd160483di-ocaml-5.4.1", + "default": true + } + ], + "store_path": "/nix/store/zrdg31hnnr0lv3xndwvh74fd160483di-ocaml-5.4.1" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2hrwc25yrvlwz6gmar9cd8zdsy89rfpa-ocaml-5.4.1", + "default": true + } + ], + "store_path": "/nix/store/2hrwc25yrvlwz6gmar9cd8zdsy89rfpa-ocaml-5.4.1" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/ryj2xb3cla5jvx41pxlk8vqm0z93g5cs-ocaml-5.4.1", + "default": true + } + ], + "store_path": "/nix/store/ryj2xb3cla5jvx41pxlk8vqm0z93g5cs-ocaml-5.4.1" + } + } + }, + "opam@latest": { + "last_modified": "2026-05-21T08:15:18Z", + "resolved": "github:NixOS/nixpkgs/4a29d733e8a7d5b824c3d8c958a946a9867b3eb2#opam", "source": "devbox-search", - "version": "4.5.1", + "version": "2.5.1", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/qbdjjl5qqrhls3ld8x8dlmsz8ddkazib-re2c-4.5.1", + "path": "/nix/store/6cji9in2mij7z9545lyx035vshsi9cag-opam-2.5.1", "default": true + }, + { + "name": "installer", + "path": "/nix/store/c7gjr0dmq98scqkd5i8gpbjhlphb1gk4-opam-2.5.1-installer" } ], - "store_path": "/nix/store/qbdjjl5qqrhls3ld8x8dlmsz8ddkazib-re2c-4.5.1" + "store_path": "/nix/store/6cji9in2mij7z9545lyx035vshsi9cag-opam-2.5.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/b8dp3yrgky5ira1yqwjda0xa5ykf11qh-re2c-4.5.1", + "path": "/nix/store/ci7lqvh196kbhc8lxgd04gz7pz0mm2yq-opam-2.5.1", "default": true + }, + { + "name": "installer", + "path": "/nix/store/1v9bx0wdr2nkfwlwddldi7gscls7qcjm-opam-2.5.1-installer" } ], - "store_path": "/nix/store/b8dp3yrgky5ira1yqwjda0xa5ykf11qh-re2c-4.5.1" + "store_path": "/nix/store/ci7lqvh196kbhc8lxgd04gz7pz0mm2yq-opam-2.5.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/3n0qpxbjhw2yziynjjg5pg0zbm2w5ka0-re2c-4.5.1", + "path": "/nix/store/3k4k7yyla8cxj8r8l688gmwd81346wsz-opam-2.5.1", "default": true + }, + { + "name": "installer", + "path": "/nix/store/2m62nyma8h6f37vcr235f9xa560jyii0-opam-2.5.1-installer" } ], - "store_path": "/nix/store/3n0qpxbjhw2yziynjjg5pg0zbm2w5ka0-re2c-4.5.1" + "store_path": "/nix/store/3k4k7yyla8cxj8r8l688gmwd81346wsz-opam-2.5.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/zkmywqm90m1wlmjs9x1ibr7hqlvy0rxk-re2c-4.5.1", + "path": "/nix/store/nd0n31sr87n8n6j34ybr30v9h1z8l34l-opam-2.5.1", "default": true + }, + { + "name": "installer", + "path": "/nix/store/xhbyql390bnwyg1if8kssdylp6y3bni9-opam-2.5.1-installer" } ], - "store_path": "/nix/store/zkmywqm90m1wlmjs9x1ibr7hqlvy0rxk-re2c-4.5.1" + "store_path": "/nix/store/nd0n31sr87n8n6j34ybr30v9h1z8l34l-opam-2.5.1" + } + } + }, + "pkg-config@latest": { + "last_modified": "2025-11-23T21:50:36Z", + "resolved": "github:NixOS/nixpkgs/ee09932cedcef15aaf476f9343d1dea2cb77e261#pkg-config", + "source": "devbox-search", + "version": "0.29.2", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hygaaqwk9ylklp0ybwppqhw75nz8ya41-pkg-config-wrapper-0.29.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/9px0sji43x3r2w4zxl3j3idwsql7lwxx-pkg-config-wrapper-0.29.2-man", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/hqk44ra6qxw7iixardl6c3hdgb9kq6ns-pkg-config-wrapper-0.29.2-doc" + } + ], + "store_path": "/nix/store/hygaaqwk9ylklp0ybwppqhw75nz8ya41-pkg-config-wrapper-0.29.2" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/lbiigi8qbp7mzf1lpr7p982l1kyf01ql-pkg-config-wrapper-0.29.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/10060k24qggqyzlwdsfmni9y32zxcg0j-pkg-config-wrapper-0.29.2-man", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/0y4v51ndpyvkj09hwlfqkz0c3h17zfmc-pkg-config-wrapper-0.29.2-doc" + } + ], + "store_path": "/nix/store/lbiigi8qbp7mzf1lpr7p982l1kyf01ql-pkg-config-wrapper-0.29.2" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vknadizq0q5kffvx6y4379p9gdry9zq3-pkg-config-wrapper-0.29.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/1nyspra675q22gfhf7hn2nmfpi6rgim5-pkg-config-wrapper-0.29.2-man", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/7lq1axxwrafwljs06n88bzyz9w523rkc-pkg-config-wrapper-0.29.2-doc" + } + ], + "store_path": "/nix/store/vknadizq0q5kffvx6y4379p9gdry9zq3-pkg-config-wrapper-0.29.2" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/8vdiwpbh0g4avsd6x5v4s0di32vcl3dp-pkg-config-wrapper-0.29.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/j9xfpnrygg3v37svc5pfin9q5bm49r94-pkg-config-wrapper-0.29.2-man", + "default": true + }, + { + "name": "doc", + "path": "/nix/store/x3bypxdxaq20kykybhkf21x4jczsiy8y-pkg-config-wrapper-0.29.2-doc" + } + ], + "store_path": "/nix/store/8vdiwpbh0g4avsd6x5v4s0di32vcl3dp-pkg-config-wrapper-0.29.2" } } }, @@ -612,6 +834,106 @@ "store_path": "/nix/store/5x5y75rc3l43ic0lwal6wzk7wi1ga3m1-zig-0.16.0" } } + }, + "zstd@latest": { + "last_modified": "2026-05-27T10:28:13Z", + "resolved": "github:NixOS/nixpkgs/4100e830e085863741bc69b156ec4ccd53ab5be0#zstd", + "source": "devbox-search", + "version": "1.5.7", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/lj773k1dkd7rrnrf0f05v43kwwbzlv4j-zstd-1.5.7-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/053fzmw9v3cc6j8j32jyp3lk1017mla5-zstd-1.5.7-man", + "default": true + }, + { + "name": "out", + "path": "/nix/store/ym2h5axmyxqkwmrrrz570gmkbmdlwvbv-zstd-1.5.7" + }, + { + "name": "dev", + "path": "/nix/store/jg2ki5fvwby45jdv5qmwmn6808gfvvks-zstd-1.5.7-dev" + } + ], + "store_path": "/nix/store/lj773k1dkd7rrnrf0f05v43kwwbzlv4j-zstd-1.5.7-bin" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/37j8jm7q19xgccgz4lawfg7hr0vf79zl-zstd-1.5.7-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/h47ihn6ksn6378j64ya87sci4lxq66mc-zstd-1.5.7-man", + "default": true + }, + { + "name": "out", + "path": "/nix/store/g3vxv9qmkd89xw1ya9ii9gpps78z4q2m-zstd-1.5.7" + }, + { + "name": "dev", + "path": "/nix/store/55aldi2c0mzgqkya8i9ssn76shz0vqp8-zstd-1.5.7-dev" + } + ], + "store_path": "/nix/store/37j8jm7q19xgccgz4lawfg7hr0vf79zl-zstd-1.5.7-bin" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/fb8skywpvnjdmwz37azp9r4dvwxwvq76-zstd-1.5.7-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/wv4by27faql80s9q8y6xkqcx3ssalvr7-zstd-1.5.7-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/g5r73l3qx4pjz4f3g0bx1nq6cihqa629-zstd-1.5.7-dev" + }, + { + "name": "out", + "path": "/nix/store/9db35k8dl5rxk3kk6kzkqwm5732s6ag6-zstd-1.5.7" + } + ], + "store_path": "/nix/store/fb8skywpvnjdmwz37azp9r4dvwxwvq76-zstd-1.5.7-bin" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/cwj48a5jfy5srxgq45p4dg37zqdlhzba-zstd-1.5.7-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/qb1yjn4gp5g2pqgqy0ywjwv3ms987n5a-zstd-1.5.7-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/cbzpsvdi282yhw702d7s42lirqnr0i0g-zstd-1.5.7-dev" + }, + { + "name": "out", + "path": "/nix/store/gd74vdpqzq0qmmikvmrf3r3kghhhnf50-zstd-1.5.7" + } + ], + "store_path": "/nix/store/cwj48a5jfy5srxgq45p4dg37zqdlhzba-zstd-1.5.7-bin" + } + } } } } diff --git a/docs/bison.md b/docs/bison.md new file mode 100644 index 0000000..3aefd35 --- /dev/null +++ b/docs/bison.md @@ -0,0 +1,57 @@ +for recursion dont need to specify the single node variant when empty a variant. +also: empty doesnt need explicit instantiation. + +``` +parameters + : %empty + | parameters[params] COMMA parameter[p] + { + $params.push_back(std::move($p)); + $$ = std::move($params); + } + ; +``` +only use single node variant if we dont allow empty. + +actually, this is incorrect, as only allowing empty nodes mess with commas, which would only allow: +``` +Void main(, test Int) { + print(2, 2) + Std$print(1 + 1 + 3 + 4) + @Intrinsics$print("hello", "bye") +} +``` +instead of: +``` +Void main(test Int) { + print(2, 2) + Std$print(1 + 1 + 3 + 4) + @Intrinsics$print("hello", "bye") +} +``` + +so we use: +``` +parameters + : %empty + | parameters[params] COMMA parameter[p] + { + $params.push_back(std::move($p)); + $$ = std::move($params); + } + ; +``` +but possible in homologous: +``` +statements + : %empty + { $$ = std::vector>(); } + | statements[list] statement[stmt] + { + $list.push_back(std::make_unique(std::move($stmt))); + $$ = std::move($list); + } + ; +``` + +no ambiguaty must exist that would be resolved through following sub-nodes, because sub-nodes are reduced before matching. otherwise we would use glr, but it is slower and lalr(1) is linear. diff --git a/docs/stages.md b/docs/stages.md new file mode 100644 index 0000000..0da2378 --- /dev/null +++ b/docs/stages.md @@ -0,0 +1,8 @@ +We use 4 stages: +1. parsing: astA (correct terminology: cst, concrete syntax tree) +2. semantics: astB (correct terminology: ast) +3. optimizations: mutate astB +4. codegen: binary + +we use two different ast's. astA only captures the content and doesnt do name resolution or desugaring. +those things are handled in stage 2 semantics. diff --git a/dune-project b/dune-project new file mode 100644 index 0000000..44ca369 --- /dev/null +++ b/dune-project @@ -0,0 +1,18 @@ +(lang dune 3.21) +(name zane-compiler) +(generate_opam_files true) + +(source + (github zane-lang/compiler)) + +(using menhir 3.0) + +(package + (name zane-compiler) + (synopsis "A short synopsis") + (description "A longer description") + (depends ocaml) + (tags + ("add topics" "to describe" your project))) + +; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html diff --git a/justfile b/justfile index 8b218f0..de805ef 100644 --- a/justfile +++ b/justfile @@ -1,26 +1,20 @@ -build: - meson compile -C build +grammarFile := "lib/cst/parser.mly" -init: - rm -rf build - test -d .git/modules || git submodule update --init --recursive - vcpkg install - CXX=clang++ meson setup build --buildtype=debug --cmake-prefix-path "$(realpath vcpkg_installed/x64-linux)" +default: + just -l -release: - rm -rf build - test -d .git/modules || git submodule update --init --recursive - vcpkg install - CXX=clang++ meson setup build --buildtype=release --cmake-prefix-path "$(realpath vcpkg_installed/x64-linux)" +run: + dune exec bin/compiler/main.exe -check: - clang-check -p build src/*.* +rebuild: + dune clean + dune build -generate-parser: - scripts/generate_parser.sh +watch: + dune build --watch -check-parser: - scripts/check_parser.sh +stats: + menhir {{ grammarFile }} --no-code-generation --infer -link: - ln -sf build/zane bin/zane +conflicts: + menhir {{ grammarFile }} --random-sentence-concrete package --random-seed 1 --random-sentence-length 16 diff --git a/lib/cst/cst.ml b/lib/cst/cst.ml new file mode 100644 index 0000000..ede9117 --- /dev/null +++ b/lib/cst/cst.ml @@ -0,0 +1,13 @@ +module Nodes = Nodes +module Parser = Parser +module Lexer = Lexer +include To_tree_graph + +let parse filename input = + let lexbuf = Sedlexing.Utf8.from_string input in + let tokenizer = Sedlexing.with_tokenizer Lexer.token lexbuf in + try + Ok (MenhirLib.Convert.Simplified.traditional2revised Parser.package tokenizer) + with Parser.Error _tokenizer -> + let (pos_start, pos_end) = Sedlexing.lexing_positions lexbuf in + Error (Parse_error.format_parse_error filename input pos_start pos_end) diff --git a/lib/cst/dune b/lib/cst/dune new file mode 100644 index 0000000..5223e60 --- /dev/null +++ b/lib/cst/dune @@ -0,0 +1,16 @@ +(library + (name cst) + (libraries + menhirLib + sedlex + tree_graph + menhirGLR + ) + (preprocess (pps sedlex.ppx))) + +(menhir + (modules parser) + (infer true) + + (flags --GLR) +) diff --git a/lib/cst/lexer.ml b/lib/cst/lexer.ml new file mode 100644 index 0000000..d4e452f --- /dev/null +++ b/lib/cst/lexer.ml @@ -0,0 +1,65 @@ +open Sedlexing +open Parser + +let digit = [%sedlex.regexp? '0'..'9'] +let digits = [%sedlex.regexp? Plus digit] +let int_lit = [%sedlex.regexp? digits, Star ('\'', digits)] +let float_lit = [%sedlex.regexp? int_lit, '.', digits] +let str_char = [%sedlex.regexp? Compl ('"' | '\\') | '\\', any] + +(* Any character valid inside an identifier (after the first) *) +let ident_char = [%sedlex.regexp? alphabetic | '0'..'9' | '_'] + +(* Starts with a Unicode lowercase letter, or '_' then one *) +let lower_ident = [%sedlex.regexp? (lowercase | '_', lowercase), Star ident_char] +(* Starts with a Unicode uppercase letter, or '_' then one *) +let upper_ident = [%sedlex.regexp? (uppercase | '_', uppercase), Star ident_char] + +let rec token buf = + match%sedlex buf with + | Plus (' ' | '\t' | '\r' | '\n') -> token buf + | "->" -> THIN_ARROW + | "=>" -> THICK_ARROW + | "==" -> EQEQ + | "<=" -> LESSEQ + | ">=" -> MOREEQ + | '<' -> LESS + | '>' -> MORE + | '=' -> EQUAL + | '(' -> LPAREN + | ')' -> RPAREN + | '{' -> LCURLY + | '}' -> RCURLY + | '[' -> LBRACKET + | ']' -> RBRACKET + | ',' -> COMMA + | ':' -> COLON + | '!' -> EXCL + | "??" -> QSTNQSTN + | '?' -> QSTNMARK + | '~' -> TILDE + | '+' -> PLUS + | '-' -> MINUS + | '*' -> STAR + | '/' -> SLASH + | '$' -> DOLLAR + | '@' -> AT + | float_lit -> FLOAT (Utf8.lexeme buf) + | int_lit -> INT (Utf8.lexeme buf) + | '"', Star str_char, '"' -> + let s = Utf8.lexeme buf in + STRING (String.sub s 1 (String.length s - 2)) + | "if" -> IF + | "elif" -> ELIF + | "else" -> ELSE + (* | "loop" -> LOOP *) + | "true" -> TRUE + | "false" -> FALSE + | "this" -> THIS + | "abort" -> ABORT + | "return" -> RETURN + | "resolve" -> RESOLVE + | lower_ident -> LIDENT (Utf8.lexeme buf) + | upper_ident -> UIDENT (Utf8.lexeme buf) + | eof -> EOF + | _ -> ERROR diff --git a/lib/cst/nodes.ml b/lib/cst/nodes.ml new file mode 100644 index 0000000..9f08fef --- /dev/null +++ b/lib/cst/nodes.ml @@ -0,0 +1,116 @@ +(* The CST's job is to represent what was parsed, not what's valid. *) + +type operator = + | Add + | Sub + | Mul + | Div + | Eq + | LessEq + | MoreEq + | Less + | More + +type expr = + | IntLit of string + | FloatLit of string + | StrLit of string + | BoolLit of bool + | Ident of string + | QualifiedIdent of string * string + | Op of { left: expr; right: expr; operator: operator } + | Flip of expr + | Parenthized of expr + | FuncCall of func_call + | FuncLambda of func_lambda + | MethLambda of meth_lambda + +and safe_call = { + callee: expr; + args: expr list; +} + +and abort_handle = + | AbortBody of body + | AbortShorthand of expr + +and abort_call = { + callee: expr; + args: expr list; + binder: string option; + handle_block: abort_handle; +} + +and func_call = + | SafeCall of safe_call + | AbortCall of abort_call + +and type_expr = + | SimpleType of string + | QualifiedType of string * string + | FuncType of { + params: type_expr list; + ret_type: type_expr + } + | MethType of { + this_type: type_expr; + params: type_expr list; + ret_type: type_expr; + is_mut: bool + } + +and param = { + name: string; + type_: type_expr +} + +and cond_block = { + cond: expr; + block: stat list +} + +and cond_seq = { + if_: cond_block; + elifs_: cond_block list; + else_: stat list option; +} + +and stat = + | FuncCallStat of func_call + | DeclStat of decl + | AbortStat of expr + | RetStat of expr + | ResolveStat of expr + | CondSeq of cond_seq + +and body = + | Scope of stat list + | RetShorthand of expr + +and ret_type = + | SafeRet of type_expr + | AbortRet of type_expr * type_expr + +and func_lambda = { + params: param list; + ret_type: ret_type; + body: body +} + +and meth_lambda = { + this_type: type_expr; + params: param list; + ret_type: ret_type; + is_mut: bool; + body: body +} + +and decl = + | FuncDecl of { name: string; func: func_lambda } + | MethDecl of { name: string; func: meth_lambda } + | VarDecl of { name: string; type_: type_expr; value: expr } + | ConstructorDecl of { name: string; type_: type_expr; args: expr list } + +type package = { + decls: decl list +} diff --git a/lib/cst/parse_error.ml b/lib/cst/parse_error.ml new file mode 100644 index 0000000..a140dfd --- /dev/null +++ b/lib/cst/parse_error.ml @@ -0,0 +1,69 @@ +let tab_width = 4 + +let visual_col_of_idx s idx = + let rec aux i vcol = + if i >= idx then vcol + else + let c = s.[i] in + let step = if c = '\t' then tab_width - (vcol mod tab_width) else 1 in + aux (i + 1) (vcol + step) + in + aux 0 0 + +let expand_tabs s = + let buf = Buffer.create (String.length s + 10) in + let rec aux i vcol = + if i >= String.length s then Buffer.contents buf + else + let c = s.[i] in + if c = '\t' then + let spaces = tab_width - (vcol mod tab_width) in + let () = Buffer.add_string buf (String.make spaces ' ') in + aux (i + 1) (vcol + spaces) + else + let () = Buffer.add_char buf c in + aux (i + 1) (vcol + 1) + in + aux 0 0 + +let format_parse_error filename input pos_start pos_end = + let line = pos_start.Lexing.pos_lnum in + let char_start = pos_start.Lexing.pos_cnum - pos_start.Lexing.pos_bol in + let lines = String.split_on_char '\n' input in + let source_line = + match List.nth_opt lines (line - 1) with + | Some l -> + let len = String.length l in + if len > 0 && l.[len - 1] = '\r' then String.sub l 0 (len - 1) else l + | None -> "" + in + let line_len = String.length source_line in + (* The error span may cross newlines. We only display the start line, so the + caret must stop at that line's end rather than following pos_end into + later lines. *) + let same_line = pos_end.Lexing.pos_lnum = line in + let char_end = + if same_line then + min line_len (pos_end.Lexing.pos_cnum - pos_start.Lexing.pos_bol) + else + line_len + in + let char_start = min char_start line_len in + let char_end = max char_start char_end in + + let vcol_start = visual_col_of_idx source_line char_start in + let vcol_end = visual_col_of_idx source_line char_end in + let visual_source_line = expand_tabs source_line in + + let buf = Buffer.create 256 in + Buffer.add_string buf + (Printf.sprintf "File \"%s\", line %d, characters %d-%d:\n" + filename line (vcol_start + 1) (vcol_end + 1)); + Buffer.add_string buf + (Printf.sprintf "%d | %s\n" line visual_source_line); + Buffer.add_string buf + (Printf.sprintf " | %s%s\n" + (String.make vcol_start ' ') + (String.make (max 1 (vcol_end - vcol_start)) '^')); + Buffer.add_string buf "Error: Parse error\n"; + Buffer.contents buf diff --git a/lib/cst/parser.mly b/lib/cst/parser.mly new file mode 100644 index 0000000..3c1cdc2 --- /dev/null +++ b/lib/cst/parser.mly @@ -0,0 +1,202 @@ +(*****************************) +(* token definitions *) +(*****************************) +%token INT "43" +%token FLOAT "8.647" +%token STRING "\"john\"" +%token LIDENT "length" +%token UIDENT "Int" + +%token LPAREN "(" +%token RPAREN ")" +%token COMMA "," +%token LCURLY "{" +%token RCURLY "}" +%token LBRACKET "[" +%token RBRACKET "]" +%token COLON +%token EQUAL "=" +%token PLUS "+" +%token MINUS "-" +%token STAR "*" +%token SLASH "/" +%token DOLLAR "$" +%token AT "@" +%token EXCL "!" +%token QSTNMARK "?" +%token QSTNQSTN "??" +%token TILDE "~" +%token THIN_ARROW "->" +%token THICK_ARROW "=>" +%token EQEQ "==" +%token LESSEQ "<=" +%token MOREEQ ">=" +%token LESS "<" +%token MORE ">" +%token IF "if" +%token ELIF "elif" +%token ELSE "else" +%token TRUE "true" +%token FALSE "false" +%token THIS "this" +%token ABORT "abort" +%token RETURN "return" +%token RESOLVE "resolve" +%token ERROR "" +%token EOF "" + +%nonassoc EQEQ LESSEQ MOREEQ LESS MORE /* comparisons */ +%left PLUS MINUS +%left STAR SLASH +%left LPAREN /* function application */ +%nonassoc IF +%nonassoc ELSE +%nonassoc TILDE /* prefix ~ */ + +%start package + +(*************************) +(* grammar rules *) +(*************************) +%% + +package: + | decls=list(decl) EOF { { Nodes.decls=decls } } + +%inline func_lambda: + | "(" params=separated_list(COMMA, param) ")" + ret_type=ret_type body=body { + { Nodes.params; ret_type; body } + } + +%inline meth_lambda: + | "(" THIS this_type=type_expr + params=loption(preceded(",", separated_nonempty_list(",", param))) + ")" ret_type=ret_type body=body { + { Nodes.this_type; params; ret_type; is_mut=false; body } + } + | "(" THIS this_type=type_expr + params=loption(preceded(",", separated_nonempty_list(",", param))) + ")" "!" ret_type=ret_type body=body { + { Nodes.this_type; params; ret_type; is_mut=true; body } + } + +decl: + | name=LIDENT type_=type_expr "=" value=expr { + Nodes.VarDecl { name; type_; value } + } + | name=LIDENT type_=type_expr "(" args=separated_list(COMMA, expr) ")" { + Nodes.ConstructorDecl { name; type_; args } + } + | name=LIDENT func_lambda=func_lambda { + Nodes.FuncDecl { name; func=func_lambda } + } + | name=LIDENT meth_lambda=meth_lambda { + Nodes.MethDecl { name; func=meth_lambda } + } + +%inline ret_type: + | ret_type=type_expr { + Nodes.SafeRet ret_type + } + | safe_type=type_expr "?" abort_type=type_expr { + Nodes.AbortRet (safe_type, abort_type) + } + +body: + | "{" statements=list(stat) "}" { + Nodes.Scope statements + } + | "=>" value=expr { + Nodes.RetShorthand value + } + +func_call: + | callee=expr "(" args=separated_list(COMMA, expr) ")" %prec LPAREN + { Nodes.SafeCall { callee; args } } + | callee=expr "(" args=separated_list(COMMA, expr) ")" "?" binder=ioption(LIDENT) body=body %prec LPAREN + { Nodes.AbortCall { callee; args; binder; handle_block=Nodes.AbortBody body } } + | callee=expr "(" args=separated_list(COMMA, expr) ")" "??" binder=ioption(LIDENT) value=expr %prec LPAREN + { Nodes.AbortCall { callee; args; binder; handle_block=Nodes.AbortShorthand value } } + +%inline if_: + | IF cond=expr "{" block=list(stat) "}" { + { Nodes.cond; block } + } + +%inline elif_: + | ELIF cond=expr "{" block=list(stat) "}" { + { Nodes.cond; block } + } + +%inline else_: + | ELSE "{" statements=list(stat) "}" { + statements + } + +stat: + | decl=decl { Nodes.DeclStat decl } + | func_call=func_call { + Nodes.FuncCallStat func_call + } + | ABORT value=expr { + Nodes.AbortStat value + } + | RETURN value=expr { + Nodes.RetStat value + } + | RESOLVE value=expr { + Nodes.ResolveStat value + } + | if_=if_ elifs_=list(elif_) else_=ioption(else_) { + Nodes.CondSeq { if_; elifs_; else_ } + } + +%inline param: + | name=LIDENT type_=type_expr { { Nodes.name; type_ } } + +type_expr: + | name=UIDENT { Nodes.SimpleType name } + | pkg=UIDENT name=UIDENT { Nodes.QualifiedType (pkg, name) } + | "[" params=separated_list(",", type_expr) "]" "->" ret=type_expr { + Nodes.FuncType { params; ret_type=ret } + } + | "[" THIS this_type=type_expr + params=loption(preceded(",", separated_nonempty_list(",", type_expr))) + "]" "->" ret=type_expr { + Nodes.MethType { this_type; params; ret_type=ret; is_mut=false } + } + | "[" THIS this_type=type_expr + params=loption(preceded(",", separated_nonempty_list(",", type_expr))) + "]" "!" "->" ret=type_expr { + Nodes.MethType { this_type; params; ret_type=ret; is_mut=true } + } + +expr: + | int=INT { Nodes.IntLit int } + | float=FLOAT { Nodes.FloatLit float } + | string=STRING { Nodes.StrLit string } + | TRUE { Nodes.BoolLit true } + | FALSE { Nodes.BoolLit false } + | ident=LIDENT { Nodes.Ident ident } + | pkg=UIDENT "$" ident=LIDENT { Nodes.QualifiedIdent (pkg, ident) } + | e1=expr "+" e2=expr { Nodes.Op { left=e1; right=e2; operator=Nodes.Add } } + | e1=expr "-" e2=expr { Nodes.Op { left=e1; right=e2; operator=Nodes.Sub } } + | e1=expr "*" e2=expr { Nodes.Op { left=e1; right=e2; operator=Nodes.Mul } } + | e1=expr "/" e2=expr { Nodes.Op { left=e1; right=e2; operator=Nodes.Div } } + | e1=expr "==" e2=expr { Nodes.Op { left=e1; right=e2; operator=Nodes.Eq } } + | e1=expr "<=" e2=expr { Nodes.Op { left=e1; right=e2; operator=Nodes.LessEq } } + | e1=expr ">=" e2=expr { Nodes.Op { left=e1; right=e2; operator=Nodes.MoreEq } } + | e1=expr "<" e2=expr { Nodes.Op { left=e1; right=e2; operator=Nodes.Less } } + | e1=expr ">" e2=expr { Nodes.Op { left=e1; right=e2; operator=Nodes.More } } + | "~" value=expr %prec TILDE { Nodes.Flip value } + | "(" e=expr ")" { Nodes.Parenthized e } + | func_call=func_call { + Nodes.FuncCall func_call + } + | func_lambda=func_lambda { + Nodes.FuncLambda func_lambda + } + | meth_lambda=meth_lambda { + Nodes.MethLambda meth_lambda + } diff --git a/lib/cst/to_tree_graph.ml b/lib/cst/to_tree_graph.ml new file mode 100644 index 0000000..565e59c --- /dev/null +++ b/lib/cst/to_tree_graph.ml @@ -0,0 +1,174 @@ +open Tree_graph + +let rec type_to_node (x: Nodes.type_expr) = match x with + | SimpleType s -> Leaf s + | QualifiedType (pkg, type_) -> Leaf (pkg ^ "$" ^ type_) + | FuncType { params; ret_type } -> + fields [ + ("param", map_seq type_to_node params); + ("type", type_to_node ret_type); + ] + | MethType { this_type; params; ret_type; is_mut } -> + fields [ + ("this_type", type_to_node this_type); + ("param", map_seq type_to_node params); + ("type", type_to_node ret_type); + ("is_mut", Leaf (string_of_bool is_mut)); + ] + +and expr_to_node (x: Nodes.expr) = match x with + | IntLit x -> Leaf x + | FloatLit x -> Leaf x + | StrLit x -> Leaf x + | BoolLit x -> Leaf (string_of_bool x) + | Ident x -> Leaf x + | QualifiedIdent (pkg, name) -> Leaf (pkg ^ "$" ^ name) + | Op x -> + fields [ + (op_to_name x.operator, fields [ + ("left", expr_to_node x.left); + ("right", expr_to_node x.right); + ]); + ] + | Flip x -> + group "flip" (expr_to_node x) + | Parenthized x -> + group "parenthized" (expr_to_node x) + | FuncCall x -> + func_call_to_node x + | FuncLambda x -> + group "func_lambda" (func_lambda_to_node x) + | MethLambda x -> + group "meth_lambda" (meth_lambda_to_node x) + +and op_to_name (x : Nodes.operator) = match x with + | Add -> "+" + | Sub -> "-" + | Mul -> "*" + | Div -> "/" + | Eq -> "==" + | LessEq -> "<=" + | MoreEq -> ">=" + | Less -> "<" + | More -> ">" + +and abort_handle_to_node (x : Nodes.abort_handle) = match x with + | AbortBody x -> body_to_node x + | AbortShorthand x -> expr_to_node x + +and func_call_to_node x = match x with + | SafeCall x -> + group "function_call" (fields [ + ("callee", expr_to_node x.callee); + ("args", map_seq expr_to_node x.args); + ]) + | AbortCall x -> + let fs = [ + ("callee", expr_to_node x.callee); + ("args", map_seq expr_to_node x.args); + ("handle_block", abort_handle_to_node x.handle_block); + ] in + let fs = match x.binder with + | Some b -> ("binder", Leaf b) :: fs + | None -> fs + in + group "function_call" (fields fs) + +and param_to_node (x : Nodes.param) = + fields [ + ("name", Leaf x.name); + ("type", type_to_node x.type_); + ] + +and params_to_node (x : Nodes.param list) = + map_seq param_to_node x + +and elif_to_fields (x: Nodes.cond_block) = + fields [ + ("cond", expr_to_node x.cond); + ("block", map_seq stat_to_node x.block); + ] + +and cond_seq_to_node (x: Nodes.cond_seq) = + let cond_seq = [ + ("if", fields [ + ("cond", expr_to_node x.if_.cond); + ("block", map_seq stat_to_node x.if_.block); + ]); + ("elif", map_seq elif_to_fields x.elifs_); + ] in + let cond_seq = match x.else_ with + | Some x -> ("else", fields [ + ("block", map_seq stat_to_node x); + ]) :: cond_seq + | None -> cond_seq + in group "cond_seq" (fields cond_seq) + +and stat_to_node (x : Nodes.stat) = match x with + | FuncCallStat x -> func_call_to_node x + | DeclStat x -> decl_to_node x + | AbortStat x -> group "abort_stat" (expr_to_node x) + | RetStat x -> group "ret_stat" (expr_to_node x) + | ResolveStat x -> group "resolve_stat" (expr_to_node x) + | CondSeq x -> cond_seq_to_node x + +and body_to_node (x : Nodes.body) = match x with + | Scope scope -> + group "scope" (fields [ + ("stat", map_seq stat_to_node scope); + ]) + | RetShorthand x -> + group "ret_shorthand" (expr_to_node x) + +and ret_to_node (x : Nodes.ret_type) = match x with + | SafeRet ret -> type_to_node ret + | AbortRet (ret_type, abort_type) -> + fields [ + ("safe_type", type_to_node ret_type); + ("abort_type", type_to_node abort_type); + ] + +and func_lambda_to_node (x : Nodes.func_lambda) = + fields [ + ("param", params_to_node x.params); + ("ret_type", ret_to_node x.ret_type); + ("body", body_to_node x.body); + ] + +and meth_lambda_to_node (x : Nodes.meth_lambda) = + fields [ + ("this_type", type_to_node x.this_type); + ("param", params_to_node x.params); + ("ret_type", ret_to_node x.ret_type); + ("body", body_to_node x.body); + ("is_mut", Leaf (string_of_bool x.is_mut)); + ] + +and decl_to_node (x: Nodes.decl) = match x with + | FuncDecl x -> + group "func_decl" (fields [ + ("name", Leaf x.name); + ("func", func_lambda_to_node x.func); + ]) + | MethDecl x -> + group "meth_decl" (fields [ + ("name", Leaf x.name); + ("func", meth_lambda_to_node x.func); + ]) + | VarDecl { name; type_; value } -> + group "var_decl" (fields [ + ("name", Leaf name); + ("type", type_to_node type_); + ("value", expr_to_node value); + ]) + | ConstructorDecl { name; type_; args } -> + group "constructor_decl" (fields [ + ("name", Leaf name); + ("type", type_to_node type_); + ("args", map_seq expr_to_node args); + ]) + +let to_node ({ Nodes.decls } : Nodes.package) = + group "package" (fields [ + ("declarations", map_seq decl_to_node decls); + ]) diff --git a/lib/tree_graph/dune b/lib/tree_graph/dune new file mode 100644 index 0000000..756b7f6 --- /dev/null +++ b/lib/tree_graph/dune @@ -0,0 +1,2 @@ +(library + (name tree_graph)) diff --git a/lib/tree_graph/node.ml b/lib/tree_graph/node.ml new file mode 100644 index 0000000..87ca62a --- /dev/null +++ b/lib/tree_graph/node.ml @@ -0,0 +1,13 @@ +module StringMap = Map.Make(String) [@@deriving map] + +type node = + | Leaf of string + | Group of { title: string; body: node } + | Fields of node StringMap.t + | Sequence of node list + +(* Smart constructors *) +let group title body = Group { title; body } +let fields pairs = Fields (StringMap.of_list pairs) +let seq xs = Sequence xs +let map_seq f xs = Sequence (List.map f xs) diff --git a/lib/tree_graph/render.ml b/lib/tree_graph/render.ml new file mode 100644 index 0000000..bc6a80a --- /dev/null +++ b/lib/tree_graph/render.ml @@ -0,0 +1,58 @@ +type item = + | ILeaf of string * string + | IContainer of string * item list + +let rec collect ~name node = + match node with + | Node.Leaf v -> + [ILeaf (name, v)] + + | Node.Group { title; body } -> + let new_name = if name = "" then title else name ^ " > " ^ title in + collect ~name:new_name body + + | Node.Fields map -> + let nested_items = + Node.StringMap.bindings map + |> List.map (fun (k, v) -> collect ~name:k v) + |> List.flatten + in + if name = "" then nested_items + else [IContainer (name, nested_items)] + + | Node.Sequence list -> + if list = [] then + if name = "" then [] else [IContainer (name, [])] + else + List.mapi (fun i v -> + let element_name = + if name = "" then Printf.sprintf "[%d]" i + else Printf.sprintf "%s[%d]" name i + in + collect ~name:element_name v + ) list |> List.flatten + +let render root_node = + let items = collect ~name:"" root_node in + let rec print_items prefix items = + let len = List.length items in + List.iteri (fun i item -> + let is_last = (i = len - 1) in + let branch = if is_last then "└── " else "├── " in + let nested_prefix = prefix ^ (if is_last then " " else "│ ") in + match item with + | ILeaf (name, value) -> + if name = "" then + Printf.printf "%s%s%s\n" prefix branch value + else + Printf.printf "%s%s%s: %s\n" prefix branch name value + | IContainer (name, nested_items) -> + if name = "" then + print_items prefix nested_items + else begin + Printf.printf "%s%s%s:\n" prefix branch name; + print_items nested_prefix nested_items + end + ) items + in + print_items "" items diff --git a/lib/tree_graph/tree_graph.ml b/lib/tree_graph/tree_graph.ml new file mode 100644 index 0000000..6db1f99 --- /dev/null +++ b/lib/tree_graph/tree_graph.ml @@ -0,0 +1,2 @@ +include Node +let render node = Render.render node diff --git a/syntax.txt b/syntax.txt new file mode 100644 index 0000000..738fbb0 --- /dev/null +++ b/syntax.txt @@ -0,0 +1,7 @@ +main (this Int, a Bool)! Void { + print(2, 2) +} + + +new function type syntax: +varName [this Player, Weapon]! -> Void diff --git a/test-parser/main.zn b/test-parser/main.zn new file mode 100644 index 0000000..83ad6d0 --- /dev/null +++ b/test-parser/main.zn @@ -0,0 +1,18 @@ +main (this Int, a Bool)! Void?String { + x [this Int]! -> Void = "hello" + y String = "world" + content String = read("wordlist.txt") ?? "" + + if random() { + results List = crawl(content) ? error { + Std$print(error) + abort error + } + } + else { + rifle Weapon("rifle", 20) + value Bool = ~true + 3 + } +} + +square (x Int, y Int) Int => x * y diff --git a/test-parser/nextStep.zn b/test-parser/nextStep.zn new file mode 100644 index 0000000..7afda32 --- /dev/null +++ b/test-parser/nextStep.zn @@ -0,0 +1,7 @@ +Void main() { + print("hello") + Std$print(1 + 1 + 3 + 4) + @Intrinsics$print("hello") + print(res.value) + print(res:getValue()) +} diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json deleted file mode 100644 index 65c85e6..0000000 --- a/vcpkg-configuration.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "default-registry": { - "kind": "git", - "baseline": "4e39a8622e56a113c89228e4e13944dce8654da7", - "repository": "https://github.com/microsoft/vcpkg" - }, - "registries": [ - { - "kind": "artifact", - "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", - "name": "microsoft" - } - ] -} diff --git a/vcpkg.json b/vcpkg.json deleted file mode 100644 index be32b30..0000000 --- a/vcpkg.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dependencies": [ - "nlohmann-json", - "cereal" - ] -} diff --git a/zane-compiler.opam b/zane-compiler.opam new file mode 100644 index 0000000..1731773 --- /dev/null +++ b/zane-compiler.opam @@ -0,0 +1,28 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "A short synopsis" +description: "A longer description" +tags: ["add topics" "to describe" "your" "project"] +homepage: "https://github.com/zane-lang/compiler" +bug-reports: "https://github.com/zane-lang/compiler/issues" +depends: [ + "dune" {>= "3.21"} + "ocaml" + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/zane-lang/compiler.git" +x-maintenance-intent: ["(latest)"]