├── .gitignore ├── LICENSE.txt ├── README.md ├── TODO.md ├── cmakelists.txt ├── dev.sh ├── docs-src ├── CNAME ├── README.md ├── _build.sh ├── _config.js ├── _css │ ├── code.css │ ├── jbm.css │ └── raster.css ├── _hl │ ├── abnf.js │ ├── armasm.js │ ├── co.js │ ├── llvm.js │ ├── wasm.js │ └── x86asm.js ├── _template.html ├── etc │ ├── arrays-and-ownership.html │ ├── arrays.md │ ├── elf-spec-v1.1.txt │ ├── elf │ │ ├── header.html │ │ ├── index.html │ │ ├── intro.html │ │ ├── lddynamic.html │ │ ├── ldheader.html │ │ ├── ldintro.html │ │ ├── relocation.html │ │ ├── sections.html │ │ ├── string-table.html │ │ └── symbol-table.html │ ├── execution-context-switching.md │ ├── guiding-principles.md │ ├── link-thoughts.md │ ├── llvm.md │ ├── ownership.md │ ├── rt-go-sched-parking.md │ ├── type-context.md │ ├── typeid.md │ ├── types-and-mutability.md │ └── x86-64-register-encodings.txt ├── index.md ├── sitemap.md └── style.css ├── docs ├── CNAME ├── etc │ ├── arrays-and-ownership.html │ ├── arrays.html │ ├── elf-spec-v1.1.txt │ ├── elf │ │ ├── header.html │ │ ├── index.html │ │ ├── intro.html │ │ ├── lddynamic.html │ │ ├── ldheader.html │ │ ├── ldintro.html │ │ ├── relocation.html │ │ ├── sections.html │ │ ├── string-table.html │ │ └── symbol-table.html │ ├── execution-context-switching.html │ ├── guiding-principles.html │ ├── link-thoughts.html │ ├── llvm.html │ ├── ownership.html │ ├── rt-go-sched-parking.html │ ├── type-context.html │ ├── typeid.html │ ├── types-and-mutability.html │ └── x86-64-register-encodings.txt ├── index.html ├── sitemap.html └── style.css ├── example ├── array.co ├── comptime-buf.w ├── comptime-list.w ├── consts.w ├── context.co ├── ex1.c ├── factorial.w ├── fmt-print-drop-pattern.co ├── future-borrow-move.w ├── future-errors.co ├── future-lambda.w ├── future-ownership.w ├── future-ownership2.w ├── future-type-functions-generics.w ├── future-type-variants.w ├── future-where.w ├── hello.co ├── hello │ ├── README.md │ ├── add.w │ └── main.w ├── idealtype.co ├── indent.co ├── macro.co ├── mem.w ├── minimeow.w ├── multi-const.co ├── namespace-idea-buffer.w ├── ownership_you_can_count_on.co ├── refs-sketch.co ├── refs.co ├── soa-sketch.co ├── struct.co ├── template-functions-idea.co ├── test-array-index.co ├── test-array-init-and-copy.co ├── test-const-default-struct-init.co ├── test-const-tuple-access.co ├── test-error-var-init-type.co ├── test-multi-assign.co ├── test-named-arguments.co ├── test-nested-struct-access.co ├── test-var-shadowing.co ├── top-level-init.w ├── type-buffer.w └── var-let-const.co ├── init.sh ├── misc ├── _common.sh ├── arm64 │ ├── .gitignore │ └── howto.txt ├── build-libcxx.sh ├── build-llvm.sh ├── clang_driver-clang13.diff ├── clang_driver.diff ├── etc │ ├── array-ref-mockup.c │ ├── array-ref-mockup2.c │ └── minimal-llvm-c-program.c ├── gen_parselet_map.py ├── git-hooks │ ├── post-checkout.sh │ └── post-merge.sh ├── libcxx.make ├── myclang │ ├── .gitignore │ ├── Makefile │ ├── build-driver.sh │ ├── driver.cc │ ├── driver_cc1_main.cc │ ├── driver_cc1as_main.cc │ ├── llvm_api.cc │ ├── llvm_api.h │ ├── main.c │ └── test │ │ └── main.c └── sublime-syntax │ └── co │ ├── Build.sublime-build │ ├── Co.sublime-syntax │ ├── Comments.tmPreferences │ ├── Indent.tmPreferences │ ├── IndentComments.tmPreferences │ ├── IndentStrings.tmPreferences │ ├── Snippets │ ├── comment-block.sublime-snippet │ └── fun.sublime-snippet │ ├── SymbolIndex.tmPreferences │ └── SymbolList.tmPreferences ├── src ├── co │ ├── bn │ │ ├── bn.c │ │ ├── bn.h │ │ ├── libbn.cc │ │ ├── libbn.h │ │ └── test.c │ ├── build.c │ ├── build.h │ ├── co.c │ ├── common.h │ ├── ir │ │ ├── arch_base.lisp │ │ ├── constcache.c │ │ ├── gen_ops.py │ │ ├── ir-ast.c │ │ ├── ir-repr.c │ │ ├── ir.c │ │ ├── ir.h │ │ ├── irbuilder.c │ │ ├── irbuilder.h │ │ ├── op.c │ │ └── op.h │ ├── llvm │ │ ├── jit.cc │ │ ├── lld.cc │ │ ├── llvm-demo.cc │ │ ├── llvm-includes.hh │ │ ├── llvm.c │ │ ├── llvm.cc │ │ ├── llvm.h │ │ └── llvmlib-dummy.cc │ ├── parse │ │ ├── ast.c │ │ ├── ast.h │ │ ├── ast_repr.c │ │ ├── ast_validate.c │ │ ├── ast_visit.c │ │ ├── convlit.c │ │ ├── eval.c │ │ ├── parse.c │ │ ├── parse.h │ │ ├── resolve_sym.c │ │ ├── resolve_type.c │ │ ├── scan.c │ │ ├── scan_test.c │ │ ├── token.c │ │ ├── typeid.c │ │ ├── universe.c │ │ └── universe.h │ ├── pos.c │ ├── pos.h │ ├── source.c │ ├── types.c │ ├── types.h │ └── util │ │ ├── array.c │ │ ├── array.h │ │ ├── array_test.c │ │ ├── error.c │ │ ├── error.h │ │ ├── hashmap.c.h │ │ ├── hashmap.h │ │ ├── ptrmap.c │ │ ├── ptrmap.h │ │ ├── rbtree.c.h │ │ ├── rtimer.c │ │ ├── rtimer.h │ │ ├── sexpr.c │ │ ├── sexpr.h │ │ ├── stk_array.h │ │ ├── str_extras.c │ │ ├── str_extras.h │ │ ├── sym.c │ │ ├── sym.h │ │ ├── symmap.c │ │ ├── symmap.h │ │ ├── tmpstr.c │ │ ├── tmpstr.h │ │ ├── tstyle.c │ │ └── tstyle.h ├── rt-test │ └── rt-test.c ├── rt │ ├── chan-wip │ │ ├── chan.c │ │ └── chan.h │ ├── exectx │ │ ├── exectx.h │ │ ├── exectx_arm64_aapcs_elf.S │ │ └── exectx_x86_64_sysv.S │ ├── fctx.h │ ├── sched.c │ ├── sched.h │ ├── schedimpl.h │ └── stack.c └── xxhash │ ├── LICENSE │ ├── cmakelists.txt │ ├── xxhash.c │ └── xxhash.h ├── test.sh └── test ├── co-parser-test.c └── parse ├── global-ideal-const.co ├── indent-blocks.co └── let-at-end-of-block.co /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ~* 3 | /.build 4 | /_* 5 | /bin 6 | /**/out/ 7 | /out* 8 | /deps 9 | /work 10 | /src/**/*.d 11 | /src/**/*.pch 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2017-2021, Rasmus Andersson 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Co programming language 2 | 3 | ## Building 4 | 5 | Initial setup: `./init.sh` will install the following into `deps/`: 6 | - [ckit](https://github.com/rsms/ckit) build tool and rbase library 7 | - [ckit-jemalloc](https://github.com/rsms/ckit-jemalloc) memory allocator 8 | - [zlib](https://zlib.net) static library 9 | - [llvm+clang](https://llvm.org) tools and static libraries 10 | 11 | Build: 12 | - Unoptimized with all checks enabled: `ckit build co` 13 | - Optimized with assertions: `ckit build -safe co` 14 | - Optimized without assertions: `ckit build -fast co` 15 | - RT test program: `ckit watch -r co-rt-test` 16 | - Verbose build: `ckit build -v` 17 | - Build everything: `ckit build` 18 | 19 | Development and testing: 20 | - Run all tests: `./test.sh` 21 | - Run all tests live: `./test.sh -w` 22 | - Run unit test with prefix "scan": `./test.sh scan` 23 | - Run unit test with prefix "scan" live: `./test.sh -w scan` 24 | - Live main program: `ckit watch -rsh="{BUILD}/co build example/hello.co" co` 25 | 26 | Note: debug builds have the following checks and features enabled: 27 | - All assertions (both "safe" and "debug") 28 | - [Clang address sanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) 29 | - [Clang undefined-behavior sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) 30 | - `R_TEST` unit tests (main executable runs all tests on start) 31 | 32 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | Wish list. Things that could be improved or added. 4 | 5 | 6 | ## Parsing, analysis and compilation 7 | 8 | - [ ] String literals. 9 | [See co1](https://github.com/rsms/co/blob/master/src/scanner.ts#L894). 10 | - [ ] Scan & parse string literals 11 | - [ ] Scan & parse multiline string literals 12 | - [ ] Scan & parse floating point literals. 13 | [See co1](https://github.com/rsms/co/blob/master/src/scanner.ts#L1205) 14 | - [ ] Scan & parse character literals (what type is a char? u8? "rune"?). 15 | [See co1](https://github.com/rsms/co/blob/master/src/scanner.ts#L823) 16 | - [ ] Arrays and slices 17 | - [x] Parsing array and slice types e.g. `[3][4]type`, `[]type` 18 | - [x] Parsing & analysis of array indexing e.g. `a[3]` 19 | - [x] IR gen of array indexing e.g. `a[3]` 20 | - [ ] Parsing & analysis of array slicing e.g. `a[:3]` 21 | - [ ] IR gen of array slicing e.g. `a[:3]` 22 | - [x] Rename basic primitive types like `int32` to LLVM/Rust/Zig style `i32`, `u16` etc. 23 | - [ ] Multi function dispatch e.g. `foo(int)` vs `foo(float32)`. 24 | 25 | 26 | ### IR 27 | 28 | Use Co IR in between AST and LLVM IR. 29 | This makes it easier to generate code with alternative backends like JS, makes the 30 | LLVM backend simpler and removes language-specifics from most of the gnarly codegen stuff. 31 | 32 | Keep it (much) simpler than Co1 IR, without regalloc or instruction selection. 33 | 34 | - [x] Dedicated type system that does not rely on AST nodes (IRType) 35 | - [ ] New LLVM backend that uses IR instead of AST as the source 36 | - [ ] Interpreter for testing and comptime evaluation 37 | 38 | 39 | ## Testing & QA 40 | 41 | - [ ] Parser test infra 42 | - [x] Scanner/tokenizer tests (`src/co/parse/scan_test.c`) 43 | - [ ] compile-time AST validator for development & debugging 44 | - [x] `@unres` unresolved hierarchy integrity 45 | - [x] AST repr that outputs easily-parsable S-expr 46 | - [ ] S-expr parser for parsing `#*!AST ...*#` comments in test sources 47 | - [ ] Diff the expected AST with the actual one. Complain on mismatch. 48 | Maybe just `text_diff(print(parse(actual)), print(parse(expected)))`. 49 | [See co1](https://github.com/rsms/co/blob/master/src/ast/test/ast_test.ts#L274) 50 | which may not be worth porting. Its ergonimics are not great. 51 | - [ ] Codegen tests. 52 | I.e. build & run programs and verify their output, like Go examples. 53 | - [ ] GitHub CI with builds 54 | See lobster for example: https://github.com/aardappel/lobster/blob/ 55 | d8e2ce7f6ce2dd5b94e9bff92532c2e50c438582/.github/workflows/build.yml 56 | 57 | 58 | ## Misc 59 | 60 | - [ ] Actual full build of packages _(need to expand the details of this here)_ 61 | - [ ] Packages/modules 62 | - [ ] Resolution and importing 63 | - [ ] What are the cached artifacts? Objects? Object archives? LLVM BC? LLVM IR? Nothing..? 64 | - [ ] Comptime eval — a simple interpreter that can evaluate code at compile time. 65 | There's some very basic code for array size eval in `src/co/parse/eval.c`. 66 | - [ ] Explore building WASM for web browsers (without LLVM) 67 | - [ ] Code generation without LLVM to WASM or JS (is there 3rd part stuff I can use?) 68 | - [ ] Look into using [mimalloc](https://github.com/microsoft/mimalloc) 69 | for memory allocation 70 | 71 | 72 | ## Documentation 73 | 74 | - [ ] Uhm... documentation?! 75 | - [ ] Language 76 | - [ ] Syntax 77 | - [ ] Semantics 78 | - [ ] Examples 79 | -------------------------------------------------------------------------------- /dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | SRCFILE=${1:-example/hello.co} 3 | export ASAN_OPTIONS=detect_stack_use_after_return=1 4 | exec ckit watch \ 5 | -rsh="{BUILD}/co build $SRCFILE" \ 6 | -wf=src/co/ir/arch_base.lisp \ 7 | -wf="$SRCFILE" \ 8 | co 9 | -------------------------------------------------------------------------------- /docs-src/CNAME: -------------------------------------------------------------------------------- 1 | co-lang.org -------------------------------------------------------------------------------- /docs-src/README.md: -------------------------------------------------------------------------------- 1 | # Co website 2 | 3 | This directory contains the source for the Co website. 4 | The `docs` directory in the source tree root contains the most recent build. 5 | 6 | Requires NodeJS >=14 7 | 8 | Build: 9 | 10 | ```sh 11 | $ ./_build.sh 12 | ``` 13 | 14 | Interactive build with a local web server: `./_build.sh -w` 15 | 16 | -------------------------------------------------------------------------------- /docs-src/_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | cd "$(dirname "$0")" 4 | . ../misc/_common.sh 5 | 6 | # npm info rsms-mkweb 7 | MKWEB_AR_VERSION=0.1.11 8 | MKWEB_AR_URL=https://registry.npmjs.org/rsms-mkweb/-/rsms-mkweb-${MKWEB_AR_VERSION}.tgz 9 | MKWEB_AR_URL_SHA1=67d417e1d1c842f6b99c4ef605b12bbf510020df 10 | MKWEB_AR_NAME=mkweb-${MKWEB_AR_VERSION}.tgz 11 | MKWEB_EXE=$DEPS_DIR/mkweb/mkweb-${MKWEB_AR_VERSION} 12 | 13 | if ! [ -f "$MKWEB_EXE" ]; then 14 | _download "$MKWEB_AR_URL" $MKWEB_AR_URL_SHA1 "$MKWEB_AR_NAME" 15 | MKWEB_DIR=$(dirname "$MKWEB_EXE") 16 | rm -rf "$MKWEB_DIR" 17 | _extract_tar "$(_downloaded_file "$MKWEB_AR_NAME")" "$MKWEB_DIR" 18 | (cd "$MKWEB_DIR" && 19 | npm i --omit dev --no-audit --no-bin-links --no-fund --no-package-lock) 20 | cp "$MKWEB_DIR/dist/mkweb" "$MKWEB_EXE" 21 | chmod +x "$MKWEB_EXE" 22 | fi 23 | 24 | MKWEB_ARGS=( -verbose -opt ) 25 | # if [ -z "$1" -o "$1" != "-w" ]; then 26 | # MKWEB_ARGS+=( -opt ) 27 | # fi 28 | 29 | exec "$MKWEB_EXE" "${MKWEB_ARGS[@]}" "$@" 30 | -------------------------------------------------------------------------------- /docs-src/_config.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ 2 | site, // mutable object describing the site 3 | hljs, // HighlightJS module (NPM: highlight.js) 4 | markdown, // Markdown module (NPM: markdown-wasm) 5 | glob, // glob function (NPM: miniglob) 6 | }) => { 7 | // called when program starts 8 | site.outdir = "../docs" 9 | // console.log(site) 10 | 11 | // configure highlight.js 12 | hljs.registerLanguage("co", require("./_hl/co")) 13 | hljs.registerLanguage("asmarm", require("./_hl/armasm")) 14 | hljs.registerLanguage("asmx86", require("./_hl/x86asm")) 15 | hljs.registerLanguage("wast", require("./_hl/wasm")) 16 | hljs.registerLanguage("llvm", require("./_hl/llvm")) 17 | hljs.registerLanguage("bnf", require("./_hl/abnf")) 18 | 19 | // these optional callbacks can return a Promise to cause build process to wait 20 | // 21 | // site.onBeforeBuild = (files) => { 22 | // // called when .pages has been populated 23 | // // console.log("onBeforeBuild pages:", site.pages) 24 | // // console.log("onBeforeBuild files:", files) 25 | // } 26 | 27 | // site.onAfterBuild = (files) => { 28 | // // called after site has been generated 29 | // // console.log("onAfterBuild") 30 | // } 31 | } 32 | -------------------------------------------------------------------------------- /docs-src/_css/code.css: -------------------------------------------------------------------------------- 1 | pre { 2 | --fg-comment: rgba(var(--foreground-color-rgb),0.5); 3 | --fg-commenterr: rgb(186, 94, 81); 4 | --fg-keyword: #d73a49; 5 | --fg-type: var(--fg-keyword); 6 | --fg-data: #333377; 7 | --fg-string: #333377; 8 | --fg-diff-add: #55a532; 9 | --bg-diff-add: #eaffea; 10 | --fg-diff-rem: #bd2c00; 11 | --bg-diff-rem: #ffecec; 12 | 13 | @media (prefers-color-scheme: dark) { 14 | --fg-comment: rgba(var(--foreground-color-rgb),0.4); 15 | --fg-commenterr: #735048; 16 | --fg-keyword: #ff7b72; 17 | --fg-data: #abe0ec; 18 | --fg-string: rgb(165, 214, 255); 19 | --fg-diff-add: #55a532; 20 | --bg-diff-add: #eaffea; 21 | --fg-diff-rem: #bd2c00; 22 | --bg-diff-rem: #ffecec; 23 | } 24 | 25 | background: var(--code-bg); 26 | font-size: 0.875rem; /* 14dp @ font-size 16 */ 27 | /* font-size: 0.9375rem; */ /* 15dp @ font-size 16 */ 28 | padding: 0.625rem; /* 10dp @ font-size 16 */ 29 | border-radius: 0.3125rem; /* 5dp @ font-size 16 */ 30 | -webkit-text-size-adjust: none; 31 | } 32 | 33 | /* comments */ 34 | .hl-comment { 35 | color: var(--fg-comment); 36 | & .hl-errormsg { color: var(--fg-commenterr) } 37 | } 38 | 39 | /* meta (?) */ 40 | .hl-meta { color: var(--fg-keyword) } 41 | 42 | /* variable */ 43 | .hl-variable, 44 | .hl-template-variable { 45 | color: rgba(var(--foreground-color-rgb),0.6); 46 | } 47 | 48 | /* type */ 49 | .hl-type { 50 | color: var(--fg-type); 51 | } 52 | 53 | /* keywords */ 54 | .hl-keyword, 55 | .hl-selector-tag, 56 | .hl-section, 57 | .hl-name, 58 | .hl-tag, 59 | .hl-attr, 60 | .hl-selector-id, 61 | .hl-selector-class, 62 | .hl-selector-attr, 63 | .hl-selector-pseudo, 64 | .hl-built_in { 65 | color: var(--fg-keyword); 66 | } 67 | 68 | /* data */ 69 | .hl-strong, 70 | .hl-emphasis, 71 | .hl-number, 72 | .hl-literal, 73 | .hl-symbol, 74 | .hl-bullet, 75 | .hl-attribute, 76 | .hl span.n + span.o + span.n { 77 | color: var(--fg-data); 78 | } 79 | 80 | /* string literal data */ 81 | .hl-string, 82 | .hl-quote { 83 | color: var(--fg-string); 84 | } 85 | 86 | .hl-typedef { 87 | .hl-title { 88 | font-weight: 600; 89 | } 90 | } 91 | 92 | .hl-function { 93 | .hl-title { 94 | font-weight: 600; 95 | } 96 | .hl-params {} 97 | .hl-params + .hl-title { 98 | color: var(--fg-type); 99 | font-weight: initial; 100 | } 101 | } 102 | 103 | /* diff */ 104 | .hl-addition { color:var(--fg-diff-add); background-color:var(--bg-diff-add); } 105 | .hl-deletion { color:var(--fg-diff-rem); background-color:var(--bg-diff-rem); } 106 | 107 | /* hyperlink..? */ 108 | .hl-link {} 109 | -------------------------------------------------------------------------------- /docs-src/_css/jbm.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'jbmono'; 3 | font-weight: 100 800; 4 | font-style: normal; 5 | font-named-instance: 'Regular'; 6 | src: url("https://rsms.me/res/fonts/jbm/jetbrains-mono-wght.woff2") format("woff2"); 7 | } 8 | @font-face { 9 | font-family: 'jbmono'; 10 | font-weight: 100 800; 11 | font-style: italic; 12 | font-named-instance: 'Italic'; 13 | src: url("https://rsms.me/res/fonts/jbm/jetbrains-mono-italic_wght.woff2") format("woff2"); 14 | } 15 | -------------------------------------------------------------------------------- /docs-src/_hl/abnf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} value 3 | * @returns {RegExp} 4 | * */ 5 | 6 | /** 7 | * @param {RegExp | string } re 8 | * @returns {string} 9 | */ 10 | function source(re) { 11 | if (!re) return null; 12 | if (typeof re === "string") return re; 13 | 14 | return re.source; 15 | } 16 | 17 | /** 18 | * @param {...(RegExp | string) } args 19 | * @returns {string} 20 | */ 21 | function concat(...args) { 22 | const joined = args.map((x) => source(x)).join(""); 23 | return joined; 24 | } 25 | 26 | /* 27 | Language: Augmented Backus-Naur Form 28 | Author: Alex McKibben 29 | Website: https://tools.ietf.org/html/rfc5234 30 | Audit: 2020 31 | */ 32 | 33 | /** @type LanguageFn */ 34 | function abnf(hljs) { 35 | const KEYWORDS = [ 36 | "ALPHA", 37 | "BIT", 38 | "CHAR", 39 | "CR", 40 | "CRLF", 41 | "CTL", 42 | "DIGIT", 43 | "DQUOTE", 44 | "HEXDIG", 45 | "HTAB", 46 | "LF", 47 | "LWSP", 48 | "OCTET", 49 | "SP", 50 | "VCHAR", 51 | "WSP" 52 | ]; 53 | 54 | const COMMENTS = [ 55 | hljs.COMMENT('//', '$'), 56 | hljs.COMMENT('/\\*', '\\*/'), 57 | ] 58 | 59 | const TERMINAL_BINARY = { 60 | scope: "symbol", 61 | match: /%b[0-1]+(-[0-1]+|(\.[0-1]+)+)?/ 62 | }; 63 | 64 | const TERMINAL_DECIMAL = { 65 | scope: "symbol", 66 | match: /%d[0-9]+(-[0-9]+|(\.[0-9]+)+)?/ 67 | }; 68 | 69 | const TERMINAL_HEXADECIMAL = { 70 | scope: "symbol", 71 | match: /%x[0-9A-F]+(-[0-9A-F]+|(\.[0-9A-F]+)+)?/ 72 | }; 73 | 74 | const CASE_SENSITIVITY = { 75 | scope: "symbol", 76 | match: /%[si](?=".*")/ 77 | }; 78 | 79 | const RULE_DECLARATION = { 80 | scope: "attribute", 81 | // match: concat(IDENT, /(?=\s*=)/) 82 | match: /^\s*[a-zA-Z][a-zA-Z0-9-_]*(?=\s*=)/ 83 | }; 84 | 85 | const ASSIGNMENT = { 86 | scope: "operator", 87 | match: /=\/?/ 88 | }; 89 | 90 | return { 91 | name: 'Augmented Backus-Naur Form', 92 | illegal: /[!@#$^&',?+~`|:]/, 93 | keywords: KEYWORDS, 94 | contains: [ 95 | ASSIGNMENT, 96 | RULE_DECLARATION, 97 | ...COMMENTS, 98 | TERMINAL_BINARY, 99 | TERMINAL_DECIMAL, 100 | TERMINAL_HEXADECIMAL, 101 | CASE_SENSITIVITY, 102 | hljs.QUOTE_STRING_MODE, 103 | hljs.NUMBER_MODE 104 | ] 105 | }; 106 | } 107 | 108 | module.exports = abnf; 109 | -------------------------------------------------------------------------------- /docs-src/_hl/co.js: -------------------------------------------------------------------------------- 1 | // Co syntax highlighting for highlight.js 2 | module.exports = function(hljs) { 3 | const LITERALS = [ 4 | "true", 5 | "false", 6 | "nil" 7 | ] 8 | const BUILT_INS = [ 9 | "copy", 10 | "panic", 11 | "print", 12 | ] 13 | const KWS = [ 14 | "auto", 15 | "break", 16 | "case", 17 | "const", 18 | "continue", 19 | "default", 20 | "else", 21 | "fallthrough", 22 | "for", 23 | "fun", 24 | "goto", 25 | "if", 26 | "import", 27 | "interface", 28 | "mut", 29 | "return", 30 | "select", 31 | "struct", 32 | "switch", 33 | "type", 34 | "var", 35 | ] 36 | const TYPES = [ 37 | "bool", 38 | "i8", "i16", "i32", "i64", "int", 39 | "u8", "u16", "u32", "u64", "uint", 40 | "f32", "f64", 41 | "byte", 42 | "str", 43 | ] 44 | 45 | const KEYWORDS = { 46 | keyword: KWS, 47 | literal: LITERALS, 48 | built_in: BUILT_INS, 49 | type: TYPES, 50 | } 51 | 52 | const COMMENTS = [ 53 | //hljs.COMMENT('//', '$'), 54 | hljs.COMMENT('/\\*', '\\*/'), 55 | { className: 'comment', 56 | begin: '//', 57 | end: '$', 58 | contains: [ 59 | { 60 | className: 'errormsg', 61 | begin: /error:/, 62 | end: /$/, 63 | }, 64 | ] 65 | }, 66 | ] 67 | 68 | const FUN_TYPE = { 69 | variants: [ 70 | { 71 | className: 'funtype', 72 | begin: 'fun', 73 | returnBegin: true, 74 | contains: [] // defined later 75 | }, 76 | { 77 | begin: /\(/, 78 | end: /\)/, 79 | contains: [] // defined later 80 | } 81 | ] 82 | } 83 | const FUN_TYPE2 = FUN_TYPE 84 | FUN_TYPE2.variants[1].contains = [ FUN_TYPE ] 85 | FUN_TYPE.variants[1].contains = [ FUN_TYPE2 ] 86 | const PARAMS = { 87 | className: 'params', 88 | begin: /\(/, 89 | end: /\)/, 90 | endsParent: true, 91 | keywords: KEYWORDS, 92 | relevance: 0, 93 | contains: [ 94 | FUN_TYPE, 95 | ...COMMENTS 96 | ] 97 | } 98 | FUN_TYPE.variants[0].contains = [ 99 | { className: 'type', 100 | begin: 'fun', 101 | }, 102 | PARAMS, 103 | ] 104 | 105 | return { 106 | name: 'Co', 107 | aliases: [], 108 | keywords: KEYWORDS, 109 | illegal: '/, 148 | keywords: 'reified', 149 | relevance: 0 150 | }, 151 | PARAMS, 152 | ...COMMENTS 153 | ] 154 | }, 155 | 156 | { className: 'typedef', 157 | beginKeywords: 'type', 158 | end: '\\s*$', 159 | excludeEnd: true, 160 | contains: [ 161 | hljs.TITLE_MODE, 162 | ...COMMENTS, 163 | ] 164 | }, 165 | 166 | ] 167 | }; 168 | } 169 | -------------------------------------------------------------------------------- /docs-src/_hl/wasm.js: -------------------------------------------------------------------------------- 1 | /* 2 | Language: WebAssembly 3 | Website: https://webassembly.org 4 | Description: Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications. 5 | Category: web 6 | Audit: 2020 7 | */ 8 | 9 | /** @type LanguageFn */ 10 | function wasm(hljs) { 11 | const BLOCK_COMMENT = hljs.COMMENT(/\(;/, /;\)/); 12 | BLOCK_COMMENT.contains.push("self"); 13 | const LINE_COMMENT = hljs.COMMENT(/;;/, /$/); 14 | 15 | const KWS = [ 16 | "anyfunc", 17 | "block", 18 | "br", 19 | "br_if", 20 | "br_table", 21 | "call", 22 | "call_indirect", 23 | "data", 24 | "drop", 25 | "elem", 26 | "else", 27 | "end", 28 | "export", 29 | "func", 30 | "global.get", 31 | "global.set", 32 | "local.get", 33 | "local.set", 34 | "local.tee", 35 | "get_global", 36 | "get_local", 37 | "global", 38 | "if", 39 | "import", 40 | "local", 41 | "loop", 42 | "memory", 43 | "memory.grow", 44 | "memory.size", 45 | "module", 46 | "mut", 47 | "nop", 48 | "offset", 49 | "param", 50 | "result", 51 | "return", 52 | "select", 53 | "set_global", 54 | "set_local", 55 | "start", 56 | "table", 57 | "tee_local", 58 | "then", 59 | "type", 60 | "unreachable" 61 | ]; 62 | 63 | const FUNCTION_REFERENCE = { 64 | begin: [ 65 | /(?:func|call|call_indirect)/, 66 | /\s+/, 67 | /\$[^\s)]+/ 68 | ], 69 | className: { 70 | 1: "keyword", 71 | 3: "title.function" 72 | } 73 | }; 74 | 75 | const ARGUMENT = { 76 | className: "variable", 77 | begin: /\$[\w_]+/ 78 | }; 79 | 80 | const PARENS = { 81 | match: /(\((?!;)|\))+/, 82 | className: "punctuation", 83 | relevance: 0 84 | }; 85 | 86 | const NUMBER = { 87 | className: "number", 88 | relevance: 0, 89 | // borrowed from Prism, TODO: split out into variants 90 | match: /[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/ 91 | }; 92 | 93 | const TYPE = { 94 | // look-ahead prevents us from gobbling up opcodes 95 | match: /(i32|i64|f32|f64)(?!\.)/, 96 | className: "type" 97 | }; 98 | 99 | const MATH_OPERATIONS = { 100 | className: "keyword", 101 | // borrowed from Prism, TODO: split out into variants 102 | match: /\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/ 103 | }; 104 | 105 | const OFFSET_ALIGN = { 106 | match: [ 107 | /(?:offset|align)/, 108 | /\s*/, 109 | /=/ 110 | ], 111 | className: { 112 | 1: "keyword", 113 | 3: "operator" 114 | } 115 | }; 116 | 117 | return { 118 | name: 'WebAssembly', 119 | keywords: { 120 | $pattern: /[\w.]+/, 121 | keyword: KWS 122 | }, 123 | contains: [ 124 | LINE_COMMENT, 125 | BLOCK_COMMENT, 126 | OFFSET_ALIGN, 127 | ARGUMENT, 128 | PARENS, 129 | FUNCTION_REFERENCE, 130 | hljs.QUOTE_STRING_MODE, 131 | TYPE, 132 | MATH_OPERATIONS, 133 | NUMBER 134 | ] 135 | }; 136 | } 137 | 138 | module.exports = wasm; 139 | -------------------------------------------------------------------------------- /docs-src/_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ page.title || site.title }} 6 | 7 | 8 | 10 | 11 | 12 | {{! page.body }} 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs-src/etc/arrays-and-ownership.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page has moved 6 | 7 | 8 | 9 | 10 | 11 | This page has moved to a arrays.html 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs-src/etc/elf/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ELF 5 | 6 | 7 | 8 |

Object files

9 | 10 | 15 | 20 | 24 | 27 | 31 | 35 | 36 | 37 |

Program loading and dynamic linking

38 | 39 | 42 | 49 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /docs-src/etc/elf/ldintro.html: -------------------------------------------------------------------------------- 1 | 2 | Program loading and dynamic linking

3 |

Introduction to Program loading and dynamic linking

4 | This section describes the object file 5 | information and system actions that create running programs. 6 | Some information here applies to all systems; 7 | information specific to one processor resides in 8 | sections marked accordingly. 9 |

10 | Executable and shared object files statically represent programs. 11 | To execute such programs, the system uses the files to create 12 | dynamic program representations, or process images. 13 | As section ''Virtual Address Space'' in Chapter 3 of the 14 | processor supplement describes, a process image has segments that 15 | hold its text, data, stack, and so on. This chapter's major sections 16 | discuss the following: 17 |

35 |
36 | NOTE: 37 | The processor supplement defines a naming convention for ELF constants 38 | that have processor ranges specified. Names such as DT_, 39 | PT_, 40 | for processor specific extensions, incorporate the name of the 41 | processor: DT_M32_SPECIAL, for example. 42 | Pre-existing processor 43 | extensions not using this convention will be supported. 44 | 45 | 46 | 47 | 48 | 49 |
Pre-Existing Extensions
DT_JUMP_REL
50 |
51 | Previous 52 | Contents 53 | Next 54 |
55 | 56 | 57 | © 1997, 1998, 1999, 2000 The Santa Cruz Operation, Inc. All rights reserved. 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /docs-src/etc/elf/string-table.html: -------------------------------------------------------------------------------- 1 | 2 | String Table

3 |

String Table

4 | String table sections hold null-terminated character sequences, 5 | commonly called strings. 6 | The object file uses these strings to represent symbol and section names. 7 | One references a string as an index into the 8 | string table section. 9 | The first byte, which is index zero, is defined to hold 10 | a null character. 11 | Likewise, a string table's last byte is defined to hold 12 | a null character, ensuring null termination for all strings. 13 | A string whose index is zero specifies 14 | either no name or a null name, depending on the context. 15 | An empty string table section is permitted; its section header's sh_size 16 | member would contain zero. 17 | Non-zero indexes are invalid for an empty string table. 18 |

19 | A section header's sh_name 20 | member holds an index into the section header string table 21 | section, as designated by the e_shstrndx 22 | member of the ELF header. 23 | The following figures show a string table with 25 bytes 24 | and the strings associated with various indexes. 25 |

26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
Index+0+1+2+3+4+5+6+7+8+9
0\0name.\0Var
10iable\0able
20\0\0xx\0 
74 |


75 | Figure 4-15: String Table Indexes 76 |

77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
IndexString
0none
1name.
7Variable
11able
16able
24null string
106 |


107 |

108 | As the example shows, a string table index may refer 109 | to any byte in the section. 110 | A string may appear more than once; 111 | references to substrings may exist; 112 | and a single string may be referenced multiple times. 113 | Unreferenced strings also are allowed. 114 |


115 | Previous 116 | Contents 117 | Next 118 |
119 | 120 | 121 | © 1997, 1998, 1999, 2000 The Santa Cruz Operation, Inc. All rights reserved. 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs-src/etc/execution-context-switching.md: -------------------------------------------------------------------------------- 1 | # Execution context switching 2 | 3 | M has a context 4 | T has a context 5 | 6 | m_call is called on a coroutine task T to switch execution to its M. 7 | 8 | 9 | go mcall 10 | mcall switches from the g to the g0 stack and invokes fn(g), 11 | where g is the goroutine that made the call. 12 | mcall saves g's current PC/SP in g->sched so that it can be restored later. 13 | It is up to fn to arrange for that later execution, typically by recording 14 | g in a data structure, causing something to call ready(g) later. 15 | mcall returns to the original goroutine g later, when g has been rescheduled. 16 | fn must not return at all; typically it ends by calling schedule, to let the m 17 | run other goroutines. 18 | 19 | -------------------------------------------------------------------------------- /docs-src/etc/link-thoughts.md: -------------------------------------------------------------------------------- 1 | Thoughts on linking code 2 | 3 | What if instead of object files, we were to maintain a graph database of all 4 | assembled code? 5 | 6 | Traditionally a C-like compiler will parse, compile and assemble each source file into 7 | an ELF/Mach-O/etc object file and 8 | finally—when all object files required for a program are available—link them all together 9 | by reading & parsing all these object files just to build a new object (exe) file. 10 | Here's an example of a simple program with four source files: 11 | 12 | main 13 | / | \ 14 | foo bar baz 15 | \ / 16 | util 17 | 18 | main requires foo, bar and baz. foo and bar both require util. 19 | In practice this is not a tree but a list: 20 | 21 | - main -> main.o 22 | - foo -> foo.o 23 | - bar -> bar.o 24 | - baz -> baz.o 25 | - util -> util.o 26 | 27 | A C-like compiler would link foo, bar, baz, util and main objects everytime any part changes. 28 | Say we only change baz, we take the cost of re-linking the tree of foo, bar & util. 29 | 30 | Imagine if these were represented as a tree even as linked objects, not just temporarily 31 | inside the compiler. Then we could link subtrees together: 32 | 33 | main = [main.o, foo+util+bar.o, baz.o] 34 | foo+util+bar.o = [foo.o, bar.o, util.o] 35 | foo+util.o = [foo.o, util.o] # Can be skipped; unused 36 | foo+util.o = [bar.o, util.o] # Can be skipped; unused 37 | 38 | If `baz` changes, we can reuse the subtree object `foo+util+bar.o` 39 | -------------------------------------------------------------------------------- /docs-src/etc/llvm.md: -------------------------------------------------------------------------------- 1 | # llvm notes 2 | 3 | To see which features and CPUs that LLVM knows about, we can use llc. For example x86: 4 | 5 | deps/llvm/bin/llvm-as < /dev/null | deps/llvm/bin/llc -march=x86 -mattr=help 6 | 7 | Look into using "memory lifetime markers" 8 | 9 | 10 | 11 | declare void @llvm.lifetime.start(i64 , i8* nocapture ) 12 | 13 | Various useful stuff 14 | 15 | - [LLVM IR language ref](https://llvm.org/docs/LangRef.html) 16 | - [Orc v2 JIT examples](https://github.com/llvm/llvm-project/tree/llvmorg-13.0.0/llvm/examples/OrcV2Examples) 17 | - [Orc v2 overview](https://llvm.org/docs/ORCv2.html) 18 | - [JIT tutorial](https://llvm.org/docs/tutorial/BuildingAJIT2.html) 19 | -------------------------------------------------------------------------------- /docs-src/etc/ownership.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Resource ownership 3 | --- 4 | # {{title}} 5 | 6 | Resource ownership rules in Co are simple: 7 | - Storage locations own their data. 8 | - Ownership is transferred only for heap arrays. All other values are copied. 9 | - References are pointers to data owned by someone else. 10 | 11 | When a storage location goes out of scope it relinquishes its ownership by bing "dropped". 12 | When a value is dropped, any heap arrays are deallocated. 13 | Any lingering references to a dropped value are invalid. 14 | Accessing such a reference causes a "safe crash" by panicing in "safe" builds 15 | and has undefined behavior in "fast" builds. 16 | 17 | A "storage location" is a variable, struct field, tuple element, 18 | array element or function parameter. 19 | 20 | All data is passed by value in Co. 21 | Note that references are memory addresses (an integer) and thus technically 22 | copied when passed around. 23 | 24 | 25 | See also 26 | 27 | - ["Ownership You Can Count On: A Hybrid Approach to Safe Explicit Memory Management"](https://researcher.watson.ibm.com/researcher/files/us-bacon/Dingle07Ownership.pdf) 28 | - ["Friendship ended with the garbage collector" (inko)](https://yorickpeterse.com/articles/friendship-ended-with-the-garbage-collector/) 29 | - [Vale's Hybrid-Generational Memory](https://vale.dev/blog/hybrid-generational-memory) 30 | -------------------------------------------------------------------------------- /docs-src/etc/rt-go-sched-parking.md: -------------------------------------------------------------------------------- 1 | # Go scheduler Worker thread parking/unparking 2 | 3 | We need to balance between keeping enough running worker threads to utilize 4 | available hardware parallelism and parking excessive running worker threads 5 | to conserve CPU resources and power. This is not simple for two reasons: 6 | (1) scheduler state is intentionally distributed (in particular, per-P work 7 | queues), so it is not possible to compute global predicates on fast paths; 8 | (2) for optimal thread management we would need to know the future (don't park 9 | a worker thread when a new goroutine will be readied in near future). 10 | 11 | Three rejected approaches that would work badly: 12 | 13 | 1. Centralize all scheduler state (would inhibit scalability). 14 | 15 | 2. Direct goroutine handoff. That is, when we ready a new goroutine and there 16 | is a spare P, unpark a thread and handoff it the thread and the goroutine. 17 | This would lead to thread state thrashing, as the thread that readied the 18 | goroutine can be out of work the very next moment, we will need to park it. 19 | Also, it would destroy locality of computation as we want to preserve 20 | dependent goroutines on the same thread; and introduce additional latency. 21 | 22 | 3. Unpark an additional thread whenever we ready a goroutine and there is an 23 | idle P, but don't do handoff. This would lead to excessive thread parking/ 24 | unparking as the additional threads will instantly park without discovering 25 | any work to do. 26 | 27 | The current approach: 28 | 29 | We unpark an additional thread when we ready a goroutine if (1) there is an 30 | idle P and there are no "spinning" worker threads. A worker thread is considered 31 | spinning if it is out of local work and did not find work in global run queue/ 32 | netpoller; the spinning state is denoted in m.spinning and in sched.nmspinning. 33 | Threads unparked this way are also considered spinning; we don't do goroutine 34 | handoff so such threads are out of work initially. Spinning threads do some 35 | spinning looking for work in per-P run queues before parking. If a spinning 36 | thread finds work it takes itself out of the spinning state and proceeds to 37 | execution. If it does not find work it takes itself out of the spinning state 38 | and then parks. 39 | 40 | If there is at least one spinning thread (sched.nmspinning>1), we don't unpark 41 | new threads when readying goroutines. To compensate for that, if the last spinning 42 | thread finds work and stops spinning, it must unpark a new spinning thread. 43 | This approach smooths out unjustified spikes of thread unparking, 44 | but at the same time guarantees eventual maximal CPU parallelism utilization. 45 | 46 | The main implementation complication is that we need to be very careful during 47 | spinning→non-spinning thread transition. This transition can race with submission 48 | of a new goroutine, and either one part or another needs to unpark another worker 49 | thread. If they both fail to do that, we can end up with semi-persistent CPU 50 | underutilization. The general pattern for goroutine readying is: submit a goroutine 51 | to local work queue, StoreLoad-style memory barrier, check sched.nmspinning. 52 | 53 | The general pattern for spinning→non-spinning transition is: 54 | 55 | - decrement nmspinning, 56 | - StoreLoad-style memory barrier, 57 | - check all per-P work queues for new work. 58 | 59 | Note that all this complexity does not apply to global run queue as we are not 60 | sloppy about thread unparking when submitting to global queue. Also see comments 61 | for nmspinning manipulation. 62 | -------------------------------------------------------------------------------- /docs-src/etc/type-context.md: -------------------------------------------------------------------------------- 1 | # Type context 2 | 3 | Type resolver 4 | "requested types" version 2 5 | 6 | The idea is that there's always an expected type context. 7 | For example, a call to a function `(int bool)->int` has the type context `(int bool)` (a tuple), 8 | while an assignment to a var e.g. `var x int = ` has the type context `int`. 9 | 10 | Now, with basic types this is pretty straight-forward; when encountering an ideally-typed value 11 | simply convert it to the basic type (if possible.) E.g. `x int = y` => `x int = y as int`. 12 | However for complex types like tuples it's a little trickier. Consider the following: 13 | 14 | fun add(x int, y uint) -> x + y 15 | fun main { 16 | a, b = 2, 4 17 | # add's parameters => (int uint) 18 | # input arguments => (ideal ideal) 19 | add(a, b) 20 | } 21 | 22 | We need to map the input type (ideal ideal) to the context type (int uint). 23 | This is essentially tree matching as the input type and context type may be arbitrarily 24 | complex. For example: 25 | 26 | input: (ideal (ideal {foo=ideal bar=ideal} ideal) ideal) => 27 | context: (int (float64 {foo=int bar=int64} int) float32) 28 | 29 | Same data as tree views: 30 | 31 | input: => context: 32 | ideal => int 33 | tuple: => tuple: 34 | ideal => float64 35 | struct: => struct: 36 | foo=ideal => foo=int 37 | bar=ideal => bar=int64 38 | ideal => int 39 | ideal => float32 40 | 41 | So what we do is to decompose complex type contexts as we descend AST nodes. 42 | Taking our example from earlier with the call: 43 | 44 | context_type_stack.push( NTupleType(int uint) ) # <-- 1 45 | call resolve_type tuplenode 46 | call resolve_tuple_type tuplenode 47 | ct = context_type_stack.top() 48 | if ct is not NTupleType 49 | error "type mismatch: got tuple where ${ct} is expected" 50 | if ct.len != tuplenode.len 51 | error "type mismatch: ${tuplenode.len} items where ${ct.len} are expected" 52 | for (i = 0; i < tuplenode.len; i++) 53 | itemnode = tuplenode[i] 54 | ct2 = ct[i] 55 | context_type_stack.push(ct2) # <-- 2 56 | call resolve_type itemnode 57 | call resolve_ideal_type itemnode 58 | ct = context_type_stack.top() 59 | if ct is not NBasicType 60 | error "type mismatch: got ideal type where ${ct} is expected" 61 | convert_ideal_type(itemnode, ct) 62 | context_type_stack.pop() # <-- 3 63 | 64 | context_type_stack.pop( NTupleType(int uint) ) # <-- 4 65 | -------------------------------------------------------------------------------- /docs-src/etc/types-and-mutability.md: -------------------------------------------------------------------------------- 1 | - `T` — "owning value" 2 | - `&T` — "ref to immutable value" 3 | - `mut&T` — "ref to mutable value" 4 | 5 | Examples with arrays: 6 | 7 | - `[T N]` is "owning value of comptime-sized array" 8 | - `[T]` is "owning value of runtime-sized array aka slice" 9 | - `&[T N]` is "ref to immutable comptime-sized array" 10 | - `&[T]` is "ref to immutable runtime-sized array aka slice" 11 | - `mut&[T N]` is "ref to mutable comptime-sized array" 12 | - `mut&[T]` is "ref to mutable runtime-sized array aka slice" 13 | 14 | Example function signatures: 15 | 16 | - `fun stash(xs [int])` — takes over ownership 17 | - `fun stash(xs Foo)` — takes over ownership 18 | - `fun join(xs &[int]) str` — borrows a read-only ref 19 | - `fun join(xs &Foo) str` — borrows a read-only ref 20 | - `fun reverse(xs mut&[int])` — borrows a mutable ref 21 | - `fun reverse(xs mut&Foo)` — borrows a mutable ref 22 | -------------------------------------------------------------------------------- /docs-src/etc/x86-64-register-encodings.txt: -------------------------------------------------------------------------------- 1 | Enc 8-bit GP 16-bit GP 32-bit GP 64-bit GP 80-bit x87 64-bit MMX 128-bit XMM 256-bit YMM 16-bit Segment 32-bit Control 32-bit Debug 2 | 0.000 (0) AL AX EAX RAX ST0 MMX0 XMM0 YMM0 ES CR0 DR0 3 | 0.001 (1) CL CX ECX RCX ST1 MMX1 XMM1 YMM1 CS CR1 DR1 4 | 0.010 (2) DL DX EDX RDX ST2 MMX2 XMM2 YMM2 SS CR2 DR2 5 | 0.011 (3) BL BX EBX RBX ST3 MMX3 XMM3 YMM3 DS CR3 DR3 6 | 0.100 (4) AH, SPL1 SP ESP RSP ST4 MMX4 XMM4 YMM4 FS CR4 DR4 7 | 0.101 (5) CH, BPL1 BP EBP RBP ST5 MMX5 XMM5 YMM5 GS CR5 DR5 8 | 0.110 (6) DH, SIL1 SI ESI RSI ST6 MMX6 XMM6 YMM6 - CR6 DR6 9 | 0.111 (7) BH, DIL1 DI EDI RDI ST7 MMX7 XMM7 YMM7 - CR7 DR7 10 | 1.000 (8) R8L R8W R8D R8 - MMX0 XMM8 YMM8 ES CR8 DR8 11 | 1.001 (9) R9L R9W R9D R9 - MMX1 XMM9 YMM9 CS CR9 DR9 12 | 1.010 (10) R10L R10W R10D R10 - MMX2 XMM10 YMM10 SS CR10 DR10 13 | 1.011 (11) R11L R11W R11D R11 - MMX3 XMM11 YMM11 DS CR11 DR11 14 | 1.100 (12) R12L R12W R12D R12 - MMX4 XMM12 YMM12 FS CR12 DR12 15 | 1.101 (13) R13L R13W R13D R13 - MMX5 XMM13 YMM13 GS CR13 DR13 16 | 1.110 (14) R14L R14W R14D R14 - MMX6 XMM14 YMM14 - CR14 DR14 17 | 1.111 (15) R15L R15W R15D R15 - MMX7 XMM15 YMM15 - CR15 DR15 18 | -------------------------------------------------------------------------------- /docs-src/sitemap.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sitemap 3 | --- 4 | 5 | # {{title}} 6 | 7 | {{ 8 | (function visit(p, indent) { 9 | print(`${indent}- [${p.title}](${p.url})\n`) 10 | indent += " " 11 | p.children.forEach(p => visit(p, indent)) 12 | })(site.root, "") 13 | }} 14 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | co-lang.org -------------------------------------------------------------------------------- /docs/etc/arrays-and-ownership.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page has moved 6 | 7 | 8 | 9 | 10 | 11 | This page has moved to a arrays.html 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/etc/elf/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ELF 5 | 6 | 7 | 8 |

Object files

9 | 10 | 15 | 20 | 24 | 27 | 31 | 35 | 36 | 37 |

Program loading and dynamic linking

38 | 39 | 42 | 49 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /docs/etc/elf/ldintro.html: -------------------------------------------------------------------------------- 1 | 2 | Program loading and dynamic linking

3 |

Introduction to Program loading and dynamic linking

4 | This section describes the object file 5 | information and system actions that create running programs. 6 | Some information here applies to all systems; 7 | information specific to one processor resides in 8 | sections marked accordingly. 9 |

10 | Executable and shared object files statically represent programs. 11 | To execute such programs, the system uses the files to create 12 | dynamic program representations, or process images. 13 | As section ''Virtual Address Space'' in Chapter 3 of the 14 | processor supplement describes, a process image has segments that 15 | hold its text, data, stack, and so on. This chapter's major sections 16 | discuss the following: 17 |

    18 |
  • 19 | Program Header. 20 | This section complements Chapter 4, describing 21 | object file structures that relate directly to program execution. 22 | The primary data structure, a program header table, locates 23 | segment images within the file and contains other information 24 | necessary to create the memory image for the program. 25 |
  • 26 | Program Loading. 27 | Given an object file, the system must load 28 | it into memory for the program to run. 29 |
  • 30 | Dynamic linking. 31 | After the system loads the program it must complete 32 | the process image by resolving symbolic references among the object 33 | files that compose the process. 34 |
35 |
36 | NOTE: 37 | The processor supplement defines a naming convention for ELF constants 38 | that have processor ranges specified. Names such as DT_, 39 | PT_, 40 | for processor specific extensions, incorporate the name of the 41 | processor: DT_M32_SPECIAL, for example. 42 | Pre-existing processor 43 | extensions not using this convention will be supported. 44 | 45 | 46 | 47 | 48 | 49 |
Pre-Existing Extensions
DT_JUMP_REL
50 |
51 | Previous 52 | Contents 53 | Next 54 |
55 | 56 | 57 | © 1997, 1998, 1999, 2000 The Santa Cruz Operation, Inc. All rights reserved. 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /docs/etc/elf/string-table.html: -------------------------------------------------------------------------------- 1 | 2 | String Table

3 |

String Table

4 | String table sections hold null-terminated character sequences, 5 | commonly called strings. 6 | The object file uses these strings to represent symbol and section names. 7 | One references a string as an index into the 8 | string table section. 9 | The first byte, which is index zero, is defined to hold 10 | a null character. 11 | Likewise, a string table's last byte is defined to hold 12 | a null character, ensuring null termination for all strings. 13 | A string whose index is zero specifies 14 | either no name or a null name, depending on the context. 15 | An empty string table section is permitted; its section header's sh_size 16 | member would contain zero. 17 | Non-zero indexes are invalid for an empty string table. 18 |

19 | A section header's sh_name 20 | member holds an index into the section header string table 21 | section, as designated by the e_shstrndx 22 | member of the ELF header. 23 | The following figures show a string table with 25 bytes 24 | and the strings associated with various indexes. 25 |

26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
Index+0+1+2+3+4+5+6+7+8+9
0\0name.\0Var
10iable\0able
20\0\0xx\0 
74 |


75 | Figure 4-15: String Table Indexes 76 |

77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
IndexString
0none
1name.
7Variable
11able
16able
24null string
106 |


107 |

108 | As the example shows, a string table index may refer 109 | to any byte in the section. 110 | A string may appear more than once; 111 | references to substrings may exist; 112 | and a single string may be referenced multiple times. 113 | Unreferenced strings also are allowed. 114 |


115 | Previous 116 | Contents 117 | Next 118 |
119 | 120 | 121 | © 1997, 1998, 1999, 2000 The Santa Cruz Operation, Inc. All rights reserved. 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/etc/execution-context-switching.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | execution-context-switching 6 | 7 | 8 | 9 | 10 | 11 |

Execution context switching

12 |

M has a context 13 | T has a context

14 |

m_call is called on a coroutine task T to switch execution to its M.

15 |

go mcall 16 | mcall switches from the g to the g0 stack and invokes fn(g), 17 | where g is the goroutine that made the call. 18 | mcall saves g's current PC/SP in g->sched so that it can be restored later. 19 | It is up to fn to arrange for that later execution, typically by recording 20 | g in a data structure, causing something to call ready(g) later. 21 | mcall returns to the original goroutine g later, when g has been rescheduled. 22 | fn must not return at all; typically it ends by calling schedule, to let the m 23 | run other goroutines.

24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/etc/link-thoughts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | link-thoughts 6 | 7 | 8 | 9 | 10 | 11 |

Thoughts on linking code

12 |

What if instead of object files, we were to maintain a graph database of all 13 | assembled code?

14 |

Traditionally a C-like compiler will parse, compile and assemble each source file into 15 | an ELF/Mach-O/etc object file and 16 | finally—when all object files required for a program are available—link them all together 17 | by reading & parsing all these object files just to build a new object (exe) file. 18 | Here's an example of a simple program with four source files:

19 |
    main
20 |   /  |   \
21 | foo  bar  baz
22 |   \  /
23 |   util
24 | 
25 |

main requires foo, bar and baz. foo and bar both require util. 26 | In practice this is not a tree but a list:

27 |
    28 |
  • main -> main.o
  • 29 |
  • foo -> foo.o
  • 30 |
  • bar -> bar.o
  • 31 |
  • baz -> baz.o
  • 32 |
  • util -> util.o
  • 33 |
34 |

A C-like compiler would link foo, bar, baz, util and main objects everytime any part changes. 35 | Say we only change baz, we take the cost of re-linking the tree of foo, bar & util.

36 |

Imagine if these were represented as a tree even as linked objects, not just temporarily 37 | inside the compiler. Then we could link subtrees together:

38 |
main           = [main.o, foo+util+bar.o, baz.o]
39 | foo+util+bar.o = [foo.o, bar.o, util.o]
40 | foo+util.o     = [foo.o, util.o]         # Can be skipped; unused
41 | foo+util.o     = [bar.o, util.o]         # Can be skipped; unused
42 | 
43 |

If baz changes, we can reuse the subtree object foo+util+bar.o

44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /docs/etc/llvm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | llvm 6 | 7 | 8 | 9 | 10 | 11 |

llvm notes

12 |

To see which features and CPUs that LLVM knows about, we can use llc. For example x86:

13 |
deps/llvm/bin/llvm-as < /dev/null | deps/llvm/bin/llc -march=x86 -mattr=help
14 | 
15 |

Look into using "memory lifetime markers"

16 |

https://llvm.org/docs/LangRef.html#memory-use-markers

17 |
declare void @llvm.lifetime.start(i64 <size>, i8* nocapture <ptr>)
18 | 
19 |

Various useful stuff

20 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/etc/ownership.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Resource ownership 6 | 7 | 8 | 9 | 10 | 11 |

Resource ownership

12 |

Resource ownership rules in Co are simple:

13 |
    14 |
  • Storage locations own their data.
  • 15 |
  • Ownership is transferred only for heap arrays. All other values are copied.
  • 16 |
  • References are pointers to data owned by someone else.
  • 17 |
18 |

When a storage location goes out of scope it relinquishes its ownership by bing "dropped". 19 | When a value is dropped, any heap arrays are deallocated. 20 | Any lingering references to a dropped value are invalid. 21 | Accessing such a reference causes a "safe crash" by panicing in "safe" builds 22 | and has undefined behavior in "fast" builds.

23 |

A "storage location" is a variable, struct field, tuple element, 24 | array element or function parameter.

25 |

All data is passed by value in Co. 26 | Note that references are memory addresses (an integer) and thus technically 27 | copied when passed around.

28 |

See also

29 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /docs/etc/rt-go-sched-parking.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | rt-go-sched-parking 6 | 7 | 8 | 9 | 10 | 11 |

Go scheduler Worker thread parking/unparking

12 |

We need to balance between keeping enough running worker threads to utilize 13 | available hardware parallelism and parking excessive running worker threads 14 | to conserve CPU resources and power. This is not simple for two reasons: 15 | (1) scheduler state is intentionally distributed (in particular, per-P work 16 | queues), so it is not possible to compute global predicates on fast paths; 17 | (2) for optimal thread management we would need to know the future (don't park 18 | a worker thread when a new goroutine will be readied in near future).

19 |

Three rejected approaches that would work badly:

20 |
    21 |
  1. Centralize all scheduler state (would inhibit scalability).

    22 |
  2. 23 |
  3. Direct goroutine handoff. That is, when we ready a new goroutine and there 24 | is a spare P, unpark a thread and handoff it the thread and the goroutine. 25 | This would lead to thread state thrashing, as the thread that readied the 26 | goroutine can be out of work the very next moment, we will need to park it. 27 | Also, it would destroy locality of computation as we want to preserve 28 | dependent goroutines on the same thread; and introduce additional latency.

    29 |
  4. 30 |
  5. Unpark an additional thread whenever we ready a goroutine and there is an 31 | idle P, but don't do handoff. This would lead to excessive thread parking/ 32 | unparking as the additional threads will instantly park without discovering 33 | any work to do.

    34 |
  6. 35 |
36 |

The current approach:

37 |

We unpark an additional thread when we ready a goroutine if (1) there is an 38 | idle P and there are no "spinning" worker threads. A worker thread is considered 39 | spinning if it is out of local work and did not find work in global run queue/ 40 | netpoller; the spinning state is denoted in m.spinning and in sched.nmspinning. 41 | Threads unparked this way are also considered spinning; we don't do goroutine 42 | handoff so such threads are out of work initially. Spinning threads do some 43 | spinning looking for work in per-P run queues before parking. If a spinning 44 | thread finds work it takes itself out of the spinning state and proceeds to 45 | execution. If it does not find work it takes itself out of the spinning state 46 | and then parks.

47 |

If there is at least one spinning thread (sched.nmspinning>1), we don't unpark 48 | new threads when readying goroutines. To compensate for that, if the last spinning 49 | thread finds work and stops spinning, it must unpark a new spinning thread. 50 | This approach smooths out unjustified spikes of thread unparking, 51 | but at the same time guarantees eventual maximal CPU parallelism utilization.

52 |

The main implementation complication is that we need to be very careful during 53 | spinning→non-spinning thread transition. This transition can race with submission 54 | of a new goroutine, and either one part or another needs to unpark another worker 55 | thread. If they both fail to do that, we can end up with semi-persistent CPU 56 | underutilization. The general pattern for goroutine readying is: submit a goroutine 57 | to local work queue, StoreLoad-style memory barrier, check sched.nmspinning.

58 |

The general pattern for spinning→non-spinning transition is:

59 |
    60 |
  • decrement nmspinning,
  • 61 |
  • StoreLoad-style memory barrier,
  • 62 |
  • check all per-P work queues for new work.
  • 63 |
64 |

Note that all this complexity does not apply to global run queue as we are not 65 | sloppy about thread unparking when submitting to global queue. Also see comments 66 | for nmspinning manipulation.

67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /docs/etc/type-context.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | type-context 6 | 7 | 8 | 9 | 10 | 11 |

Type context

12 |

Type resolver 13 | "requested types" version 2

14 |

The idea is that there's always an expected type context. 15 | For example, a call to a function (int bool)->int has the type context (int bool) (a tuple), 16 | while an assignment to a var e.g. var x int = has the type context int.

17 |

Now, with basic types this is pretty straight-forward; when encountering an ideally-typed value 18 | simply convert it to the basic type (if possible.) E.g. x int = y => x int = y as int. 19 | However for complex types like tuples it's a little trickier. Consider the following:

20 |
fun add(x int, y uint) -> x + y
21 | fun main {
22 |   a, b = 2, 4
23 |   # add's parameters => (int uint)
24 |   # input arguments  => (ideal ideal)
25 |   add(a, b)
26 | }
27 | 
28 |

We need to map the input type (ideal ideal) to the context type (int uint). 29 | This is essentially tree matching as the input type and context type may be arbitrarily 30 | complex. For example:

31 |
input:   (ideal (ideal {foo=ideal bar=ideal} ideal) ideal) =>
32 | context: (int (float64 {foo=int bar=int64} int) float32)
33 | 
34 |

Same data as tree views:

35 |
input:            =>    context:
36 |   ideal           =>      int
37 |   tuple:          =>      tuple:
38 |     ideal         =>        float64
39 |     struct:       =>        struct:
40 |       foo=ideal   =>          foo=int
41 |       bar=ideal   =>          bar=int64
42 |     ideal         =>        int
43 |   ideal           =>      float32
44 | 
45 |

So what we do is to decompose complex type contexts as we descend AST nodes. 46 | Taking our example from earlier with the call:

47 |
context_type_stack.push( NTupleType(int uint) )   # <-- 1
48 | call resolve_type tuplenode
49 |   call resolve_tuple_type tuplenode
50 |     ct = context_type_stack.top()
51 |     if ct is not NTupleType
52 |       error "type mismatch: got tuple where ${ct} is expected"
53 |     if ct.len != tuplenode.len
54 |       error "type mismatch: ${tuplenode.len} items where ${ct.len} are expected"
55 |     for (i = 0; i < tuplenode.len; i++)
56 |       itemnode = tuplenode[i]
57 |       ct2 = ct[i]
58 |       context_type_stack.push(ct2)   # <-- 2
59 |       call resolve_type itemnode
60 |         call resolve_ideal_type itemnode
61 |           ct = context_type_stack.top()
62 |           if ct is not NBasicType
63 |             error "type mismatch: got ideal type where ${ct} is expected"
64 |           convert_ideal_type(itemnode, ct)
65 |       context_type_stack.pop()       # <-- 3
66 | 
67 | context_type_stack.pop( NTupleType(int uint) )   # <-- 4
68 | 
69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /docs/etc/types-and-mutability.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | types-and-mutability 6 | 7 | 8 | 9 | 10 | 11 |
    12 |
  • T — "owning value"
  • 13 |
  • &T — "ref to immutable value"
  • 14 |
  • mut&T — "ref to mutable value"
  • 15 |
16 |

Examples with arrays:

17 |
    18 |
  • [T N] is "owning value of comptime-sized array"
  • 19 |
  • [T] is "owning value of runtime-sized array aka slice"
  • 20 |
  • &[T N] is "ref to immutable comptime-sized array"
  • 21 |
  • &[T] is "ref to immutable runtime-sized array aka slice"
  • 22 |
  • mut&[T N] is "ref to mutable comptime-sized array"
  • 23 |
  • mut&[T] is "ref to mutable runtime-sized array aka slice"
  • 24 |
25 |

Example function signatures:

26 |
    27 |
  • fun stash(xs [int]) — takes over ownership
  • 28 |
  • fun stash(xs Foo) — takes over ownership
  • 29 |
  • fun join(xs &[int]) str — borrows a read-only ref
  • 30 |
  • fun join(xs &Foo) str — borrows a read-only ref
  • 31 |
  • fun reverse(xs mut&[int]) — borrows a mutable ref
  • 32 |
  • fun reverse(xs mut&Foo) — borrows a mutable ref
  • 33 |
34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /docs/etc/x86-64-register-encodings.txt: -------------------------------------------------------------------------------- 1 | Enc 8-bit GP 16-bit GP 32-bit GP 64-bit GP 80-bit x87 64-bit MMX 128-bit XMM 256-bit YMM 16-bit Segment 32-bit Control 32-bit Debug 2 | 0.000 (0) AL AX EAX RAX ST0 MMX0 XMM0 YMM0 ES CR0 DR0 3 | 0.001 (1) CL CX ECX RCX ST1 MMX1 XMM1 YMM1 CS CR1 DR1 4 | 0.010 (2) DL DX EDX RDX ST2 MMX2 XMM2 YMM2 SS CR2 DR2 5 | 0.011 (3) BL BX EBX RBX ST3 MMX3 XMM3 YMM3 DS CR3 DR3 6 | 0.100 (4) AH, SPL1 SP ESP RSP ST4 MMX4 XMM4 YMM4 FS CR4 DR4 7 | 0.101 (5) CH, BPL1 BP EBP RBP ST5 MMX5 XMM5 YMM5 GS CR5 DR5 8 | 0.110 (6) DH, SIL1 SI ESI RSI ST6 MMX6 XMM6 YMM6 - CR6 DR6 9 | 0.111 (7) BH, DIL1 DI EDI RDI ST7 MMX7 XMM7 YMM7 - CR7 DR7 10 | 1.000 (8) R8L R8W R8D R8 - MMX0 XMM8 YMM8 ES CR8 DR8 11 | 1.001 (9) R9L R9W R9D R9 - MMX1 XMM9 YMM9 CS CR9 DR9 12 | 1.010 (10) R10L R10W R10D R10 - MMX2 XMM10 YMM10 SS CR10 DR10 13 | 1.011 (11) R11L R11W R11D R11 - MMX3 XMM11 YMM11 DS CR11 DR11 14 | 1.100 (12) R12L R12W R12D R12 - MMX4 XMM12 YMM12 FS CR12 DR12 15 | 1.101 (13) R13L R13W R13D R13 - MMX5 XMM13 YMM13 GS CR13 DR13 16 | 1.110 (14) R14L R14W R14D R14 - MMX6 XMM14 YMM14 - CR14 DR14 17 | 1.111 (15) R15L R15W R15D R15 - MMX7 XMM15 YMM15 - CR15 DR15 18 | -------------------------------------------------------------------------------- /docs/sitemap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sitemap 6 | 7 | 8 | 9 | 10 | 11 |

Sitemap

12 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/array.co: -------------------------------------------------------------------------------- 1 | fun main(arg1 [int 3], arg2 int) int 2 | // a [int 3] 3 | // const K [int 3] = [10,20,30] // explicitly typed; type resolved by parse 4 | const K = [10,20,30] // ideally typed, resolved by type resolver 5 | // const b [int 3] = [0, 1, 2] 6 | 7 | // indexing arrays (parse: done, resolve: done, LLVM: done) 8 | v1 = K[0] 9 | // v2 = K[1:3][2] // error: index 2 out of bounds, accessing array of length 2 10 | // v3 = K[3] // error: index 3 out of bounds, accessing array of length 3 11 | // v4 = K[-2] // error: index 4294967294 out of bounds, accessing array of length 3 12 | // s = K[2/2:6-3] // &[int 2] 13 | // v = s[3] // error: index 3 out of bounds, accessing array of length 2 14 | 15 | // // indexing tuples (parse: done, resolve: done, LLVM: TODO) 16 | // t = (1, 2, 3) 17 | // v5 = t[1] 18 | // // v6 = t[3] // error: no element 3 in tuple with 3 elements 19 | 20 | // slices/refs of arrays (parse: done, resolve: done, LLVM: WIP) 21 | h = &K // &[int 3] 22 | // cts1 = K[:] // &[int 3] 23 | // cts2 = K[0:] // &[int 3] 24 | // // cts3 = K[2:1] // error: invalid slice index: 2 > 1 25 | // // cts4 = K[:4] // error: index 4 out of bounds, slicing array of length 3 26 | // cts5 = K[:2] // &[int 2] 27 | // cts6 = K[1:2] // &[int 1] 28 | // cts7 = K[2:2] // &[int] -- runtime sized rather than [int 0] or nil 29 | // rts1 = K[0:arg1] // &[int] 30 | // cts8 = rts1[:2] // &[int 2] -- upgrade to sized array type 31 | 32 | // error: cannot reference a reference 33 | //var r1 &int 34 | //r2 = &r1 // error: cannot reference a reference (type &int) 35 | //r3 = &gref // error: cannot reference a reference (type &int) 36 | //global var gref &int 37 | 38 | 39 | // // c = [0, 1, 2] 40 | // // c[1] = 3 41 | 42 | // // d = [10, c[1], 10] 43 | // // d[1] = 4 44 | 45 | // // mut e [int 3] = [10,11,12] 46 | // // e[1] = 22 47 | // //var f [int 3] = [100,101,102] 48 | // // f = K // error: array type [int 3] is not assignable 49 | // // g = K // error: array type [int 3] is not assignable 50 | // // h[1] = 9 51 | 52 | 53 | 54 | // K[1] 55 | // K[arg2] 56 | // h[1] 57 | h[arg2] 58 | // arg1[1] 59 | // arg1[arg2] 60 | -------------------------------------------------------------------------------- /example/comptime-buf.w: -------------------------------------------------------------------------------- 1 | 2 | fun hello(s str) { 3 | s1 = Buf(5){} # comptime => struct{data [5]byte; len uint}{{0,0,0,0,0},0} 4 | s3 = Buf("hello") # comptime => struct{data [5]byte; len uint}{"hello",5} 5 | s2 = Buf(s) # runtime 6 | l1 = s1.len() # comptime => 5 7 | l2 = s2.len() # comptime => 5 8 | l3 = s3.len() # runtime 9 | l2.at(0) # comptime => 'h' 10 | l2.at(9) # comptime error: out of bounds 11 | l3.at(0) # runtime 12 | l2.at(9) # runtime panic: out of bounds 13 | } 14 | 15 | interface ByteArray { 16 | data []byte 17 | len uint 18 | } 19 | 20 | fun ByteArray.at(a, index uint) byte -> a.data[index] 21 | fun ByteArray.at(comptime a, comptime index uint) byte -> a.data[index] 22 | 23 | fun Buf(comptime size uint) Type { 24 | struct { 25 | data [size]byte # array, not slice 26 | len uint 27 | } 28 | } 29 | 30 | fun Buf(comptime a [uint]byte) ByteArray { 31 | Buf(len(a)) { 32 | data = a 33 | len = len(a) 34 | } 35 | } 36 | 37 | fun Buf(s str) ByteArray { 38 | st = struct { 39 | data []byte # slice, not array 40 | len uint 41 | }{ 42 | data = memalloc(len(s)) 43 | len = len(s) 44 | } 45 | memcopy(st.data, s) 46 | st 47 | } 48 | -------------------------------------------------------------------------------- /example/comptime-list.w: -------------------------------------------------------------------------------- 1 | 2 | fun main() nil { 3 | ints [10]int32 4 | xs = List(int) { # typeof(xs) => struct { items [int]; len uint } 5 | items = &ints 6 | len = 0 7 | } 8 | } 9 | 10 | fun List(comptime T Type) Type { 11 | struct { 12 | items [T] 13 | len uint 14 | } 15 | } 16 | 17 | # List(int) is equivalent to this: 18 | type IntList struct { 19 | items [int] 20 | len uint 21 | } 22 | -------------------------------------------------------------------------------- /example/consts.w: -------------------------------------------------------------------------------- 1 | const start = 5 2 | const one = 1 3 | 4 | var foo int 5 | const no = false 6 | const t = int 7 | const f = fun -> 1 8 | 9 | fun main { 10 | var x = true 11 | factorial(start) 12 | } 13 | 14 | # Factorial function 15 | fun factorial(n int) t { 16 | const zero = 0 17 | if n == zero { 18 | one 19 | } else { 20 | n * factorial(n - 1) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/context.co: -------------------------------------------------------------------------------- 1 | fun foo 2 | x = allocate([512]u8) // allocated in the ArenaAllocator 3 | x[400] = 4 4 | // drop(x), ArenaAllocator does nothing (no free/reclaim) 5 | 6 | fun main 7 | x = allocate("hello") // allocated in the default heap allocator 8 | context.allocator = ArenaAllocator() 9 | foo() 10 | // drop(x), free memory x points to 11 | // drop(context.allocator), frees all memory of our ArenaAllocator 12 | -------------------------------------------------------------------------------- /example/ex1.c: -------------------------------------------------------------------------------- 1 | int main(int argc, const char** argv) { 2 | printf("hello! argc: %d", argc); 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /example/factorial.w: -------------------------------------------------------------------------------- 1 | # < 2 | # <= 3 | # << 4 | # <<= 5 | # > 6 | # >= 7 | # >> 8 | # >>= 9 | 10 | fun main { 11 | # a, b = 1, 2 + 1 12 | # z = 20 as int8 13 | # a = z as int16 14 | # a = int16(20) 15 | # b = int64(arg0) 16 | # k = x / y * z # oops! Right-associate but should be left-associative 17 | 18 | # a = 1 + 2 # 1 left & right are untyped 19 | # a = 2 + (1 as uint32) # 2 left is untyped, right is typed 20 | # a = (1 as uint32) + 2 # 3 left is typed, right is untyped 21 | # a = (1 as uint32) + (2 as uint32) # 4 left & right are typed 22 | 23 | # a = 4 24 | # b = a 25 | # y = b + 1 26 | 27 | z = if true { 28 | a = 4 # avoid block elimination while working on ir builder 29 | y = a + 1 30 | } else { 31 | 0 32 | } 33 | 34 | z 35 | 36 | # factorial(start) 37 | } 38 | 39 | # fun foo(i int) -> i 40 | 41 | # # Factorial function 42 | # fun factorial(n int) int { 43 | # if n <= 0 { 44 | # 1 45 | # } else { 46 | # n * factorial(n - 1) 47 | # } 48 | # } 49 | 50 | # fun factorial(n float32) float32 { 51 | # if n <= 0.0 { 52 | # 1.0 53 | # } else { 54 | # n * factorial(n - 1.0) 55 | # } 56 | # } 57 | 58 | # fun factorial(n int) int { 59 | # # y = 3 60 | # # x, y, _ = 1, 2, 3 61 | # # t = (1, 2, 3) 62 | # # xs = for x in [1,2,3] { x * 2 } 63 | # # if n <= 0 1 else n * factorial(n - 1) 64 | # if n <= 0 { 65 | # 1 66 | # } else { 67 | # n * factorial(n - 1) 68 | # } 69 | # } 70 | -------------------------------------------------------------------------------- /example/fmt-print-drop-pattern.co: -------------------------------------------------------------------------------- 1 | 2 | type str [u8] 3 | fun fmt_thing(Thing) str 4 | 5 | fun dostuff() { 6 | var thing1 Thing 7 | var thing2 Thing 8 | print("things: %s, %s", fmt_thing(thing1), fmt_thing(thing2)) 9 | something_else() 10 | } 11 | 12 | // ... is equivalent to: 13 | 14 | fun dostuff() { 15 | var thing1 Thing 16 | var thing2 Thing 17 | { 18 | tmp1 = fmt_thing(thing1) 19 | tmp2 = fmt_thing(thing2) 20 | print("things: %s, %s", tmp1, tmp2) 21 | } // tmp1 & tmp2 deallocated here 22 | something_else() 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /example/future-borrow-move.w: -------------------------------------------------------------------------------- 1 | # 2 | # -- This is just an idea-- 3 | # 4 | # Rust-like borrowing and moving, with a twist. 5 | # - Things are borrowed by default 6 | # - Moving can be explicit 7 | # 8 | 9 | type Thing { 10 | x int 11 | } 12 | 13 | fun helperTakes(t Thing) { 14 | t.x = 2 # ok to mutate since we own t 15 | } 16 | 17 | fun f1 { 18 | t = Thing(1) 19 | t.x = 0 # ok to mutate since we own t 20 | helperTakesOver(t) # t moves 21 | } 22 | 23 | fun f2 { 24 | t = Thing(1) 25 | helperTakesOver(t) 26 | y = t # no longer alive; error: t moved to helperTakesOver 27 | } 28 | 29 | fun helperBorrows(t &Thing) { 30 | print(t.x) # reading is okay, but... 31 | # t.x = 2 # ...mutation is not, since we are just borrowing t 32 | } 33 | 34 | fun f3 { 35 | t = Thing(1) 36 | helperBorrows(t) 37 | t.x = 0 # ok to mutate since we still own t 38 | } 39 | 40 | fun f4 { 41 | # borrowing prevents moving 42 | t = Thing(1) 43 | y = &t # y borrows t 44 | helperTakes(t) # error: cannot move t; borrowed by y 45 | } 46 | 47 | fun f5 { 48 | # scope is important 49 | t = Thing(1) 50 | { 51 | y = &t # y borrows t 52 | } 53 | helperTakes(t) # ok; no borrowed refs in scope 54 | } 55 | -------------------------------------------------------------------------------- /example/future-errors.co: -------------------------------------------------------------------------------- 1 | fun read_text_file1() str 2 | f = fs.open("file") // forward error (return imm) 3 | f = fs.open("file") else nil // ignore error 4 | f = fs.open("file") else(err) // handle error 5 | if err == ErrNotFound 6 | return "" 7 | return err 8 | 9 | fun read_text_file2() (str, error) 10 | f = fs.open("file") // forward error (return imm) 11 | f, _ = fs.open("file") else nil // ignore error 12 | f, err = fs.open("file") // handle error 13 | if err == ErrNotFound 14 | return "" 15 | return "", err 16 | 17 | -------------------------------------------------------------------------------- /example/future-ownership.w: -------------------------------------------------------------------------------- 1 | # Exploring a variant of the Rust model 2 | # 3 | # Rust's Ownership Rules: 4 | # - Each value in Rust has a variable that’s called its owner. 5 | # - There can only be one owner at a time. 6 | # - When the owner goes out of scope, the value will be dropped. 7 | # 8 | # https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html 9 | 10 | type User { 11 | id int 12 | name str 13 | emails [str] 14 | } 15 | 16 | fun print(u &User) # borrows u 17 | fun addEmail(u User, email &str) User # takes ownership of u, borrows email, returns u 18 | fun store(u User) # takes ownership of u 19 | 20 | fun example1 { 21 | u = User(id=0, name="sam") # heap-alloc + assign pointer 22 | u = addEmail(u, "sam@hawtmail.com") 23 | print(u) # print borrows u 24 | store(u) # u moves to store(); local u is invalid 25 | print(u) # error! u has moved to store 26 | } 27 | 28 | fun example2 { 29 | u = User(id=0, name="sam") 30 | { 31 | t = timer(fun { 32 | # u in here is an immutable borrowed reference 33 | print(u) # ok; print just reads 34 | store(u) # error! can't move reference u to store 35 | }) 36 | # u is immutable here as a reference has been borrowed 37 | print(u) # ok; print just reads 38 | store(u) # error! can't move borrowed u to store 39 | } 40 | # t is gone thus nothing borrows u anymore; u is mutable and can be moved 41 | store(u) # ok; u moved to store 42 | print(u) # error! u has moved to store 43 | } 44 | -------------------------------------------------------------------------------- /example/future-ownership2.w: -------------------------------------------------------------------------------- 1 | 2 | type User struct { 3 | name str 4 | id int 5 | } 6 | # C equiv: struct User { str name; int id; }; 7 | 8 | var user User # C equiv: User user = {}; 9 | const user User # C equiv: const User user = {}; // immovable; can only be borrowed 10 | var user User? # C equiv: User* user = 0; 11 | var user MutableHandle(User) 12 | # C equiv: User u = {}; struct { User* p; } user = {&u}; 13 | 14 | # Rust's Ownership Rules: 15 | # - Each value in Rust has a variable that’s called its owner. 16 | # - There can only be one owner at a time. 17 | # - When the owner goes out of scope, the value will be dropped. 18 | fun borrowUser(u &User) { 19 | print(u.name) # reading is ok 20 | u.id = 0 # ERROR: u is borrowed; it's immutable 21 | } 22 | fun editUser(u User) User { 23 | u.id = 2 # OK: we own u 24 | return u # move ownership to caller 25 | } 26 | fun takeUser(u User) { 27 | u.id = 0 # OK: we own u 28 | # u is deallocated here 29 | } 30 | fun makeUser() User { 31 | return User(name = "Sam", id = 0) # move ownership to caller 32 | } 33 | fun example() { 34 | var u = makeUser() # OK: u owns the User value 35 | u.name = "Robin" # OK 36 | borrowUser(u) # OK: borrow is always valid (except for dead vars) 37 | u.name = "Sam" # OK 38 | { 39 | var u2 = &u # OK: u2 borrows u 40 | print(u.name) # OK: can read u but not write... 41 | u.name = "Robin" # ERROR: u is immutable since it's currently borrowed (by u2) 42 | takeUser(u) # ERROR: u can't move; it's currently borrowed 43 | } # u2 falls out of scope 44 | u.name = "Robin" # OK: no borrowed refs of u; it's mutable again 45 | u = editUser(u) # OK: move ownership of u and then take back ownership of u 46 | takeUser(u) # OK: ownership of u moves to takeUser 47 | print(u.name) # ERROR: u has moved to takeUser 48 | } 49 | -------------------------------------------------------------------------------- /example/future-type-functions-generics.w: -------------------------------------------------------------------------------- 1 | # generics w required type Name 2 | type Vec3(T) = (T, T, T) 3 | type Tup3(Y) = (Y, Y, Y) 4 | a Vec3(int) = (1,1,0) 5 | b Bec3(float) = (1.0,1.0,0.0) 6 | c = (1,1,0) # == a 7 | d Tup3(float) = (1.0,1.0,0.0) # == b 8 | -------------------------------------------------------------------------------- /example/future-type-variants.w: -------------------------------------------------------------------------------- 1 | # OCaml / ReasonML style types 2 | type Account = Twitter(str) 3 | | Google(str, int) 4 | | Local 5 | | Test 6 | 7 | fun signIn(a Account) { 8 | switch a { 9 | Twitter(handle) -> print "Sign in to twitter as @$handle" 10 | Google(email, id) -> print "Sign in to Google with #$id $email" 11 | Local | Test -> print "Use local computer user" 12 | } 13 | } 14 | 15 | # really just compiles to tuples. The above becomes: 16 | fun signIn(a (int,str)|(int,str,int)|(int)) { 17 | switch a[0] { 18 | case 0: 19 | handle = a[1] 20 | print("Sign in to twitter as @$handle") 21 | case 1: 22 | email, id = a[1:] 23 | print("Sign in to Google with #$id $email") 24 | case 2: case 3: 25 | print("Use local computer user") 26 | } 27 | } 28 | 29 | # ReasonML syntax: 30 | # 31 | # type account = Twitter(string) 32 | # | Google(string, int) 33 | # | Local 34 | # | Test 35 | # 36 | # let a = Twitter("bobby99") 37 | # let b = Google("bob@gmail.com", 123556) 38 | # 39 | # let signIn = switch (a) { 40 | # | Twitter(handle) => "Sign in to twitter as @$handle" 41 | # | Google(email, id) => "Sign in to Google with #$id $email" 42 | # | Local | Test => "Use local computer user" 43 | # }; 44 | # 45 | -------------------------------------------------------------------------------- /example/future-where.w: -------------------------------------------------------------------------------- 1 | # where 2 | # 3 | # Similar to Haskell's "where" 4 | # Similar to Rust's "where" 5 | # 6 | # Note: This may be a bad idea. 7 | # 8 | 9 | fun fmtSyntaxErrors(errors [Error]) { 10 | errors.map(e -> 11 | logger.warn("$severity in $file:$line:$col: $error$snippet") where { 12 | severity = if e.severity == nil "error" else e.severity 13 | line, col, snippet = switch e.loc { 14 | nil -> (0,0,"") 15 | Location(source, line, col) -> { 16 | line, col, switch source.IndexOfNth('\n', line - 1) { 17 | nil -> "" 18 | i -> "\n" + source[i:i+1] 19 | } 20 | } 21 | } 22 | } 23 | ) 24 | } 25 | 26 | fun fmtSyntaxErrors(errors [Error]) { 27 | errors.map(e -> { 28 | severity = if e.severity == nil "error" else e.severity 29 | line, col, snippet = switch e.loc { 30 | nil -> (0,0,"") 31 | Location(source, line, col) -> { 32 | line, col, switch source.IndexOfNth('\n', line - 1) { 33 | nil -> "" 34 | i -> "\n" + source[i:i+1] 35 | } 36 | } 37 | } 38 | logger.warn("$severity in $file:$line:$col: $error$snippet") 39 | }) 40 | } 41 | 42 | # Python-esque 43 | 44 | fun fmtSyntaxErrors(errors [Error]): 45 | errors.map(e -> 46 | logger.warn("$severity in $file:$line:$col: $error$snippet") where: 47 | severity = if e.severity == nil "error" else e.severity 48 | line, col, snippet = switch e.loc: 49 | nil -> (0,0,"") 50 | Location(source, line, col) -> 51 | line, col, switch source.IndexOfNth('\n', line - 1): 52 | nil -> "" 53 | i -> "\n" + source[i:i+1] ) 54 | 55 | 56 | fun fmtSyntaxErrors(errors [Error]): 57 | errors.map(e -> { 58 | severity = if e.severity == nil "error" else e.severity 59 | line, col, snippet = switch e.loc: 60 | nil -> (0,0,"") 61 | Location(source, line, col) -> 62 | line, col, switch source.IndexOfNth('\n', line - 1): 63 | nil -> "" 64 | i -> "\n" + source[i:i+1] 65 | logger.warn("$severity in $file:$line:$col: $error$snippet") 66 | }) 67 | -------------------------------------------------------------------------------- /example/hello.co: -------------------------------------------------------------------------------- 1 | fun add(x, y int) auto { 2 | x + y 3 | } 4 | 5 | // fun print_i32(x int) 6 | 7 | fun main() int { 8 | // a = 12 // equivalent to var a = 12 9 | // b = a 10 | // if a > 5 { 11 | // b = 3 12 | // var a int // explicitly use var to declare new instead of storing to outer a 13 | // } 14 | a = 3 15 | b = 4 16 | r = add(a, b) 17 | // print_i32(r) 18 | r + b // = 11 19 | } 20 | -------------------------------------------------------------------------------- /example/hello/README.md: -------------------------------------------------------------------------------- 1 | This is a package. Build with: 2 | 3 | ./bin/co-debug build example/hello 4 | -------------------------------------------------------------------------------- /example/hello/add.w: -------------------------------------------------------------------------------- 1 | fun add(x int, y uint) int { 2 | x + (y as int) 3 | } 4 | -------------------------------------------------------------------------------- /example/hello/main.w: -------------------------------------------------------------------------------- 1 | fun main() nil { 2 | a = 2 3 | b = a 4 | add(a, b) 5 | } 6 | -------------------------------------------------------------------------------- /example/idealtype.co: -------------------------------------------------------------------------------- 1 | fun foo() { 2 | a = 129 3 | 4 | b = 5 as uint32 5 | b = a + b 6 | 7 | c = 6 as int8 8 | c = c + a 9 | } 10 | 11 | -------------------------------------------------------------------------------- /example/indent.co: -------------------------------------------------------------------------------- 1 | /* 2 | if a < b | = | if a < b { 3 | b = 5 | | b = 5 } 4 | —————————————————————————————————————— 5 | if a < b 6 | ^ 7 | 1. insert semicolon 8 | 9 | b = 5 10 | ^^ 11 | 2. finds indentation — push block 12 | 13 | b = 5 14 | ^ 15 | 3. insert semicolon 16 | 17 | ^ 18 | 4. finds dedentation — pop block 19 | 20 | */ 21 | fun main() 22 | a = 3 23 | b = 4 24 | if a < b 25 | b = 5 26 | a + b 27 | -------------------------------------------------------------------------------- /example/macro.co: -------------------------------------------------------------------------------- 1 | fun add(x, y T) R { 2 | x + y 3 | } 4 | 5 | fun main { 6 | x = 4 as u32 7 | y = 6 as u32 8 | add(x, y) 9 | } 10 | -------------------------------------------------------------------------------- /example/mem.w: -------------------------------------------------------------------------------- 1 | # comment 2 | 3 | 4 | # const lol int = 5 5 | # var foo Foo 6 | # var red, green int = 4, 5 7 | # var x int = 8 8 | # var A, B, C int 9 | # var a, b int = 1, 2 10 | # var r, g, b = 255, 128, 5 11 | # r, g, b = 255, 128, (g = 5) 12 | 13 | # fun lol(int, int32, Foo) int64 14 | # var f fun(int, int32) int64 15 | 16 | const start = 5 17 | 18 | fun main { 19 | # var x = 1 20 | factorial(start) 21 | } 22 | 23 | fun factorial(n int) int { 24 | if n == 0 { 25 | 1 26 | } else { 27 | n * factorial(n - 1) 28 | } 29 | } 30 | 31 | # fun multiply(x, y int, z int32) int { 32 | # if x > y { 33 | # x * y * z 34 | # } else if x == 0 { 35 | # return 8 36 | # } else { 37 | # x / y * z 38 | # } 39 | # } 40 | 41 | 42 | 43 | # z = { x = 6; 5 * x } 44 | 45 | # # r, g, b = 255, 128, g = 5 # invalid: 46 | # # (Assign = 47 | # # (ExprList 48 | # # (Ident r) 49 | # # (Ident g) 50 | # # (Ident b)) 51 | # # (Assign = 52 | # # (ExprList 53 | # # (Int 255) 54 | # # (Int 128) 55 | # # (Ident g)) 56 | # # (Int 5))) 57 | 58 | # 4 + # let's add four 59 | # 5 + # and five to 60 | # 6 # six 61 | # foo + bar * baz 62 | 63 | # oändlig # C3 A4 64 | # 😀 = 1337 65 | 66 | # # !$lol; int # another comment 67 | # # foo * bar + 8 68 | # # const lol, foo, bar = 9, 7, 0 69 | # # var cat = 6 70 | # # x ++ 71 | # # y -- 72 | # # 3 * 9 73 | # # -1 + 5 74 | 75 | # fun multiply (x, y int, z i32) int { 76 | # x * y * z 77 | # } 78 | 79 | # # multiply = (x, y int, z i32) -> { 80 | # # x * y * z 81 | # # } 82 | 83 | # # fun map(c Collection, f (T,int)->str) str 84 | # # fun map(c Collection, f fun(T,int)str) str 85 | 86 | # # names = map(entries, (entry, index) -> entry.name) 87 | -------------------------------------------------------------------------------- /example/minimeow.w: -------------------------------------------------------------------------------- 1 | # scalar int, float (address can be modeled on int) 2 | # compound array, slice, enum, struct 3 | # function identity = name + arg types) 4 | # flow control if...else, for, goto, switch 5 | 6 | var names [4]str # array of strings; cap=4, len=0 7 | # C equiv: str names[4]; 8 | 9 | var days = [byte]('M','T','W','T','F','S','S') # slice of bytes; cap=7, len=7 10 | # C equiv: 11 | # struct { byte p[7]; uint len, refs; } days_a = {{'M','T','W','T','F','S','S'}, 7, 1}; 12 | # struct byteslice { byte* ptr; uint cap, len; } days = {days_a, 7, 7}; 13 | 14 | var name = "Sam" 15 | # C equiv: struct { const char* p; uint len, refs; } name = {"Sam", 3, 1}; 16 | 17 | var nameBytes = []byte("Sam") 18 | # C equiv: 19 | # struct { byte p[7]; uint len, refs; } nameBytes_a = {{'S','a','m'}, 3, 1}; 20 | # struct byteslice { byte* ptr; uint cap, len; } nameBytes = {nameBytes_a, 3, 3}; 21 | 22 | type User struct { 23 | name str 24 | id int 25 | } 26 | # C equiv: struct User { str name; int id; }; 27 | 28 | 29 | # ocaml-like union enum type 30 | type Account enum { 31 | DummyAccount 32 | UserAccount User 33 | TwitterAccount str 34 | POSIXAccount uid, gid int # parameter names are merely comments 35 | } 36 | # C equivalent: 37 | # struct Account { 38 | # int type; // 0 | 1 | 2 | 3 39 | # union { 40 | # User* user; // when type == 1 41 | # char* str; // when type == 2 42 | # int pair[2]; // when type == 3 43 | # } u; 44 | # }; 45 | 46 | fun name(a Account) str { 47 | switch a { 48 | DummyAccount "Dummy" 49 | TwitterAccount(handle) handle 50 | POSIXAccount(uid, gid) "${uid}:${gid}" 51 | } 52 | } 53 | 54 | fun greet(name str, a Account) { 55 | var i = 3 56 | for i-- > 0 { 57 | print("Hello $name. Your account is ${a.name()}") 58 | } 59 | } 60 | 61 | greet("Sam", TwitterAccount("sam")) 62 | 63 | # ---------------- 64 | 65 | type BinaryTree enum { 66 | Leaf T 67 | Tree BinaryTree, BinaryTree 68 | } 69 | # C equivalent: 70 | # struct BinaryTree { 71 | # int type; // 0 | 1 72 | # union { 73 | # T leaf; // when type == 0 74 | # struct BinaryTree* LR[2]; // when type == 1 75 | # } u; 76 | # }; 77 | 78 | -------------------------------------------------------------------------------- /example/multi-const.co: -------------------------------------------------------------------------------- 1 | // multiple constants 2 | const ( 3 | FLAG1 = 1 << iota // 1 << 0 4 | FLAG2 // 1 << 1 5 | FLAG3 // 1 << 2 6 | ) 7 | -------------------------------------------------------------------------------- /example/namespace-idea-buffer.w: -------------------------------------------------------------------------------- 1 | # This example uses a `ns name {...}` syntax for setting the namespace scope 2 | 3 | fun main { 4 | b1 = Buffer("hello") 5 | b1.len() # => 5 6 | b2 Buffer 7 | b2.len() # => 0 since it's zero initialized 8 | } # b1.drop() and b2.drop() called here 9 | 10 | struct Buffer { # defining a struct also defines a namespace 11 | data []byte 12 | len uint 13 | } 14 | 15 | fun Buffer(s str) { 16 | Buffer { 17 | data = memcopy(s) 18 | len = s.len() 19 | } 20 | } 21 | 22 | # We can explicitly declare stuff with namespaces: 23 | fun Buffer.drop(b) { 24 | # called when the buffer drops out of scope, Rust style 25 | if b.cap > 0 26 | memfree(b.data) 27 | } 28 | 29 | # enter the namespace "struct Buffer" 30 | ns Buffer { 31 | # Now things declared here are implicitly in the "Buffer" namespace 32 | 33 | fun cap(&b) -> b.data.cap() # == fun Buffer.cap(&b) uint 34 | fun at(&b, uint index) -> b.data[index] # == fun Buffer.at(&b, uint index) byte 35 | 36 | fun append(mut &b, c byte) nil { 37 | if b.cap - b.len == 0 { 38 | b.data = memrealloc(b.data, b.cap * 2) 39 | } 40 | b.data[b.len] = c 41 | b.len++ 42 | } 43 | 44 | # We can even make nested struct types this way; Buffer.View 45 | struct View { 46 | buf &Buffer 47 | offs uint 48 | } 49 | 50 | fun View(&b, offs uint) { # == Buffer.View(&b, offs uint) View 51 | assert(offs < b.len) 52 | View { buf = b; offs = offs } 53 | } 54 | 55 | ns View { 56 | fun len(&v) -> v.buf.len() - v.offs 57 | fun cap(&v) -> v.buf.cap() - v.offs 58 | fun buffer(&v) &Buffer -> &v.buf 59 | } 60 | 61 | # we can enter arbitrary namespaces. (This may be a bad idea.) 62 | ns foo { 63 | # Buffer.foo 64 | ns bar.baz { 65 | # Buffer.foo.bar.baz 66 | name = "fred" # Buffer.foo.bar.baz.nametrix 67 | fun add(x, y int) -> x + y # Buffer.foo.bar.baz.add(int,int) -> int 68 | # [?] It's a little weird that this fun does not take a Buffer "self" arg... but, 69 | # if it did, it would be confusing as hell since e.g. Buffer.View.cap doesn't. 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /example/ownership_you_can_count_on.co: -------------------------------------------------------------------------------- 1 | // https://researcher.watson.ibm.com/researcher/files/us-bacon/Dingle07Ownership.pdf 2 | // https://vale.dev/blog/generational-references 3 | 4 | fun make_nstr(pattern str, n int) str { 5 | s = "" // s owns str 6 | for i = 0; i < n; i++ { 7 | s += pattern 8 | } 9 | return s // ownership of str transfers to caller 10 | } 11 | 12 | fun count_chars(s str) { // s is borrowed 13 | i = 0 14 | for ; i < len(s); i++ {} 15 | return i 16 | } 17 | 18 | fun main() int { 19 | msg = make_nstr("ha ", 3) // ownership of str transferred to msg 20 | count_chars(msg) // lends a reference of str to callee 21 | } 22 | -------------------------------------------------------------------------------- /example/refs.co: -------------------------------------------------------------------------------- 1 | 2 | // fun main(arg1 &int, arg2 int) int 3 | // *arg1 4 | 5 | fun foo(x, y &int) int 6 | *x + *y 7 | 8 | fun main(arg1 int) int 9 | x = &arg1 10 | x = &arg1 // make mutable 11 | foo(&arg1, x) 12 | *x 13 | 14 | // var x int = 1 15 | // x = 2 16 | 17 | // fun main(arg1 int) int 18 | // x = &arg1 // mut&int 19 | // y = &arg1 // mut&int 20 | // a = *x // int 21 | // b = *y // int 22 | // a + b 23 | -------------------------------------------------------------------------------- /example/soa-sketch.co: -------------------------------------------------------------------------------- 1 | 2 | 3 | for b in bullets 4 | b.position.x += b.velocity.x // ILP pipe A 5 | b.position.y += b.velocity.y // ILP pipe B 6 | 7 | struct Vec2 { x, y f32 } 8 | 9 | struct Bullet1 { 10 | position Vec2 11 | velocity Vec2 12 | name str 13 | } 14 | bullets1 = [Bullet1(), Bullet1(), Bullet1()] 15 | // ↓ memory layout 16 | // 0 4 8 12 16 24 28 32 36 40 48 52 56 60 64 17 | // posx,posy,velx,vely,name,posx,posy,velx,vely,name,posx,posy,velx,vely,name 18 | // A1 B1 A1 B1 A2 B2 A2 B2 A3 B3 A3 B3 19 | // ↑ ILP pipe + iteration 20 | // 1. A: *0 + *8 B: *4 + *12 21 | // 2. A: *24 + *32 B: *28 + *36 22 | // 3. A: *48 + *56 B: *52 + *60 23 | 24 | struct Bullet2 { 25 | position [Vec2 3] 26 | velocity [Vec2 3] 27 | name [str 3] 28 | } 29 | bullets2 = Bullet2() 30 | // ↓ memory layout 31 | // 0 4 8 12 16 20 24 28 32 36 40 44 48 56 64 32 | // posx,posy,posx,posy,posx,posy,velx,vely,velx,vely,velx,vely,name,name,name 33 | // A1 B1 A2 B2 A3 B3 A1 B1 A2 B2 A3 B3 34 | // ↑ ILP pipe + iteration 35 | // 1. A: *0 + *8 B: *4 + *12 36 | // 2. A: *16 + *24 B: *20 + *28 37 | // 3. A: *32 + *40 B: *36 + *44 38 | 39 | struct Bullet3 { 40 | x [struct { pos, vel f32 } 3] 41 | y [struct { pos, vel f32 } 3] 42 | name [str 3] 43 | } 44 | bullets3 = Bullet3() 45 | // ↓ memory layout 46 | // 0 4 8 12 16 20 24 28 32 36 40 44 48 56 64 47 | // posx,velx,posy,vely,posx,velx,posy,vely,posx,velx,posy,vely,name,name,name 48 | // A1 A1 B1 B1 A2 A2 B2 B2 A3 A3 B3 B3 49 | // ↑ ILP pipe + iteration 50 | // 1. A: *0 + *4 B: *8 + *12 51 | // 2. A: *16 + *20 B: *24 + *28 52 | // 3. A: *32 + *36 B: *40 + *44 53 | 54 | 55 | -------------------------------------------------------------------------------- /example/struct.co: -------------------------------------------------------------------------------- 1 | x int = 3 2 | 3 | type Spaceship 4 | a int //= x 5 | b uint 6 | c i64 7 | 8 | // A = Spaceship() 9 | 10 | fun foo( 11 | a int 12 | b uint 13 | c i64 14 | ) int 15 | x = 4 16 | B = Spaceship(c = 1, a = 5) 17 | B.a 18 | 19 | // ——————————————————————————————————————————————————————— 20 | 21 | /*// // Bridge = struct { occupants int } 22 | // type Bridge 23 | // r int 24 | // v int 25 | // occupants int 26 | 27 | type Spaceship 28 | shields u8 = 0 29 | engine 30 | fuel u64 = 1000 31 | output int = int(fuel / 2) 32 | 33 | // fun Spaceship() Spaceship 34 | // // custom constructor 35 | // Spaceship(shields = 1.0) 36 | 37 | // fun len(s Spaceship) auto 38 | // s.occupants 39 | 40 | A = Spaceship() 41 | // B = Spaceship() 42 | 43 | fun main() int 44 | B = Spaceship() 45 | B.engine.output 46 | // b = Bridge() 47 | // B.engine.output = 3 48 | // s = Spaceship() 49 | // s.engine.fuel 50 | // s.occupants 51 | // s.len // WIP 52 | */ -------------------------------------------------------------------------------- /example/template-functions-idea.co: -------------------------------------------------------------------------------- 1 | fun add(x, y auto) typeof(x) 2 | x + y 3 | 4 | fun push(xs []auto, elements ...auto) typeof(xs) 5 | append(xs, elements...) 6 | 7 | fun main 8 | add(u32(4), u32(6)) // => u32 10 9 | add(4.0, 6.0) // => f32 10.0 10 | xs = [3]u16(1, 2, 3) 11 | xs = push(xs, 4, 5) 12 | 13 | // generated function instances: 14 | // fun add(x, y u32) u32 -> x + y 15 | // fun add(x, y f32) f32 -> x + y 16 | // fun push(xs []u16, elements ...u16) []u16 -> append(xs, elements...) 17 | -------------------------------------------------------------------------------- /example/test-array-index.co: -------------------------------------------------------------------------------- 1 | const K = [10,20,30] 2 | 3 | fun index_constarray_ct1() int 4 | K[1] // constant int 20 5 | 6 | fun index_constarray_ct2() int 7 | i = 1 8 | K[i] // constant int 20 9 | 10 | fun index_constarray_rt1(arg1 int) int 11 | K[arg1] // gep load 12 | 13 | fun index_constarray_rt2() int 14 | var i int 15 | i = 1 // make i mutable 16 | K[i] // gep load 17 | 18 | fun index_constarrayref_ct() int 19 | r = &K // &[int 3] 20 | r[1] // constant int 20 21 | 22 | fun index_constarrayref_ct(arg1 int) int 23 | r = &K // &[int 3] 24 | r[arg1] // gep load 25 | 26 | fun index_mutarrayref_ct() int 27 | a = [10,20,30] 28 | r = &a // mut&[int 3] 29 | r[1] // gep load (not const since a is mut) 30 | 31 | fun index_mutarrayref_rt(arg1 int) int 32 | a = [10,20,30] 33 | r = &a // mut&[int 3] 34 | r[arg1] // gep load 35 | 36 | fun index_mutarrayref_explicit_deref_rt(arg1 int) int 37 | a = [10,20,30] 38 | r = &a // mut&[int 3] 39 | (*r)[arg1] // gep load 40 | 41 | fun index_argarray_ct(arg1 [int 3]) int 42 | arg1[1] // extractvalue 43 | 44 | fun index_argarray_rt(arg1 [int 3], arg2 int) int 45 | arg1[arg2] // gep load 46 | 47 | fun index_argarray_ref_ct(arg1 &[int 3]) int 48 | arg1[1] // extractvalue 49 | 50 | fun index_argarray_ref_rt(arg1 &[int 3], arg2 int) int 51 | arg1[arg2] // gep load 52 | 53 | fun index_argarray_mutref_ct(arg1 mut&[int 3]) int 54 | arg1[1] // extractvalue 55 | 56 | fun index_argarray_mutref_rt(arg1 mut&[int 3], arg2 int) int 57 | arg1[arg2] // gep load 58 | 59 | fun index_tuple_ct() int 60 | t = (1, 2, 3) 61 | t[1] // constant int 2 62 | 63 | fun index_tuple_rt() int 64 | t = (1, 2, 3) 65 | t = (11, 22, 33) // force t to become a mutable local 66 | t[1] // gep load 67 | -------------------------------------------------------------------------------- /example/test-array-init-and-copy.co: -------------------------------------------------------------------------------- 1 | fun main() int 2 | // a [int 3] 3 | const K = [0,1,2] 4 | // const b [int 3] = [ 5 | // 0, 6 | // 1 7 | // 2,] 8 | 9 | c = [0, 1, 2] 10 | c[1] = 3 11 | 12 | d = [10, c[1], 10] 13 | d[1] = 4 14 | 15 | b [int 3] = [100,101,102] 16 | b = K // copy K to b 17 | 18 | b[1] 19 | -------------------------------------------------------------------------------- /example/test-const-default-struct-init.co: -------------------------------------------------------------------------------- 1 | type Spaceship 2 | shields u8 = 0 3 | engine 4 | fuel u64 = 1000 5 | output int = int(fuel / 2) 6 | 7 | B = Spaceship() 8 | 9 | fun main() int 10 | B.engine.output 11 | 12 | // expected_exit_status 500 13 | -------------------------------------------------------------------------------- /example/test-const-tuple-access.co: -------------------------------------------------------------------------------- 1 | fun foo() int 2 | z = { a, b = u8(1), 2 } // constant tuple of type "(i8 i32)" 3 | z[1] // => 2 4 | 5 | fun main() int 6 | foo() // => 2 7 | -------------------------------------------------------------------------------- /example/test-error-var-init-type.co: -------------------------------------------------------------------------------- 1 | fun foo() int 2 | x i8 = 3 3 | y int = x // error: incompatible initializer type i8 for var of type int 4 | y 5 | -------------------------------------------------------------------------------- /example/test-multi-assign.co: -------------------------------------------------------------------------------- 1 | fun foo(x, y int) int 2 | y, b = x, y 3 | // y is now value of x 4 | // b is value of y before assignment 5 | y = 7 6 | y * b 7 | 8 | fun main() int 9 | foo(1, 2) 10 | 11 | // expected_exit_status 14 12 | -------------------------------------------------------------------------------- /example/test-named-arguments.co: -------------------------------------------------------------------------------- 1 | fun foo(a, b int, c uint) int 2 | fun bar(int, int, uint) int 3 | 4 | fun main() int 5 | foo(1, 2, 3) // ok 6 | foo(1, c=3, b=2) // ok 7 | bar(1, 2, 3) // ok 8 | // bar(1, c=3, b=2) // error: cannot call with named parameters 9 | // foo(1, 2) // error: missing argument 10 | // foo(1, 2, 3, 4) // error: extra argument 11 | // foo(1, c=3, c=2) // error: duplicate arguments 12 | // foo(1, c=3, x=2) // error: no parameter named "x" 13 | -------------------------------------------------------------------------------- /example/test-nested-struct-access.co: -------------------------------------------------------------------------------- 1 | type Spaceship 2 | shields f32 3 | engine 4 | fuel int 5 | output int 6 | 7 | B = Spaceship() 8 | 9 | fun main() int 10 | B.engine.output 11 | 12 | // expected_exit_status 0 13 | -------------------------------------------------------------------------------- /example/test-var-shadowing.co: -------------------------------------------------------------------------------- 1 | fun a() int 2 | const z = 1 3 | z = 2 // implicit definition shadows previous z (warning issued) 4 | 5 | fun b() int 6 | const z = 1 7 | var z = 2 // explicit definition shadows previous z (no warning) 8 | -------------------------------------------------------------------------------- /example/top-level-init.w: -------------------------------------------------------------------------------- 1 | # order-independent 2 | β = add(α, 10) 3 | α = add(12, 34) 4 | 5 | fun main() int { 6 | α + β 7 | } 8 | 9 | fun add(x int, y uint) int { 10 | x + (y as int) 11 | } 12 | -------------------------------------------------------------------------------- /example/type-buffer.w: -------------------------------------------------------------------------------- 1 | # This example shows a byte buffer type and is _inspired_ by Rust's 2 | # memory mamangement. Rust's Ownership Rules are as follows: 3 | # - Each value in Rust has a variable that’s called its owner. 4 | # - There can only be one owner at a time. 5 | # - When the owner goes out of scope, the value will be dropped. 6 | # However, this example does not use references and mutable references 7 | # like Rust does. Instead it uses a simpler less restrictive model: 8 | # - Moving a thing is explicitly defined by an ampersand on an argument 9 | # - Without an ampersand things are referenced/borrowed 10 | 11 | fun main { 12 | b1 = Buffer("hello") 13 | b1.len() # => 5 14 | b2 = b1 # value of b1 moved to b2 15 | # b1 is invalid here 16 | log("first byte of buffer: %v", first_byte(b2)) 17 | print_buf(b2) # b2 moves into print_buf 18 | } 19 | 20 | fun first_byte(a ByteArray) byte -> a[0] 21 | 22 | fun set_first_byte(a mut ByteArray, b byte) { 23 | a[0] = b 24 | } 25 | 26 | fun print_buf(b &Buffer) { # & here means b takes over ownership 27 | for i = 0; i < b.len(); i++ { 28 | print("b[%v] = %v", i, b[i]) 29 | } 30 | } # b.drop() called here 31 | 32 | interface ByteArray { 33 | [uint index] byte 34 | } 35 | 36 | type Buffer { 37 | data []byte 38 | len uint 39 | cap uint 40 | } 41 | 42 | fun Buffer(s str) { 43 | .len = s.len() 44 | .cap = .len 45 | .data = memalloc(.len) 46 | memcopy(.data, s) 47 | } 48 | 49 | fun Buffer.drop() { 50 | # called when the buffer drops out of scope, Rust style 51 | memfree(.data) 52 | } 53 | 54 | fun Buffer.len() -> .len 55 | fun Buffer.cap() -> .cap 56 | 57 | # implementing [uint]->byte makes Buffer the shape of ByteArray 58 | fun Buffer.[uint index] -> .data[index] 59 | 60 | fun Buffer.append(b byte) nil { 61 | if .cap - .len == 0 { 62 | .data = memrealloc(.data, .cap * 2) 63 | } 64 | .data[.len] = b 65 | .len++ 66 | } 67 | -------------------------------------------------------------------------------- /example/var-let-const.co: -------------------------------------------------------------------------------- 1 | // "var" to make a binding mutable FAVORITE 2 | // global scope can only hold immutable vars (constants) 3 | A = 3 // immutable ideally-typed var 4 | B i8 = 3 // immutable int8-typed var 5 | var C = 3 // mutable int-typed var 6 | fun foo(x int) { // x is mutable(?) int-typed var 7 | a = 1 // immutable ideally-typed var 8 | a = 5 // immtuable ideally-typed var (shadows) 9 | var b = 2 // mutable int-typed var 10 | c, d u8 = 3, 4 // immutable int-typed vars 11 | e int = 2 // immutable int-typed var 12 | a++ // error: increment of immutable var 13 | b++ // store result of b + 1 to var b 14 | } 15 | //————————————————————————————————————————————————————— 16 | // "const" and implicit var (Python style) FAVORITE 17 | const A = 3 // ideally-typed constant 18 | const B i8 = 3 // int8-typed constant 19 | C = 3 // [alt1] mutable int-typed var 20 | C = 3 // [alt2] error: no global vars (only consts at pkg level) 21 | C = 3 // [alt3] int-typed constant (only consts at pkg level) 22 | fun foo(x int) { // x is int-typed var 23 | a = 1 // int-typed var 24 | b int = 2 // int-typed var 25 | c, d u8 = 3, 4 // u8-typed vars 26 | e = u32(2) // u32-typed var 27 | f = 6 // int-typed var 28 | c = 5 // assign to var c 29 | } 30 | //————————————————————————————————————————————————————— 31 | // "const" and "var" 32 | const A = 3 // ideally-typed constant 33 | const B i8 = 3 // int8-typed constant 34 | var c = 3 // int-typed var 35 | fun foo(x int) { // x is int-typed var 36 | var a = 1 // int-typed var 37 | var b int = 2 // int-typed var 38 | var c, d u8 = 3, 4 // int-typed vars 39 | var e = u32(2) // u32-typed var 40 | f = 6 // error: unknown identifier f 41 | c = 5 // assign to var c 42 | } 43 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | cd "$(dirname "$0")" 4 | . misc/_common.sh 5 | 6 | [ "$1" != "-quiet" ] || OPT_QUIET=true 7 | 8 | 9 | _init_githooks() { 10 | _log "---- git hooks ----" 11 | if [ -d .git ] && [ -d misc/git-hooks ]; then 12 | mkdir -p .git/hooks 13 | _pushd .git/hooks 14 | for f in ../../misc/git-hooks/*.sh; do 15 | HOOKFILE=$(basename "$f" .sh) 16 | if ! [ -f "$HOOKFILE" ]; then 17 | ln -vfs "$f" "$HOOKFILE" 18 | fi 19 | done 20 | _popd 21 | fi 22 | } 23 | 24 | 25 | _init_ckit() { 26 | local CKIT_GIT_BRANCH=main 27 | local CKIT_DIR=$DEPS_DIR/ckit 28 | _log "---- ckit ----" 29 | 30 | _git_pull_if_needed \ 31 | https://github.com/rsms/ckit.git \ 32 | "$CKIT_DIR" \ 33 | "$CKIT_GIT_BRANCH" || true 34 | 35 | _log "Using ckit at $(_relpath "$CKIT_DIR")" 36 | 37 | # check PATH 38 | local inpath=false 39 | if ! _in_PATH "$CKIT_DIR/bin" || 40 | [ "$(command -v ckit 2>/dev/null)" != "$CKIT_DIR/bin/ckit" ] 41 | then 42 | local CKIT_DIR_NICE=$CKIT_DIR 43 | case "$CKIT_DIR_NICE" in 44 | "$HOME"/*) CKIT_DIR_NICE="\$HOME${CKIT_DIR_NICE:${#HOME}}" 45 | esac 46 | _log "Please add ckit to PATH:" 47 | _log " export PATH=\"$CKIT_DIR_NICE/bin:\$PATH\"" 48 | fi 49 | } 50 | 51 | 52 | _init_jemalloc() { 53 | local JEMALLOC_GIT_BRANCH=main 54 | local JEMALLOC_DIR=$DEPS_DIR/jemalloc 55 | _log "---- jemalloc ----" 56 | 57 | _git_pull_if_needed \ 58 | https://github.com/rsms/ckit-jemalloc.git \ 59 | "$JEMALLOC_DIR" \ 60 | "$JEMALLOC_GIT_BRANCH" || true 61 | 62 | _log "Using jemalloc at $(_relpath "$JEMALLOC_DIR")" 63 | } 64 | 65 | 66 | _init_llvm() { 67 | _log "---- LLVM ----" 68 | local args=() 69 | if $OPT_QUIET; then 70 | args+=(-quiet) 71 | fi 72 | $SHELL misc/build-llvm.sh "${args[@]}" 73 | } 74 | 75 | 76 | _init_githooks 77 | _init_ckit 78 | _init_jemalloc 79 | _init_llvm 80 | -------------------------------------------------------------------------------- /misc/arm64/.gitignore: -------------------------------------------------------------------------------- 1 | *.qcow2 2 | qemu-arm64.sh 3 | README.md 4 | QEMU_EFI.fd 5 | -------------------------------------------------------------------------------- /misc/arm64/howto.txt: -------------------------------------------------------------------------------- 1 | Get files from https://github.com/rsms/qemu-macos-x86-arm64 2 | 3 | Terminal 1: 4 | qemu-arm64.sh 5 | 6 | Terminal 2: 7 | # wait for qemu-arm64.sh to finish launching, then: 8 | sshfs root@localhost: ~/src/co/a64 -f -p 10022 -o sshfs_sync -o cache=no 9 | 10 | Terminal 3: (optional) 11 | ssh root@localhost -p10022 12 | -------------------------------------------------------------------------------- /misc/build-libcxx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | cd "$(dirname "$0")/.." 4 | . misc/_common.sh 5 | SRCDIR=$PWD 6 | DESTDIR=$PWD/deps/libcxx 7 | BUILDDIR=$PWD/out/libcxx 8 | 9 | echo "SRCDIR $SRCDIR" 10 | echo "DESTDIR $DESTDIR" 11 | echo "BUILDDIR $BUILDDIR" 12 | 13 | rm -rf "$DESTDIR" 14 | mkdir -p "$BUILDDIR" "$DESTDIR" 15 | 16 | # Copy headers & sources for libcxx, libcxxabi, libunwind 17 | for component in libcxx libcxxabi libunwind; do 18 | cd "$DEPS_DIR/llvm-src/$component" 19 | 20 | DEST_INC_DIR=$DESTDIR/$component 21 | if [ "$component" = "libcxx" ]; then 22 | DEST_INC_DIR=$DESTDIR 23 | fi 24 | 25 | # include 26 | echo "copy $PWD/include/* -> $DEST_INC_DIR/include/*" 27 | mkdir -p "$DEST_INC_DIR/libunwind" 28 | for f in $(find include -not -name CMakeLists.txt); do 29 | if [ -d "$f" ]; then 30 | mkdir -p "$DEST_INC_DIR/$f" 31 | else 32 | cp -a $f "$DEST_INC_DIR/$f" 33 | fi 34 | done 35 | 36 | # src 37 | echo "copy $PWD/src/* -> $DESTDIR/$component/src/*" 38 | mkdir -p "$DESTDIR/$component" 39 | cp LICENSE.txt "$DESTDIR/$component/LICENSE.txt" 40 | for f in $(find src -not -name CMakeLists.txt -and -not -path 'src/support/win32*'); do 41 | if [ -d "$f" ]; then 42 | mkdir -p "$DESTDIR/$component/$f" 43 | else 44 | cp -a $f "$DESTDIR/$component/$f" 45 | fi 46 | done 47 | done 48 | 49 | # create Makefile from template misc/libcxx.make 50 | echo "# generated by $(basename "$0"), $(date)" > "$BUILDDIR"/Makefile 51 | 52 | sed -E 's:@DEPSDIR@:'"$SRCDIR"'/deps:g' "$SRCDIR"/misc/libcxx.make \ 53 | | sed -E 's:@DESTDIR@:'"$DESTDIR"':g' \ 54 | >> "$BUILDDIR"/Makefile 55 | 56 | LIBCXX_OBJS= 57 | LIBCXXABI_OBJS= 58 | 59 | cd "$DESTDIR"/libcxx/src 60 | for f in $(find . -type f -name '*.cpp'); do 61 | f=${f:2} # ./foo/bar.cpp -> foo/bar.cpp 62 | objf=${f:0:$(( ${#f} - 4 ))}.o # foo/bar.cpp -> foo/bar.o 63 | LIBCXX_OBJS="$LIBCXX_OBJS $objf" 64 | if [[ "$f" == *"/"* ]]; then 65 | cat << EOF >> "$BUILDDIR"/Makefile 66 | libcxx/$objf: \$(DESTDIR)/libcxx/src/$f 67 | @mkdir -p \$(dir \$@) 68 | \$(CXXC) \$(CFLAGS) \$(CFLAGS_LIBCPP) -c -o \$@ \$< 69 | EOF 70 | fi 71 | done 72 | 73 | cd "$DESTDIR"/libcxxabi/src 74 | for f in $(find . -type f -name '*.cpp'); do 75 | f=${f:2} # ./foo/bar.cpp -> foo/bar.cpp 76 | objf=${f:0:$(( ${#f} - 4 ))}.o # foo/bar.cpp -> foo/bar.o 77 | LIBCXXABI_OBJS="$LIBCXXABI_OBJS $objf" 78 | if [[ "$f" == *"/"* ]]; then 79 | cat << EOF >> "$BUILDDIR"/Makefile 80 | libcxxabi/$objf: \$(DESTDIR)/libcxxabi/src/$f 81 | @mkdir -p \$(dir \$@) 82 | \$(CXXC) \$(CFLAGS) \$(CFLAGS_LIBCPPABI) -c -o \$@ \$< 83 | EOF 84 | fi 85 | done 86 | 87 | cd "$BUILDDIR" 88 | sed -i '' -E 's:@LIBCXX_OBJS@:'"$LIBCXX_OBJS"':g' Makefile 89 | sed -i '' -E 's:@LIBCXXABI_OBJS@:'"$LIBCXXABI_OBJS"':g' Makefile 90 | if ! make -j$(nproc); then 91 | status=$? 92 | echo "Make failed (exit status $status)" >&2 93 | exit $status 94 | fi 95 | 96 | # cleanup 97 | cd "$SRCDIR" 98 | rm -rf "$BUILDDIR" 99 | 100 | echo "libc++ archives created:" 101 | ls -lh "$DESTDIR"/lib 102 | 103 | -------------------------------------------------------------------------------- /misc/clang_driver-clang13.diff: -------------------------------------------------------------------------------- 1 | --- misc/myclang/driver.cc.orig 2021-05-18 17:45:09.000000000 -0700 2 | +++ misc/myclang/driver.cc 2021-05-18 17:46:22.000000000 -0700 3 | @@ -207,8 +207,6 @@ 4 | void *MainAddr); 5 | extern int cc1as_main(ArrayRef Argv, const char *Argv0, 6 | void *MainAddr); 7 | -extern int cc1gen_reproducer_main(ArrayRef Argv, 8 | - const char *Argv0, void *MainAddr); 9 | 10 | static void insertTargetAndModeArgs(const ParsedClangName &NameParts, 11 | SmallVectorImpl &ArgVector, 12 | @@ -339,16 +337,13 @@ 13 | if (Tool == "-cc1as") 14 | return cc1as_main(makeArrayRef(ArgV).slice(2), ArgV[0], 15 | GetExecutablePathVP); 16 | - if (Tool == "-cc1gen-reproducer") 17 | - return cc1gen_reproducer_main(makeArrayRef(ArgV).slice(2), ArgV[0], 18 | - GetExecutablePathVP); 19 | // Reject unknown tools. 20 | llvm::errs() << "error: unknown integrated tool '" << Tool << "'. " 21 | << "Valid tools include '-cc1' and '-cc1as'.\n"; 22 | return 1; 23 | } 24 | 25 | -int main(int Argc, const char **Argv) { 26 | +extern "C" int clang_main(int Argc, const char **Argv) { 27 | noteBottomOfStack(); 28 | llvm::InitLLVM X(Argc, Argv); 29 | llvm::setBugReportMsg("PLEASE submit a bug report to " BUG_REPORT_URL 30 | -------------------------------------------------------------------------------- /misc/clang_driver.diff: -------------------------------------------------------------------------------- 1 | --- misc/myclang/driver.cc.orig 2021-02-08 11:13:07.000000000 -0800 2 | +++ misc/myclang/driver.cc 2021-02-08 11:13:57.000000000 -0800 3 | @@ -207,8 +207,6 @@ 4 | void *MainAddr); 5 | extern int cc1as_main(ArrayRef Argv, const char *Argv0, 6 | void *MainAddr); 7 | -extern int cc1gen_reproducer_main(ArrayRef Argv, 8 | - const char *Argv0, void *MainAddr); 9 | 10 | static void insertTargetAndModeArgs(const ParsedClangName &NameParts, 11 | SmallVectorImpl &ArgVector, 12 | @@ -331,16 +329,14 @@ 13 | if (Tool == "-cc1as") 14 | return cc1as_main(makeArrayRef(ArgV).slice(2), ArgV[0], 15 | GetExecutablePathVP); 16 | - if (Tool == "-cc1gen-reproducer") 17 | - return cc1gen_reproducer_main(makeArrayRef(ArgV).slice(2), ArgV[0], 18 | - GetExecutablePathVP); 19 | // Reject unknown tools. 20 | llvm::errs() << "error: unknown integrated tool '" << Tool << "'. " 21 | << "Valid tools include '-cc1' and '-cc1as'.\n"; 22 | return 1; 23 | } 24 | 25 | -int main(int argc_, const char **argv_) { 26 | +extern "C" int clang_main(int c, const char** v); 27 | +int clang_main(int argc_, const char **argv_) { 28 | noteBottomOfStack(); 29 | llvm::InitLLVM X(argc_, argv_); 30 | llvm::setBugReportMsg("PLEASE submit a bug report to " BUG_REPORT_URL 31 | -------------------------------------------------------------------------------- /misc/etc/array-ref-mockup.c: -------------------------------------------------------------------------------- 1 | /* 2 | autorun array-ref-mockup.c -- \ 3 | "clang -emit-llvm -c -S -o array-ref-mockup.ll array-ref-mockup.c" 4 | */ 5 | #define strlen __builtin_strlen 6 | #define memcpy __builtin_memcpy 7 | 8 | struct mut_slice { int len; int cap; int* ptr; }; // mut&[int] 9 | struct imu_slice { int len; int* ptr; }; // &[int] 10 | struct dynarray { int len; int cap; int* ptr; }; // [int] 11 | 12 | struct dynarray alloc_i32x3() { // fun alloc(T, cap) ⟶ [T] 13 | static int fakeheap[3]; 14 | return (struct dynarray){ 0, 3, fakeheap }; 15 | } 16 | 17 | int first(struct imu_slice s) { // fun look(s &[int]) -> s[0] 18 | return s.ptr[0]; 19 | } 20 | 21 | int main(int argc, const char** argv) { 22 | int a1[3] = {10,20,30}; // really just a pointer to stack data 23 | // int a2[2] = a1; // error: array initializer must be an initializer list 24 | int* s1 = a1; // s1 = a1[:] ⟶ mut&[int 3] 25 | int* s2 = a1+1; // s1 = a1[1:] ⟶ mut&[int 2] 26 | 27 | // copy local stack array to heap 28 | struct dynarray ha = alloc_i32x3(); // alloc(int, 3) ⟶ [T] 29 | ha.len = 3; // ha.len = sa.len 30 | memcpy(ha.ptr, s1, 3); // copy(ha, s1) 31 | 32 | int v1 = first((struct imu_slice){3,s1}); // v1 = first(&s1) 33 | 34 | // Automatic casts: 35 | // digraph { 36 | // graph [ rankdir="LR" ]; 37 | // 38 | // "[T n]" -> "mut&[T n]" 39 | // "[T n]" -> "&[T n]" 40 | // "mut&[T n]" -> "&[T n]" -> "&[T]" 41 | // "mut&[T n]" -> "mut&[T]" -> "&[T]" 42 | // "mut&[T n]" -> "&[T]" 43 | // 44 | // "[T]" -> "mut&[T]" 45 | // 46 | // "alloc(T)" -> "[T]" 47 | // "var [T n]" -> "[T n]" 48 | // } 49 | // 50 | // Only way to get a [T] is with alloc(), [T n] on stack or global. 51 | // 52 | // ┌───────────┐ ┌────────┐ ┌───────────┐ ┌──────┐ 53 | // │ alloc(T) │ ──▶ │ [T] │ ──▶ │ mut&[T] │ ──▶ │ &[T] │ 54 | // └───────────┘ └────────┘ └───────────┘ └──────┘ 55 | // ▲ ▲ ▲ 56 | // │ │ │ 57 | // ┌───────────┐ ┌────────┐ ┌───────────┐ │ │ 58 | // │ var [T n] │ ──▶ │ [T n] │ ──▶ │ mut&[T n] │ ──────┘ │ 59 | // └───────────┘ └────────┘ └───────────┘ │ 60 | // │ ┌─────────────┘ │ 61 | // ▼ ▼ │ 62 | // ┌────────┐ │ 63 | // │ &[T n] │ ───────────────────────────┘ 64 | // └────────┘ 65 | // 66 | 67 | // int d[3] = {10,argc,12}; 68 | // d[1] = 4; 69 | // return (int)d[1]; 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /misc/etc/minimal-llvm-c-program.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | LLVMInitializeNativeTarget(); 10 | LLVMInitializeNativeAsmPrinter(); 11 | char* triple = LLVMGetDefaultTargetTriple(); 12 | char* error; 13 | LLVMTargetRef target_ref; 14 | if (LLVMGetTargetFromTriple(triple, &target_ref, &error)) { 15 | printf("Error: %s\n", error); 16 | return 1; 17 | } 18 | LLVMTargetMachineRef tm_ref = LLVMCreateTargetMachine( 19 | target_ref, 20 | triple, 21 | "", 22 | "", 23 | LLVMCodeGenLevelDefault, 24 | LLVMRelocStatic, 25 | LLVMCodeModelJITDefault); 26 | LLVMDisposeMessage(triple); 27 | 28 | LLVMContextRef context = LLVMContextCreate(); 29 | LLVMModuleRef module = LLVMModuleCreateWithNameInContext("module_name", context); 30 | // LLVMModuleRef module = LLVMModuleCreateWithName("module_name"); 31 | 32 | LLVMTypeRef i32type = LLVMIntTypeInContext(context, 32); 33 | LLVMTypeRef param_types[] = {i32type, i32type}; 34 | LLVMTypeRef func_type = LLVMFunctionType(i32type, param_types, 2, 0); 35 | 36 | LLVMValueRef func = LLVMAddFunction(module, "function_name", func_type); 37 | LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(context, func, "entry"); 38 | 39 | LLVMBuilderRef builder = LLVMCreateBuilderInContext(context); 40 | LLVMPositionBuilderAtEnd(builder, entry); 41 | LLVMValueRef tmp = LLVMBuildAdd(builder, LLVMGetParam(func, 0), LLVMGetParam(func, 1), "add"); 42 | LLVMBuildRet(builder, tmp); 43 | 44 | LLVMVerifyModule(module, LLVMAbortProcessAction, &error); 45 | LLVMDisposeMessage(error); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /misc/gen_parselet_map.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # This script reads and updates the parselet map in src/co/parse/parse.c 4 | # 5 | import re, sys, os, os.path 6 | 7 | def err(msg): 8 | print(msg) 9 | sys.exit(1) 10 | 11 | # os.chdir(srcdir) 12 | 13 | if len(sys.argv) < 3: 14 | err("usage: %s " % sys.argv[0]) 15 | sourcefilename = sys.argv[1] 16 | markfilename = sys.argv[2] 17 | 18 | with open(sourcefilename, "r") as f: 19 | source = f.read() 20 | 21 | # //!Parselet (TPlusPlus UNARY_POSTFIX) (TMinusMinus UNARY_POSTFIX) 22 | # //!PrefixParselet TPlus TMinus TStar TSlash 23 | parseletp = re.compile( 24 | r'\n//\s*\!Parselet\s+(?P(?:\([^\)]+\)[\s\r\n\/\/]*)+)\n\s*(?:static|)\s*Node\*\s*(?:nullable\s*|)(?P\w+)') 25 | prefixparseletp = re.compile( 26 | r'\n//\s*\!PrefixParselet\s+([^\n]+)\n\s*(?:static|)\s*Node\*\s*(?:nullable\s*|)(\w+)') 27 | splitspecs = re.compile(r'\)[\s\r\n\/\/]*\(') 28 | splitsep = re.compile(r'[\s,]+') 29 | parselets = dict() # keyed by token, e.g. "TPlus" 30 | 31 | for m in prefixparseletp.finditer(source): 32 | fun = m.group(2) 33 | for tok in splitsep.split(m.group(1)): 34 | struct_init = parselets.get(tok) 35 | if struct_init: 36 | err("duplicate parselet %s for token %s" % (fun, tok)) 37 | parselets[tok] = [fun, "NULL", "MEMBER"] 38 | 39 | for m in parseletp.finditer(source): 40 | md = m.groupdict() 41 | for s in splitspecs.split(md["m"]): 42 | tok, prec = splitsep.split(s.strip("()"), 1) 43 | fun = md["fun"] 44 | # print({ "tok": tok, "prec": prec, "fun": md["fun"] }) 45 | struct_init = parselets.get(tok) 46 | if not struct_init: 47 | parselets[tok] = ["NULL", fun, prec] 48 | else: 49 | if struct_init[1] != "NULL": 50 | err("duplicate parselet %s for token %s" % (fun, tok)) 51 | struct_init[1] = fun 52 | struct_init[2] = prec 53 | 54 | # const Parselet parselets[TMax] = { 55 | # [TComment] = { PLComment, NULL, PREC_LOWEST }, 56 | # }; 57 | srcdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 58 | relscriptfile = os.path.relpath(os.path.abspath(__file__), srcdir) 59 | output = [ 60 | '// automatically generated by %s; do not edit' % relscriptfile, 61 | ] 62 | output.append("static const Parselet parselets[TMax] = {") 63 | for tok, struct_init in parselets.items(): 64 | output.append(" [%s] = {%s, %s, PREC_%s}," % (tok, *struct_init)) 65 | output.append("};") 66 | output = "\n".join(output) 67 | 68 | startstr = '//PARSELET_MAP_BEGIN\n' 69 | endstr = '\n//PARSELET_MAP_END' 70 | start = source.find(startstr) 71 | end = source.find(endstr, start) 72 | if start == -1: 73 | err("can not find %r in %s" % (startstr, sourcefilename)) 74 | if end == -1: 75 | err("can not find %r in %s" % (endstr, sourcefilename)) 76 | 77 | source2 = source[:start + len(startstr)] + output + source[end:] 78 | 79 | # write changes only if we modified the source 80 | if source2 != source: 81 | print("write", sourcefilename) 82 | with open(sourcefilename, "w") as f: 83 | f.write(source2) 84 | # write "marker" file for ninja/make 85 | with open(markfilename, "w") as f: 86 | f.write("") 87 | -------------------------------------------------------------------------------- /misc/git-hooks/post-checkout.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "${SHELL:-sh} init.sh -quiet" 3 | exec ${SHELL:-sh} init.sh -quiet 4 | -------------------------------------------------------------------------------- /misc/git-hooks/post-merge.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "${SHELL:-sh} init.sh -quiet" 3 | exec ${SHELL:-sh} init.sh -quiet 4 | -------------------------------------------------------------------------------- /misc/libcxx.make: -------------------------------------------------------------------------------- 1 | DEPSDIR := @DEPSDIR@ 2 | DESTDIR := @DESTDIR@ 3 | LIBCXX_OBJS := @LIBCXX_OBJS@ 4 | LIBCXXABI_OBJS := @LIBCXXABI_OBJS@ 5 | 6 | LIBCXX_OBJDIR := libcxx 7 | LIBCXXABI_OBJDIR := libcxxabi 8 | 9 | LIBCXX_OBJS := $(foreach fn,$(LIBCXX_OBJS),$(LIBCXX_OBJDIR)/$(fn)) 10 | LIBCXXABI_OBJS := $(foreach fn,$(LIBCXXABI_OBJS),$(LIBCXXABI_OBJDIR)/$(fn)) 11 | 12 | CXXC := $(DEPSDIR)/llvm/bin/clang++ 13 | AR := $(DEPSDIR)/llvm/bin/llvm-ar 14 | 15 | CFLAGS := \ 16 | -Wall -nostdinc++ -fvisibility-inlines-hidden -std=c++14 -Wno-user-defined-literals \ 17 | -DNDEBUG \ 18 | -D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS \ 19 | -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS \ 20 | -I$(DESTDIR)/include \ 21 | -I$(DESTDIR)/libcxxabi/include 22 | 23 | ifneq ($(MUSL_ABI),) 24 | CFLAGS += -D_LIBCPP_HAS_MUSL_LIBC 25 | endif 26 | 27 | ifneq ($(TARGET_SUPPORTS_FPIC),) 28 | CFLAGS += -fPIC 29 | endif 30 | 31 | CFLAGS_LIBCPP := \ 32 | -D_LIBCPP_BUILDING_LIBRARY \ 33 | -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER \ 34 | -DLIBCXX_BUILDING_LIBCXXABI 35 | 36 | CFLAGS_LIBCPPABI := \ 37 | -DHAVE___CXA_THREAD_ATEXIT_IMPL \ 38 | -D_LIBCPP_DISABLE_EXTERN_TEMPLATE \ 39 | -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS \ 40 | -D_LIBCXXABI_BUILDING_LIBRARY 41 | 42 | all: $(DESTDIR)/lib/libc++.a $(DESTDIR)/lib/libc++abi.a 43 | 44 | $(DESTDIR)/lib/libc++.a: $(LIBCXX_OBJS) 45 | @mkdir -p $(dir $@) 46 | $(AR) -r $@ $^ 47 | 48 | $(DESTDIR)/lib/libc++abi.a: $(LIBCXXABI_OBJS) 49 | @mkdir -p $(dir $@) 50 | $(AR) -r $@ $^ 51 | 52 | $(LIBCXX_OBJDIR)/%.o: $(DESTDIR)/libcxx/src/%.cpp 53 | @mkdir -p $(dir $@) 54 | $(CXXC) $(CFLAGS) $(CFLAGS_LIBCPP) -c -o $@ $< 55 | 56 | $(LIBCXXABI_OBJDIR)/%.o: $(DESTDIR)/libcxxabi/src/%.cpp 57 | @mkdir -p $(dir $@) 58 | $(CXXC) $(CFLAGS) $(CFLAGS_LIBCPPABI) -c -o $@ $< 59 | 60 | # $(LIBCXX_OBJS): | $(LIBCXX_OBJDIR) 61 | # $(LIBCXXABI_OBJS): | $(LIBCXXABI_OBJDIR) 62 | # $(LIBCXX_OBJDIR): 63 | # mkdir $(LIBCXX_OBJDIR) 64 | # $(LIBCXXABI_OBJDIR): 65 | # mkdir $(LIBCXXABI_OBJDIR) 66 | 67 | Makefile: 68 | @true 69 | -------------------------------------------------------------------------------- /misc/myclang/.gitignore: -------------------------------------------------------------------------------- 1 | /myclang* 2 | -------------------------------------------------------------------------------- /misc/myclang/Makefile: -------------------------------------------------------------------------------- 1 | LLVM_PREFIX := ../../deps/llvm 2 | SYSTEM := $(shell uname -s) 3 | 4 | CC := $(LLVM_PREFIX)/bin/clang 5 | CXXC := $(LLVM_PREFIX)/bin/clang++ 6 | STRIP := $(LLVM_PREFIX)/bin/llvm-strip 7 | LLVM_CONFIG := $(LLVM_PREFIX)/bin/llvm-config 8 | 9 | ifeq ($(SYSTEM),Darwin) 10 | LS_SHLIBS := $(LLVM_PREFIX)/bin/llvm-objdump --macho --dylibs-used 11 | else 12 | LS_SHLIBS := ldd # fixme (note: only used by "test" target) 13 | endif 14 | 15 | _FLAGS := -Oz -g -I$(LLVM_PREFIX)/include 16 | CFLAGS := $(_FLAGS) -std=c17 17 | CXXFLAGS := $(_FLAGS) -stdlib=libc++ -nostdinc++ -std=c++14 -I../../lib/libcxx/include 18 | 19 | CSRCS := $(wildcard *.c) 20 | CXXSRCS := $(wildcard *.cc) 21 | 22 | OBJDIR := .obj 23 | COBJS := $(patsubst %,$(OBJDIR)/%.o,$(CSRCS)) 24 | CXXOBJS := $(patsubst %,$(OBJDIR)/%.o,$(CXXSRCS)) 25 | OBJS := $(COBJS) $(CXXOBJS) 26 | 27 | myclang: $(OBJS) 28 | $(CC) -flto \ 29 | -L"$(LLVM_PREFIX)"/lib -L../../deps/zlib/lib -Wl,-no_pie \ 30 | -o $@ $^ \ 31 | ../../work/build/libc++.a \ 32 | ../../work/build/libc++abi.a \ 33 | "$(LLVM_PREFIX)"/lib/libclang*.a \ 34 | "$(LLVM_PREFIX)"/lib/liblld*.a \ 35 | $(shell "$(LLVM_CONFIG)" --libfiles) \ 36 | $(shell "$(LLVM_CONFIG)" --system-libs) 37 | 38 | myclang-stripped: myclang 39 | $(STRIP) -o $@ $< 40 | 41 | $(OBJDIR)/%.c.o: %.c 42 | $(CC) $(CFLAGS) -c -o $@ $< 43 | 44 | $(OBJDIR)/%.cc.o: %.cc 45 | $(CXXC) -static $(CXXFLAGS) -c -o $@ $< 46 | 47 | $(OBJS): | $(OBJDIR) 48 | $(OBJDIR): 49 | mkdir $(OBJDIR) 50 | 51 | test: myclang 52 | @# Note: "PATH=" to ensure myclang is not using any system programs like ld 53 | PATH= ./myclang -o test/test test/main.c 54 | ./test/test 55 | $(LS_SHLIBS) myclang 56 | $(LS_SHLIBS) test/test 57 | 58 | info: 59 | @echo "OBJDIR $(OBJDIR)" 60 | @echo "COBJS $(COBJS)" 61 | @echo "CXXOBJS $(CXXOBJS)" 62 | @echo "CFLAGS $(CFLAGS)" 63 | @echo "CXXFLAGS $(CXXFLAGS)" 64 | 65 | clean: 66 | rm -rf $(OBJDIR) myclang myclang-stripped 67 | 68 | .PHONY: clean info test 69 | -------------------------------------------------------------------------------- /misc/myclang/build-driver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | cd "$(dirname "$0")" 4 | . ../../misc/_common.sh 5 | 6 | OUTFILE=myclang 7 | LLVM_PREFIX="$DEPS_DIR"/llvm 8 | CFLAGS="-Oz -g -I$LLVM_PREFIX/include" 9 | 10 | # Note: there's currently no stdc++ lib at LLVM_PREFIX, so use the system compiler for now. 11 | # TODO: use clang at LLVM_PREFIX with stdc++ 12 | export PATH="$LLVM_PREFIX/bin:$PATH" 13 | command -v clang 14 | 15 | 16 | mkdir -p .obj 17 | ATEXIT+=("rm -rf '$PWD/.obj'") 18 | for f in *.cc; do 19 | echo "$f -> .obj/$f.o" 20 | clang++ -static -stdlib=libc++ -nostdinc++ -std=c++14 \ 21 | "-I$PROJECT/lib/libcxx/include" $CFLAGS -c -o .obj/$f.o $f & 22 | done 23 | for f in *.c; do 24 | echo "$f -> .obj/$f.o" 25 | clang $CFLAGS -std=c17 -c -o .obj/$f.o $f & 26 | done 27 | wait 28 | 29 | 30 | echo "link $OUTFILE" 31 | 32 | clang -flto \ 33 | -L"$LLVM_PREFIX"/lib \ 34 | -L"$DEPS_DIR"/zlib/lib \ 35 | -Wl,-no_pie \ 36 | -o "$OUTFILE" \ 37 | \ 38 | .obj/*.o \ 39 | "$PROJECT"/work/build/libc++.a \ 40 | "$PROJECT"/work/build/libc++abi.a \ 41 | "$LLVM_PREFIX"/lib/libclang*.a \ 42 | $(llvm-config --libfiles) \ 43 | \ 44 | $(llvm-config --system-libs) \ 45 | 46 | # print dylibs 47 | echo otool -L "$OUTFILE" 48 | otool -L "$OUTFILE" 49 | 50 | # strip 51 | cp "$OUTFILE" "$OUTFILE"-stripped 52 | llvm-strip "$OUTFILE"-stripped 53 | 54 | # test it 55 | echo "building test program" 56 | ./"$OUTFILE"-stripped -o test/test test/main.c 57 | ./test/test 58 | echo otool -L test/test 59 | otool -L test/test 60 | -------------------------------------------------------------------------------- /misc/myclang/llvm_api.cc: -------------------------------------------------------------------------------- 1 | #if __GNUC__ >= 9 2 | #pragma GCC diagnostic push 3 | #pragma GCC diagnostic ignored "-Winit-list-lifetime" 4 | #endif 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "llvm_api.h" 15 | 16 | #if __GNUC__ >= 9 17 | #pragma GCC diagnostic pop 18 | #endif 19 | 20 | using namespace llvm; 21 | 22 | // if (Triple(sys::getProcessTriple()).getOS() == Triple::Win32) 23 | 24 | OSType LLVMGetHostOSType() { 25 | return (OSType)Triple(sys::getProcessTriple()).getOS(); 26 | } 27 | 28 | OSType LLVMParseOS(const char* osname) { 29 | if (strcmp(osname, "macos") == 0) 30 | return OSMacOSX; 31 | return (OSType)Triple("", "", osname).getOS(); 32 | } 33 | 34 | const char* LLVMGetOSTypeName(OSType os) { 35 | const char* name = (const char*)Triple::getOSTypeName((Triple::OSType)os).bytes_begin(); 36 | if (strcmp(name, "macosx") == 0) 37 | return "macos"; 38 | return name; 39 | } 40 | 41 | 42 | bool LLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, 43 | OSType os_type) 44 | { 45 | object::Archive::Kind kind; 46 | switch (os_type) { 47 | case OSWin32: 48 | // For some reason llvm-lib passes K_GNU on windows. 49 | // See lib/ToolDrivers/llvm-lib/LibDriver.cpp:168 in libDriverMain 50 | kind = object::Archive::K_GNU; 51 | break; 52 | case OSLinux: 53 | kind = object::Archive::K_GNU; 54 | break; 55 | case OSMacOSX: 56 | case OSDarwin: 57 | case OSIOS: 58 | kind = object::Archive::K_DARWIN; 59 | break; 60 | case OSOpenBSD: 61 | case OSFreeBSD: 62 | kind = object::Archive::K_BSD; 63 | break; 64 | default: 65 | kind = object::Archive::K_GNU; 66 | } 67 | SmallVector new_members; 68 | for (size_t i = 0; i < file_name_count; i += 1) { 69 | Expected new_member = NewArchiveMember::getFile(file_names[i], true); 70 | Error err = new_member.takeError(); 71 | if (err) return true; 72 | new_members.push_back(std::move(*new_member)); 73 | } 74 | Error err = writeArchive(archive_name, new_members, true, kind, true, false, nullptr); 75 | if (err) return true; 76 | return false; 77 | } 78 | 79 | // See lld/Common/Driver.h for lld api 80 | // See lls/tools/lld/lld.cpp for the lld program main 81 | 82 | int LLDLinkCOFF(int argc, const char **argv, bool can_exit_early) { 83 | std::vector args(argv, argv + argc); 84 | return lld::coff::link(args, can_exit_early, llvm::outs(), llvm::errs()); 85 | } 86 | 87 | int LLDLinkELF(int argc, const char **argv, bool can_exit_early) { 88 | std::vector args(argv, argv + argc); 89 | return lld::elf::link(args, can_exit_early, llvm::outs(), llvm::errs()); 90 | } 91 | 92 | int LLDLinkMachO(int argc, const char **argv, bool can_exit_early) { 93 | // Note: there's both lld::mach_o and lld::macho -- the latter is a newer linker 94 | // which is work in progress (as of Feb 2021) 95 | std::vector args(argv, argv + argc); 96 | // return lld::macho::link(args, can_exit_early, llvm::outs(), llvm::errs()); // WIP 97 | return lld::mach_o::link(args, can_exit_early, llvm::outs(), llvm::errs()); 98 | } 99 | 100 | int LLDLinkWasm(int argc, const char **argv, bool can_exit_early) { 101 | std::vector args(argv, argv + argc); 102 | return lld::wasm::link(args, can_exit_early, llvm::outs(), llvm::errs()); 103 | } 104 | 105 | -------------------------------------------------------------------------------- /misc/myclang/llvm_api.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __cplusplus 7 | #define EXTERN_C extern "C" 8 | #else 9 | #define EXTERN_C 10 | #endif 11 | 12 | typedef enum OSType { // must match llvm::Triple::OSType 13 | OSUnknown, 14 | OSAnanas, 15 | OSCloudABI, 16 | OSDarwin, 17 | OSDragonFly, 18 | OSFreeBSD, 19 | OSFuchsia, 20 | OSIOS, 21 | OSKFreeBSD, 22 | OSLinux, 23 | OSLv2, // PS3 24 | OSMacOSX, 25 | OSNetBSD, 26 | OSOpenBSD, 27 | OSSolaris, 28 | OSWin32, 29 | OSHaiku, 30 | OSMinix, 31 | OSRTEMS, 32 | OSNaCl, // Native Client 33 | OSCNK, // BG/P Compute-Node Kernel 34 | OSAIX, 35 | OSCUDA, // NVIDIA CUDA 36 | OSNVCL, // NVIDIA OpenCL 37 | OSAMDHSA, // AMD HSA Runtime 38 | OSPS4, 39 | OSELFIAMCU, 40 | OSTvOS, // Apple tvOS 41 | OSWatchOS, // Apple watchOS 42 | OSMesa3D, 43 | OSContiki, 44 | OSAMDPAL, // AMD PAL Runtime 45 | OSHermitCore, // HermitCore Unikernel/Multikernel 46 | OSHurd, // GNU/Hurd 47 | OSWASI, // Experimental WebAssembly OS 48 | OSEmscripten, 49 | } OSType; 50 | 51 | EXTERN_C const char* LLVMGetOSTypeName(OSType os); 52 | EXTERN_C OSType LLVMParseOS(const char* osname); 53 | EXTERN_C OSType LLVMGetHostOSType(); 54 | 55 | EXTERN_C bool LLVMWriteArchive( 56 | const char *archive_name, const char** infiles, size_t infilec, OSType os); 57 | 58 | EXTERN_C int LLDLinkCOFF(int argc, const char** argv, bool can_exit_early); 59 | EXTERN_C int LLDLinkELF(int argc, const char** argv, bool can_exit_early); 60 | EXTERN_C int LLDLinkMachO(int argc, const char** argv, bool can_exit_early); 61 | EXTERN_C int LLDLinkWasm(int argc, const char** argv, bool can_exit_early); 62 | -------------------------------------------------------------------------------- /misc/myclang/test/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | int main(int argc, const char* argv[argc+1]) { 3 | printf("Hello world!\n"); 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /misc/sublime-syntax/co/Build.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": ["co", "build", "$file"], 3 | "selector": "source.co", 4 | "file_regex": "^(.*?):([0-9]+):([0-9]+):\\s[0-9]+:[0-9]+\\s(.*)$", 5 | 6 | "variants": [ 7 | { "selector": "source.co", 8 | "cmd": ["./$file_base_name"], 9 | "name": "Run", 10 | "windows": 11 | { "cmd": ["$file_base_name.exe"] 12 | }, 13 | }, 14 | { "selector": "source.co", 15 | "shell_cmd": "co build \"$file\" && \"./$file_base_name\"", 16 | "name": "Build & Run", 17 | }, 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /misc/sublime-syntax/co/Comments.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | name 5 | Comments 6 | scope 7 | source.co 8 | settings 9 | 10 | shellVariables 11 | 12 | 13 | name 14 | TM_COMMENT_START 15 | value 16 | // 17 | 18 | 19 | name 20 | TM_COMMENT_DISABLE_INDENT 21 | value 22 | no 23 | 24 | 25 | name 26 | TM_LINE_TERMINATOR 27 | value 28 | ; 29 | 30 | 31 | name 32 | TM_COMMENT_START_2 33 | value 34 | /* 35 | 36 | 37 | name 38 | TM_COMMENT_END_2 39 | value 40 | */ 41 | 42 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /misc/sublime-syntax/co/Indent.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | name 5 | Indentation Rules 6 | scope 7 | source.co 8 | settings 9 | 10 | decreaseIndentPattern 11 | ^(.*\*/)?\s*\}.*$ 12 | increaseIndentPattern 13 | ^.*\{[^}"']*$ 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /misc/sublime-syntax/co/IndentComments.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | scope 5 | source.co comment 6 | settings 7 | 8 | preserveIndent 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /misc/sublime-syntax/co/IndentStrings.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | scope 5 | source.co string 6 | settings 7 | 8 | preserveIndent 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /misc/sublime-syntax/co/Snippets/comment-block.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | #* 6 | source.co 7 | #* … *# 8 | 9 | -------------------------------------------------------------------------------- /misc/sublime-syntax/co/Snippets/fun.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | fun 6 | source.co 7 | fun …(…) { … } 8 | 9 | -------------------------------------------------------------------------------- /misc/sublime-syntax/co/SymbolIndex.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | scope 5 | 6 | settings 7 | 8 | showInIndexedSymbolList 9 | 1 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /misc/sublime-syntax/co/SymbolList.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | name 5 | Symbol List 6 | scope 7 | meta.function.declaration.co - meta.function.declaration.anonymous.co, entity.name.type.co, entity.name.struct.co 8 | settings 9 | 10 | showInSymbolList 11 | 1 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/co/bn/bn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../build.h" 3 | 4 | typedef struct Node Node; 5 | 6 | bool bn_codegen(Build* build, Node* pkgnode); 7 | -------------------------------------------------------------------------------- /src/co/bn/libbn.cc: -------------------------------------------------------------------------------- 1 | #include "libbn.h" 2 | // #include // for ShellExternalInterface 3 | // #include // in deps/binaryen/src 4 | 5 | #ifdef CO_BN_STATICLIB 6 | int cobn_dummy = 1; // silence linker warning about no exported symbols 7 | #endif 8 | 9 | // using namespace wasm; 10 | 11 | // struct ExtInterface : ModuleInstanceBase::ExternalInterface { 12 | // virtual void init(Module& wasm, SubType& instance) override { 13 | // dlog("ExtInterface.init"); 14 | // } 15 | // virtual void importGlobals(GlobalManager& globals, Module& wasm) override { 16 | // dlog("ExtInterface.importGlobals"); 17 | // } 18 | // virtual Literals callImport(Function* import, LiteralList& arguments) override { 19 | // dlog("ExtInterface.callImport"); 20 | // } 21 | // virtual Literals callTable(Name tableName, Index index, Signature sig, LiteralList& arguments, Type result, SubType& instance) override { 22 | // dlog("ExtInterface.callTable"); 23 | // } 24 | // virtual bool growMemory(Address oldSize, Address newSize) override { 25 | // dlog("ExtInterface.growMemory"); 26 | // } 27 | // virtual void trap(const char* why) override { 28 | // dlog("ExtInterface.trap"); 29 | // } 30 | // virtual void hostLimit(const char* why) override { 31 | // dlog("ExtInterface.hostLimit"); 32 | // } 33 | // virtual void throwException(const WasmException& exn) override { 34 | // dlog("ExtInterface.throwException"); 35 | // } 36 | // }; 37 | 38 | // int bn_mod_interpret(BinaryenModuleRef module) { 39 | // ShellExternalInterface interface; 40 | // ModuleInstance instance(*(Module*)module, &interface, {}); 41 | // return 0; 42 | // } 43 | -------------------------------------------------------------------------------- /src/co/bn/libbn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | // int bn_mod_interpret(BinaryenModuleRef module); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | -------------------------------------------------------------------------------- /src/co/bn/test.c: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | #include 3 | 4 | R_TEST(binaryen) { 5 | BinaryenModuleRef module = BinaryenModuleCreate(); 6 | 7 | // Create a function type for i32 (i32, i32) 8 | BinaryenType ii[2] = {BinaryenTypeInt32(), BinaryenTypeInt32()}; 9 | BinaryenType params = BinaryenTypeCreate(ii, 2); 10 | BinaryenType results = BinaryenTypeInt32(); 11 | 12 | // Get the 0 and 1 arguments, and add them 13 | BinaryenExpressionRef x = BinaryenLocalGet(module, 0, BinaryenTypeInt32()), 14 | y = BinaryenLocalGet(module, 1, BinaryenTypeInt32()); 15 | BinaryenExpressionRef add = BinaryenBinary(module, BinaryenAddInt32(), x, y); 16 | 17 | // Create the add function 18 | // Note: no additional local variables 19 | // Note: no basic blocks here, we are an AST. The function body is just an expression node. 20 | //BinaryenFunctionRef adder = 21 | BinaryenAddFunction(module, "adder", params, results, NULL, 0, add); 22 | 23 | // Print it out 24 | BinaryenModulePrint(module); 25 | 26 | // validate it 27 | assert(BinaryenModuleValidate(module)); 28 | 29 | // optimize it 30 | BinaryenModuleOptimize(module); 31 | assert(BinaryenModuleValidate(module)); 32 | 33 | dlog("after BinaryenModuleOptimize:"); 34 | BinaryenModulePrint(module); 35 | 36 | // Clean up the module, which owns all the objects we created above 37 | BinaryenModuleDispose(module); 38 | 39 | // exit(0); 40 | } 41 | -------------------------------------------------------------------------------- /src/co/build.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "build.h" 3 | #include "parse/parse.h" // universe_syms 4 | 5 | void build_init(Build* b, 6 | Mem mem, 7 | SymPool* syms, 8 | Pkg* pkg, 9 | DiagHandler* nullable diagh, 10 | void* userdata) 11 | { 12 | assertnotnull(mem); 13 | memset(b, 0, sizeof(Build)); 14 | b->mem = mem; 15 | b->safe = true; 16 | b->syms = syms; 17 | b->pkg = pkg; 18 | b->diagh = diagh; 19 | b->userdata = userdata; 20 | b->diaglevel = DiagMAX; 21 | b->sint_type = sizeof(long) > 4 ? TypeCode_i64 : TypeCode_i32; // default to host size 22 | b->uint_type = sizeof(long) > 4 ? TypeCode_u64 : TypeCode_u32; 23 | SymMapInit(&b->types, 32, mem); 24 | ArrayInit(&b->diagarray); 25 | posmap_init(&b->posmap, mem); 26 | } 27 | 28 | void build_dispose(Build* b) { 29 | ArrayFree(&b->diagarray, b->mem); 30 | SymMapDispose(&b->types); 31 | posmap_dispose(&b->posmap); 32 | #if DEBUG 33 | memset(b, 0, sizeof(*b)); 34 | #endif 35 | } 36 | 37 | Diagnostic* build_mkdiag(Build* b) { 38 | auto d = memalloct(b->mem, Diagnostic); 39 | d->build = b; 40 | ArrayPush(&b->diagarray, d, b->mem); 41 | return d; 42 | } 43 | 44 | void diag_free(Diagnostic* d) { 45 | assertnotnull(d->build); 46 | memfree(d->build->mem, (void*)d->message); 47 | memfree(d->build->mem, d); 48 | } 49 | 50 | void build_diag(Build* b, DiagLevel level, PosSpan pos, const char* message) { 51 | if (level <= DiagError) 52 | b->errcount++; 53 | if (level > b->diaglevel || b->diagh == NULL) 54 | return; 55 | auto d = build_mkdiag(b); 56 | d->level = level; 57 | d->pos = pos; 58 | d->message = memstrdup(b->mem, message); 59 | b->diagh(d, b->userdata); 60 | } 61 | 62 | void build_diagv(Build* b, DiagLevel level, PosSpan pos, const char* fmt, va_list ap) { 63 | if (level > b->diaglevel || b->diagh == NULL) { 64 | if (level <= DiagError) 65 | b->errcount++; 66 | return; 67 | } 68 | char buf[256]; 69 | va_list ap1; 70 | va_copy(ap1, ap); 71 | ssize_t n = vsnprintf(buf, sizeof(buf), fmt, ap1); 72 | if (n < (ssize_t)sizeof(buf)) 73 | return build_diag(b, level, pos, buf); 74 | // buf too small; heap allocate 75 | char* buf2 = (char*)memalloc(b->mem, n + 1); 76 | n = vsnprintf(buf2, n + 1, fmt, ap); 77 | build_diag(b, level, pos, buf2); 78 | memfree(b->mem, buf2); 79 | } 80 | 81 | void build_diagf(Build* b, DiagLevel level, PosSpan pos, const char* fmt, ...) { 82 | va_list ap; 83 | va_start(ap, fmt); 84 | build_diagv(b, level, pos, fmt, ap); 85 | va_end(ap); 86 | } 87 | 88 | void build_errf(Build* b, PosSpan pos, const char* fmt, ...) { 89 | va_list ap; 90 | va_start(ap, fmt); 91 | build_diagv(b, DiagError, pos, fmt, ap); 92 | va_end(ap); 93 | } 94 | 95 | void build_warnf(Build* b, PosSpan pos, const char* fmt, ...) { 96 | va_list ap; 97 | va_start(ap, fmt); 98 | build_diagv(b, DiagWarn, pos, fmt, ap); 99 | va_end(ap); 100 | } 101 | 102 | void build_notef(Build* b, PosSpan pos, const char* fmt, ...) { 103 | va_list ap; 104 | va_start(ap, fmt); 105 | build_diagv(b, DiagNote, pos, fmt, ap); 106 | va_end(ap); 107 | } 108 | 109 | static const char* const _DiagLevelName[DiagMAX + 1] = { 110 | "error", 111 | "warn", 112 | "note", 113 | }; 114 | 115 | const char* DiagLevelName(DiagLevel l) { 116 | return _DiagLevelName[MAX(0, MIN(l, DiagMAX))]; 117 | } 118 | 119 | Str diag_fmt(Str s, const Diagnostic* d) { 120 | assert(d->level <= DiagMAX); 121 | return pos_fmt(&d->build->posmap, d->pos, s, 122 | "%s: %s", DiagLevelName(d->level), d->message); 123 | } 124 | 125 | 126 | #if R_TESTING_ENABLED 127 | 128 | Build* test_build_new() { 129 | Mem mem = MemLinearAlloc(1); 130 | 131 | auto syms = memalloct(mem, SymPool); 132 | sympool_init(syms, universe_syms(), mem, NULL); 133 | 134 | auto pkg = memalloct(mem, Pkg); 135 | pkg->dir = "."; 136 | 137 | auto b = memalloct(mem, Build); 138 | build_init(b, mem, syms, pkg, NULL, NULL); 139 | 140 | return b; 141 | } 142 | 143 | void test_build_free(Build* b) { 144 | auto mem = b->mem; 145 | // sympool_dispose(b->syms); // not needed because of MemFree 146 | build_dispose(b); 147 | MemLinearFree(mem); 148 | } 149 | 150 | #endif /* R_TESTING_ENABLED */ 151 | -------------------------------------------------------------------------------- /src/co/common.h: -------------------------------------------------------------------------------- 1 | // 2 | // This files contains definitions used across the entire codebase. 3 | // Keep it lean. 4 | // 5 | #pragma once 6 | #include 7 | 8 | // Outline of memory functions: 9 | // 10 | // Mem MemHeap A shared thread-safe heap allocator 11 | // type MemArena MemArena is the type of a memory arena 12 | // Mem MemArenaAlloc() Allocate a new MemArena 13 | // void MemArenaFree(Mem) Frees all memory allocated in the arena 14 | // 15 | #define CO_MEM_USE_JEMALLOC /* use jemalloc instead of libc */ 16 | #ifdef CO_MEM_USE_JEMALLOC 17 | #include 18 | #define MemHeap MemJEMalloc() 19 | #if 0 /* use jemalloc arenas instead of rbase/MemArenaShim */ 20 | typedef Mem MemArena; 21 | inline static Mem MemArenaAlloc() { 22 | return MemJEMallocArenaAlloc(MemJEMallocArenaDummyFree); 23 | } 24 | inline static void MemArenaFree(Mem m) { 25 | MemJEMallocArenaFree(m); 26 | } 27 | #else 28 | // typedef MemArenaShim MemArena; 29 | // inline static Mem MemArenaAlloc() { 30 | // auto a = memalloct(MemHeap, MemArena); 31 | // return MemArenaShimInit(a, MemHeap); 32 | // } 33 | // inline static void MemArenaFree(Mem m) { 34 | // auto a = (MemArenaShim*)m; 35 | // MemArenaShimFree(a); 36 | // memfree(MemHeap, a); 37 | // } 38 | #endif 39 | #else 40 | #define MemHeap MemLibC() 41 | // typedef MemArenaShim MemArena; 42 | // inline static Mem MemArenaAlloc() { 43 | // auto a = memalloct(MemHeap, MemArena); 44 | // return MemArenaShimInit(a, MemHeap); 45 | // } 46 | // inline static void MemArenaFree(Mem m) { 47 | // auto a = (MemArenaShim*)m; 48 | // MemArenaShimFree(a); 49 | // memfree(MemHeap, a); 50 | // } 51 | #endif 52 | -------------------------------------------------------------------------------- /src/co/ir/irbuilder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../util/array.h" 3 | #include "../util/ptrmap.h" 4 | #include "../util/symmap.h" 5 | #include "../build.h" 6 | #include "../parse/parse.h" 7 | 8 | 9 | typedef enum IRBuilderFlags { 10 | IRBuilderDefault = 0, 11 | IRBuilderComments = 1 << 1, // include comments in some values, for formatting 12 | IRBuilderOpt = 1 << 2, // apply construction-pass [optimization]s 13 | } IRBuilderFlags; 14 | 15 | 16 | typedef struct IRBuilder { 17 | Mem mem; // memory for all IR data constructed by this builder 18 | Build* build; // current source context (source-file specific) 19 | IRBuilderFlags flags; 20 | IRPkg* pkg; 21 | PtrMap typecache; // IRType* interning keyed on AST Type* which is interned 22 | 23 | // state used during building 24 | IRBlock* b; // current block 25 | IRFun* f; // current function 26 | 27 | // vas holds variable assignments in the current block (map from variable symbol to ssa value). 28 | // This map is moved into defvars when a block ends (internal call to endBlock.) 29 | SymMap* vars; // Sym => IRValue* 30 | 31 | // defvars maps block id to defined variables at the end of each block. 32 | // A NULL entry indicates there are no variables in that block. 33 | Array defvars; void* defvarsStorage[512]; // [block_id] => SymMap* from vars field 34 | 35 | // funstack is used for saving current function generation state when stumbling upon 36 | // a call op to a not-yet-generated function. 37 | Array funstack; void* funstackStorage[8]; 38 | 39 | // incompletePhis tracks pending, incomplete phis that are completed by sealBlock for 40 | // blocks that are sealed after they have started. This happens when preds are not known 41 | // at the time a block starts, but is known and registered before the block ends. 42 | //incompletePhis :Map>|null 43 | 44 | } IRBuilder; 45 | 46 | // IRBuilderInit starts a new IRPkg. 47 | // Initially b must be zeroed memory. You can pass a previously-used b to recycle it. 48 | // Returns false if OS memory allocation failed. 49 | bool IRBuilderInit(IRBuilder* b, Build* build, IRBuilderFlags flags); 50 | 51 | // IRBuilderDispose frees all memory allocated by b. 52 | void IRBuilderDispose(IRBuilder* b); 53 | 54 | // IRBuilderAddAST adds a top-level AST node to the current IRPkg. 55 | // Returns false if any errors occurred. 56 | // After AST has been added, the AST's memory may be freed as IR does not reference the AST. 57 | // It does however hold on to references to symbols (Sym) but those are usually allocated 58 | // separately from the AST. 59 | bool IRBuilderAddAST(IRBuilder*, Node*); 60 | 61 | // IROpFromAST performs a lookup of IROp based on one or two inputs (type1 & type2) 62 | // along with an AST operator (astOp). For single-input, set type2 to TypeCode_nil. 63 | // Returns OpNil if there is no corresponding IR operation. 64 | IROp IROpFromAST(Tok astOp, TypeCode type1, TypeCode type2); 65 | -------------------------------------------------------------------------------- /src/co/llvm/jit.cc: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | #include "llvm.h" 3 | #include "llvm-includes.hh" 4 | /* 5 | using llvm::DataLayout; 6 | using llvm::LLVMContext; 7 | using llvm::JITEvaluatedSymbol; 8 | using llvm::StringRef; 9 | using llvm::SectionMemoryManager; 10 | 11 | using namespace llvm::orc; 12 | 13 | 14 | class CoJITImpl { 15 | public: 16 | 17 | ExecutionSession ES; 18 | RTDyldObjectLinkingLayer objectLayer; 19 | IRCompileLayer compileLayer; 20 | 21 | DataLayout DL; 22 | MangleAndInterner mangle; 23 | ThreadSafeContext ctx; 24 | 25 | CoJITImpl(JITTargetMachineBuilder JTMB, DataLayout DL) 26 | : objectLayer(ES, []() { return std::make_unique(); }) 27 | , compileLayer(ES, objectLayer, std::make_unique(std::move(JTMB))) 28 | , DL(std::move(DL)), mangle(ES, this->DL) 29 | , ctx(std::make_unique()) 30 | { 31 | ES.getMainJITDylib().addGenerator( 32 | cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess( 33 | DL.getGlobalPrefix()))); 34 | } 35 | 36 | static llvm::Expected> Create() { 37 | auto JTMB = JITTargetMachineBuilder::detectHost(); 38 | 39 | if (!JTMB) 40 | return JTMB.takeError(); 41 | 42 | auto DL = JTMB->getDefaultDataLayoutForTarget(); 43 | if (!DL) 44 | return DL.takeError(); 45 | 46 | return std::make_unique(std::move(*JTMB), std::move(*DL)); 47 | } 48 | 49 | const DataLayout& getDataLayout() const { return DL; } 50 | 51 | LLVMContext& getContext() { return *ctx.getContext(); } 52 | 53 | // void addModule(std::unique_ptr M) { 54 | // cantFail(compileLayer.add(ES.getMainJITDylib(), 55 | // ThreadSafeModule(std::move(M), ctx))); 56 | // } 57 | 58 | llvm::Expected lookup(StringRef name) { 59 | return ES.lookup({ &ES.getMainJITDylib() }, mangle(name.str())); 60 | } 61 | }; 62 | 63 | 64 | static void jit_init_llvm() { 65 | static r_sync_once_flag once; 66 | r_sync_once(&once, { 67 | LLVMInitializeNativeTarget(); 68 | LLVMInitializeNativeAsmPrinter(); 69 | }); 70 | } 71 | 72 | 73 | CoJIT* jit_create(Error* err) { 74 | jit_init_llvm(); 75 | auto j = new CoJITImpl(); 76 | return reinterpret_cast(j); 77 | } 78 | 79 | 80 | void jit_dispose(CoJIT* jit) { 81 | auto j = reinterpret_cast(jit); 82 | delete j; 83 | } 84 | 85 | 86 | void jit_addmodule(CoJIT* jit, LLVMModuleRef M) { 87 | CoJITImpl& j = *reinterpret_cast(jit); 88 | llvm::Module* module = llvm::unwrap(M); 89 | cantFail(j.compileLayer.add( 90 | j.ES.getMainJITDylib(), 91 | ThreadSafeModule(std::move(module), j.ctx))); 92 | } 93 | */ -------------------------------------------------------------------------------- /src/co/llvm/llvm-includes.hh: -------------------------------------------------------------------------------- 1 | // precompiled header 2 | #include "llvm-c/Core.h" /* for LLVMDisposeMessage */ 3 | #include "llvm/ADT/APFloat.h" 4 | #include "llvm/ADT/Optional.h" 5 | #include "llvm/ADT/STLExtras.h" 6 | #include "llvm/ADT/StringRef.h" 7 | #include "llvm/Analysis/AliasAnalysis.h" 8 | #include "llvm/Analysis/TargetLibraryInfo.h" 9 | #include "llvm/Analysis/TargetTransformInfo.h" 10 | #include "llvm/Bitcode/BitcodeWriter.h" 11 | #include "llvm/ExecutionEngine/JITSymbol.h" 12 | #include "llvm/ExecutionEngine/Orc/LLJIT.h" 13 | #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" 14 | #include "llvm/ExecutionEngine/Orc/OrcABISupport.h" 15 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" 16 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" 17 | #include "llvm/InitializePasses.h" 18 | #include "llvm/IR/BasicBlock.h" 19 | #include "llvm/IR/Constants.h" 20 | #include "llvm/IR/DataLayout.h" 21 | #include "llvm/IR/DerivedTypes.h" 22 | #include "llvm/IR/DiagnosticInfo.h" 23 | #include "llvm/IR/DIBuilder.h" 24 | #include "llvm/IR/Function.h" 25 | #include "llvm/IR/InlineAsm.h" 26 | #include "llvm/IR/Instructions.h" 27 | #include "llvm/IR/IRBuilder.h" 28 | #include "llvm/IR/LegacyPassManager.h" /* TODO: remove; used by MC gen */ 29 | #include "llvm/IR/LLVMContext.h" 30 | #include "llvm/IR/LLVMContext.h" 31 | #include "llvm/IR/Module.h" 32 | #include "llvm/IR/PassInstrumentation.h" 33 | #include "llvm/IR/PassManager.h" 34 | #include "llvm/IR/Type.h" 35 | #include "llvm/IR/Verifier.h" 36 | #include "llvm/MC/SubtargetFeature.h" 37 | #include "llvm/Object/Archive.h" 38 | #include "llvm/Object/ArchiveWriter.h" 39 | #include "llvm/Object/COFF.h" 40 | #include "llvm/Object/COFFImportFile.h" 41 | #include "llvm/Object/COFFModuleDefinition.h" 42 | #include "llvm/Passes/PassBuilder.h" 43 | #include "llvm/Passes/StandardInstrumentations.h" 44 | #include "llvm/PassRegistry.h" 45 | #include "llvm/Support/BuryPointer.h" 46 | #include "llvm/Support/CommandLine.h" 47 | #include "llvm/Support/CrashRecoveryContext.h" 48 | #include "llvm/Support/ErrorHandling.h" 49 | #include "llvm/Support/FileSystem.h" 50 | #include "llvm/Support/Host.h" 51 | #include "llvm/Support/InitLLVM.h" 52 | #include "llvm/Support/Path.h" 53 | #include "llvm/Support/PrettyStackTrace.h" 54 | #include "llvm/Support/Process.h" 55 | #include "llvm/Support/Program.h" 56 | #include "llvm/Support/raw_ostream.h" 57 | #include "llvm/Support/Regex.h" 58 | #include "llvm/Support/Signals.h" 59 | #include "llvm/Support/StringSaver.h" 60 | #include "llvm/Support/TargetParser.h" 61 | #include "llvm/Support/TargetRegistry.h" 62 | #include "llvm/Support/TargetSelect.h" 63 | #include "llvm/Support/Timer.h" 64 | #include "llvm/Target/CodeGenCWrappers.h" 65 | #include "llvm/Target/TargetMachine.h" 66 | #include "llvm/Target/TargetOptions.h" 67 | #include "llvm/Transforms/InstCombine/InstCombine.h" 68 | #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" 69 | #include "llvm/Transforms/IPO.h" 70 | #include "llvm/Transforms/IPO/AlwaysInliner.h" 71 | #include "llvm/Transforms/IPO/PassManagerBuilder.h" 72 | #include "llvm/Transforms/Scalar.h" 73 | #include "llvm/Transforms/Scalar/GVN.h" 74 | #include "llvm/Transforms/Utils.h" 75 | #include "llvm/Transforms/Utils/AddDiscriminators.h" 76 | #include "llvm/Transforms/Utils/CanonicalizeAliases.h" 77 | #include "llvm/Transforms/Utils/Mem2Reg.h" 78 | #include "llvm/Transforms/Utils/NameAnonGlobals.h" 79 | 80 | #include "lld/Common/Driver.h" 81 | #include "lld/Common/ErrorHandler.h" 82 | 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | -------------------------------------------------------------------------------- /src/co/llvm/llvmlib-dummy.cc: -------------------------------------------------------------------------------- 1 | // #include "llvm-includes.hh" 2 | -------------------------------------------------------------------------------- /src/co/parse/ast_validate.c: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | #include "../util/ptrmap.h" 3 | #include "parse.h" 4 | 5 | 6 | static Str nodepath1(NodeList* nl, Str s, int depth) { 7 | if (nl->parent) 8 | s = nodepath1(nl->parent, s, depth - 1); 9 | s = str_appendfmt(s, "\n%*.s%s %s [flags: ", 10 | (int)(depth * 2), "", 11 | NodeKindName(nl->n->kind), fmtnode(nl->n)); 12 | s = NodeFlagsStr(nl->n->flags, s); 13 | s = str_appendc(s, ']'); 14 | return s; 15 | } 16 | 17 | 18 | static Str nodepath(NodeList* nl, Str s) { 19 | NodeList* parent = nl->parent; 20 | int depth = 0; 21 | while (parent) { 22 | depth++; 23 | parent = parent->parent; 24 | } 25 | return nodepath1(nl, s, depth); 26 | } 27 | 28 | 29 | typedef struct ValidateCtx { 30 | Build* b; 31 | NodeValidateFlags fl; 32 | u32 errcount; 33 | PtrMap seenmap; // functions we've already verified 34 | } ValidateCtx; 35 | 36 | 37 | static void report_error(ValidateCtx* ctx, NodeList* nl, const char* msg) { 38 | Str npath = nodepath(nl, str_new(64)); 39 | build_errf(ctx->b, NodePosSpan(nl->n), 40 | "AST validation error: %s at:%s\nsource location:", msg, npath); 41 | str_free(npath); 42 | ctx->errcount++; 43 | } 44 | 45 | 46 | static bool visit(NodeList* nl, void* ctxp) { 47 | auto ctx = (ValidateCtx*)ctxp; 48 | auto n = nl->n; 49 | 50 | // skip primitive constants (nil, true, i32, etc.) 51 | if (NodeIsPrimitiveConst(n)) 52 | return true; 53 | 54 | // skip already visited nodes 55 | if ((NodeIsType(n) || NodeIsExpr(n)) && PtrMapSet(&ctx->seenmap, n, (void*)n)) 56 | return true; 57 | 58 | bool errors = false; 59 | 60 | // check "unresolved" integrity 61 | if (nl->parent && 62 | NodeIsUnresolved(n) && 63 | !NodeIsUnresolved(nl->parent->n) && 64 | (n->kind != NVar || n->var.nrefs > 0) ) 65 | { 66 | // node is marked as unresolved but its parent is not 67 | report_error(ctx, nl, "inconsitent \"unresolved\" flags"); 68 | errors = true; 69 | } 70 | 71 | // check for missing types 72 | if ((ctx->fl & NodeValidateMissingTypes) && 73 | n->kind != NTypeType && n->kind != NPkg && n->kind != NFile && !NodeIsType(n) && 74 | n->type == NULL) 75 | { 76 | report_error(ctx, nl, "missing type"); 77 | errors = true; 78 | } 79 | 80 | // check for "bad" nodes (placeholders used to recover parsing on syntax error) 81 | if (NodeKindClass(n->kind) == NodeClassNone && n->kind != NPkg && n->kind != NFile) { 82 | report_error(ctx, nl, "invalid AST node"); 83 | errors = true; 84 | } 85 | 86 | // visit type 87 | if (!errors && n->type) { 88 | u32 errcount = ctx->errcount; 89 | if (!NodeVisitp(nl, n->type, ctx, visit)) 90 | return false; 91 | errors = ctx->errcount > errcount; 92 | } 93 | 94 | // descend unless there were errors ("true" to keep going) 95 | return errors ? true : NodeVisitChildren(nl, ctxp, visit); 96 | } 97 | 98 | bool NodeValidate(Build* b, Node* n, NodeValidateFlags fl) { 99 | ValidateCtx ctx = { .b = b, .fl = fl }; 100 | PtrMapInit(&ctx.seenmap, 64, MemHeap); 101 | NodeVisit(n, &ctx, visit); 102 | PtrMapDispose(&ctx.seenmap); 103 | return ctx.errcount == 0; 104 | } 105 | -------------------------------------------------------------------------------- /src/co/parse/token.c: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | #include "parse.h" 3 | 4 | const char* TokName(Tok t) { 5 | switch (t) { 6 | #define I_ENUM(name, str) case name: return str; 7 | TOKENS(I_ENUM) 8 | #undef I_ENUM 9 | 10 | case TKeywordsStart: return "TKeywordsStart"; 11 | 12 | #define I_ENUM(str, name) case name: return "keyword " #str; 13 | TOKEN_KEYWORDS(I_ENUM) 14 | #undef I_ENUM 15 | 16 | case TKeywordsEnd: return "TKeywordsEnd"; 17 | 18 | case TMax: return "TMax"; 19 | } 20 | return "?"; // TODO 21 | } 22 | -------------------------------------------------------------------------------- /src/co/parse/universe.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // Syntax-specific symbolic definitions 3 | // 4 | // Defines: 5 | // 6 | // const Sym sym_{TOKEN_KEYWORDS...} 7 | // const Sym sym_{TYPE_SYMS...} 8 | // const Sym sym_{PREDEFINED_CONSTANTS...} 9 | // const Sym sym_{PREDEFINED_IDENTS...} 10 | // 11 | // Node* Type_{TYPE_SYMS...} 12 | // Node* Type_nil 13 | // Node* Type_ideal 14 | // 15 | // Node* Const_{PREDEFINED_CONSTANTS...} 16 | // Node* Const_nil 17 | // 18 | ASSUME_NONNULL_BEGIN 19 | 20 | // universe_syms() returns a read-only SymPool which holds all predefined symbols of the language 21 | const SymPool* universe_syms(); 22 | 23 | // TypeCodeToTypeNode returns the type Node for TypeCode t. 24 | static Node* TypeCodeToTypeNode(TypeCode t); 25 | 26 | // sym_langtok returns the Tok representing this sym in the language syntax. 27 | // Either returns a keyword token or TId if s is not a keyword. 28 | static Tok sym_langtok(Sym s); 29 | 30 | // symbols for language keywords (defined in parse.h) 31 | #define SYM_DEF(str, _) \ 32 | extern const Sym sym_##str; 33 | TOKEN_KEYWORDS(SYM_DEF) 34 | #undef SYM_DEF 35 | 36 | 37 | // symbols and AST nodes for predefined types (defined in types.h) 38 | typedef struct Node Node; 39 | extern Node* Type_type; 40 | #define SYM_DEF(name, ...) \ 41 | extern const Sym sym_##name; \ 42 | extern Node* Type_##name; 43 | TYPE_SYMS(SYM_DEF) 44 | TYPE_SYMS_PRIVATE(SYM_DEF) 45 | #undef SYM_DEF 46 | 47 | // IMPL TypeCodeToTypeNode 48 | extern Node* const _TypeCodeToTypeNodeMap[TypeCode_CONCRETE_END]; 49 | inline static Node* TypeCodeToTypeNode(TypeCode t) { 50 | assert(t >= 0 && t < TypeCode_CONCRETE_END); 51 | return _TypeCodeToTypeNodeMap[t]; 52 | } 53 | 54 | // symbols and AST nodes for predefined constants 55 | #define PREDEFINED_CONSTANTS(_) \ 56 | /* name CType_ value */ \ 57 | _( true, bool, 1 ) \ 58 | _( false, bool, 0 ) \ 59 | _( nil, nil, 0 ) \ 60 | /*END PREDEFINED_CONSTANTS*/ 61 | #define SYM_DEF(name, _type, _val) \ 62 | extern const Sym sym_##name; \ 63 | extern Node* Const_##name; 64 | PREDEFINED_CONSTANTS(SYM_DEF) 65 | #undef SYM_DEF 66 | 67 | 68 | // symbols for predefined common identifiers 69 | // predefined common identifiers (excluding types) 70 | #define PREDEFINED_IDENTS(ID) \ 71 | ID( _ ) \ 72 | /*END PREDEFINED_IDENTS*/ 73 | #define SYM_DEF(name) \ 74 | extern const Sym sym_##name; 75 | PREDEFINED_IDENTS(SYM_DEF) 76 | #undef SYM_DEF 77 | 78 | // ----------------------------------------------------------------------------------------------- 79 | // implementation 80 | 81 | inline static Tok sym_langtok(Sym s) { 82 | // Bits [4-8) represents offset into Tok enum when s is a language keyword. 83 | u8 kwindex = symflags(s); 84 | return kwindex == 0 ? TId : TKeywordsStart + kwindex; 85 | } 86 | 87 | ASSUME_NONNULL_END 88 | -------------------------------------------------------------------------------- /src/co/source.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "build.h" 3 | #include "util/tstyle.h" 4 | 5 | #include 6 | #include 7 | 8 | static void SourceInit(const Pkg* pkg, Source* src, const char* filename) { 9 | memset(src, 0, sizeof(Source)); 10 | auto filenamelen = strlen(filename); 11 | if (filenamelen == 0) 12 | panic("empty filename"); 13 | if (!strchr(filename, PATH_SEPARATOR)) { // foo.c -> pkgdir/foo.c 14 | src->filename = path_join(pkg->dir, filename); 15 | } else { 16 | src->filename = str_cpy(filename, filenamelen); 17 | } 18 | src->pkg = pkg; 19 | } 20 | 21 | bool SourceOpen(Source* src, const Pkg* pkg, const char* filename) { 22 | SourceInit(pkg, src, filename); 23 | 24 | src->fd = open(src->filename, O_RDONLY); 25 | if (src->fd < 0) 26 | return false; 27 | 28 | struct stat st; 29 | if (fstat(src->fd, &st) != 0) { 30 | auto _errno = errno; 31 | close(src->fd); 32 | errno = _errno; 33 | return false; 34 | } 35 | src->len = (size_t)st.st_size; 36 | 37 | return true; 38 | } 39 | 40 | void SourceInitMem(Source* src, const Pkg* pkg, const char* filename, const char* text, size_t len) { 41 | SourceInit(pkg, src, filename); 42 | src->fd = -1; 43 | src->body = (const u8*)text; 44 | src->len = len; 45 | } 46 | 47 | bool SourceOpenBody(Source* src) { 48 | if (src->body == NULL) { 49 | src->body = mmap(0, src->len, PROT_READ, MAP_PRIVATE, src->fd, 0); 50 | if (!src->body) 51 | return false; 52 | src->ismmap = true; 53 | } 54 | return true; 55 | } 56 | 57 | bool SourceCloseBody(Source* src) { 58 | bool ok = true; 59 | if (src->body) { 60 | if (src->ismmap) { 61 | ok = munmap((void*)src->body, src->len) == 0; 62 | src->ismmap = false; 63 | } 64 | src->body = NULL; 65 | } 66 | return ok; 67 | } 68 | 69 | bool SourceClose(Source* src) { 70 | auto ok = SourceCloseBody(src); 71 | if (src->fd > -1) { 72 | ok = close(src->fd) != 0 && ok; 73 | src->fd = -1; 74 | } 75 | return ok; 76 | } 77 | 78 | void SourceDispose(Source* src) { 79 | str_free(src->filename); 80 | src->filename = NULL; 81 | } 82 | 83 | void SourceChecksum(Source* src) { 84 | SHA1Ctx sha1; 85 | sha1_init(&sha1); 86 | auto chunksize = mem_pagesize(); 87 | auto remaining = src->len; 88 | auto inptr = src->body; 89 | while (remaining > 0) { 90 | auto z = MIN(chunksize, remaining); 91 | sha1_update(&sha1, inptr, z); 92 | inptr += z; 93 | remaining -= z; 94 | } 95 | sha1_final(src->sha1, &sha1); 96 | } 97 | 98 | // ----------------------------------------------------------------------------------------------- 99 | // Pkg 100 | 101 | void PkgAddSource(Pkg* pkg, Source* src) { 102 | if (pkg->srclist) 103 | src->next = pkg->srclist; 104 | pkg->srclist = src; 105 | } 106 | 107 | bool PkgAddFileSource(Pkg* pkg, const char* filename) { 108 | auto src = memalloct(pkg->mem, Source); 109 | if (!SourceOpen(src, pkg, filename)) { 110 | memfree(pkg->mem, src); 111 | errlog("failed to open %s", filename); 112 | return false; 113 | } 114 | PkgAddSource(pkg, src); 115 | return true; 116 | } 117 | 118 | bool PkgScanSources(Pkg* pkg) { 119 | assert(pkg->srclist == NULL); 120 | DIR* dirp = opendir(pkg->dir); 121 | if (!dirp) 122 | return false; 123 | 124 | DirEntry e; 125 | int readdir_status; 126 | bool ok = true; 127 | while ((readdir_status = fs_readdir(dirp, &e)) > 0) { 128 | switch (e.d_type) { 129 | case DT_REG: 130 | case DT_LNK: 131 | case DT_UNKNOWN: 132 | if (e.d_namlen > 3 && e.d_name[0] != '.' && 133 | strcmp(&e.d_name[e.d_namlen-2], ".co") == 0) 134 | { 135 | ok = PkgAddFileSource(pkg, e.d_name) && ok; 136 | } 137 | break; 138 | default: 139 | break; 140 | } 141 | } 142 | 143 | return closedir(dirp) == 0 && ok && readdir_status == 0; 144 | } 145 | -------------------------------------------------------------------------------- /src/co/types.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "types.h" 3 | 4 | // Lookup table TypeCode => string encoding char 5 | const char _TypeCodeEncodingMap[TypeCode_MAX] = { 6 | #define I_ENUM(name, encoding, _flags) encoding, 7 | TYPE_CODES(I_ENUM) 8 | #undef I_ENUM 9 | }; 10 | 11 | 12 | // #if DEBUG 13 | const char* _TypeCodeName[TypeCode_MAX] = { 14 | #define I_ENUM(name, _encoding, _flags) #name, 15 | TYPE_CODES(I_ENUM) 16 | #undef I_ENUM 17 | }; 18 | 19 | 20 | const TypeCodeFlag _TypeCodeFlagMap[TypeCode_MAX] = { 21 | #define I_ENUM(_name, _encoding, flags) flags, 22 | TYPE_CODES(I_ENUM) 23 | #undef I_ENUM 24 | }; 25 | 26 | const char* CTypeName(CType ct) { 27 | switch (ct) { 28 | case CType_INVALID: return "INVALID"; 29 | case CType_int: return "int"; 30 | case CType_rune: return "rune"; 31 | case CType_float: return "float"; 32 | case CType_str: return "str"; 33 | case CType_bool: return "bool"; 34 | case CType_nil: return "nil"; 35 | } 36 | return "?"; 37 | } 38 | 39 | 40 | // const char* TypeCodeName(TypeCode tc) { 41 | // assert(tc > 0 && tc < TypeCode_MAX); 42 | // return _TypeCodeName[tc]; 43 | // } 44 | // #else 45 | // // compact names where a string is formed from encoding chars + sentinels bytes. 46 | // // E.g. "b\01\02\03\04\05\06\07\08\0f\0F\0..." Index is *2 that of TypeCode. 47 | // static const char _TypeCodeName[TypeCode_MAX * 2] = { 48 | // #define I_ENUM(_, enc) enc, 0, 49 | // TYPE_CODES(I_ENUM) 50 | // #undef I_ENUM 51 | // }; 52 | // const char* TypeCodeName(TypeCode tc) { 53 | // assert(tc > 0 && tc < TypeCode_MAX); 54 | // return &_TypeCodeName[tc * 2]; 55 | // } 56 | // #endif 57 | -------------------------------------------------------------------------------- /src/co/util/array.c: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | #include "array.h" 3 | #include // for qsort_r 4 | 5 | // ARRAY_CAP_STEP defines a power-of-two which the cap must be aligned to. 6 | // This is used to round up growth. I.e. grow by 60 with a cap of 32 would increase the cap 7 | // to 96 (= 32 + (align2(60, ARRAY_CAP_STEP=32) = 64)). 8 | #define ARRAY_CAP_STEP 32 9 | 10 | typedef struct SortCtx { 11 | ArraySortFun f; 12 | void* userdata; 13 | } SortCtx; 14 | 15 | 16 | static int _sort(void* ctx, const void* s1p, const void* s2p) { 17 | return ((SortCtx*)ctx)->f( 18 | *((const void**)s1p), 19 | *((const void**)s2p), 20 | ((SortCtx*)ctx)->userdata 21 | ); 22 | } 23 | 24 | void ArraySort(Array* a, ArraySortFun f, void* userdata) { 25 | SortCtx ctx = { f, userdata }; 26 | qsort_r(a->v, a->len, sizeof(void*), &ctx, &_sort); 27 | } 28 | 29 | void TArrayGrow(void** v, const void* init, u32* cap, size_t elemsize, Mem mem) { 30 | u32 newcap = align2(*cap + 1, ARRAY_CAP_STEP); 31 | if (*v != init) { 32 | *v = memrealloc(mem, *v, elemsize * newcap); 33 | *cap = newcap; 34 | } else { 35 | // moving array from stack to heap 36 | if (R_LIKELY(*v = memalloc(mem, elemsize * newcap))) 37 | memcpy(*v, init, elemsize * *cap); 38 | *cap = newcap; 39 | } 40 | } 41 | 42 | void ArrayGrow(Array* a, size_t addl, Mem mem) { 43 | u32 reqcap = a->cap + addl; 44 | u32 cap = align2(reqcap, ARRAY_CAP_STEP); 45 | if (!a->onstack || a->v == NULL) { 46 | a->v = memrealloc(mem, a->v, sizeof(void*) * cap); 47 | } else { 48 | // moving array from stack to heap 49 | void** v = (void**)memalloc(mem, sizeof(void*) * cap); 50 | memcpy(v, a->v, sizeof(void*) * a->len); 51 | a->v = v; 52 | a->onstack = false; 53 | } 54 | a->cap = cap; 55 | } 56 | 57 | ssize_t ArrayIndexOf(Array* a, void* nullable entry) { 58 | for (u32 i = 0; i < a->len; i++) { 59 | if (a->v[i] == entry) 60 | return (ssize_t)i; 61 | } 62 | return -1; 63 | } 64 | 65 | ssize_t ArrayLastIndexOf(Array* a, void* nullable entry) { 66 | for (u32 i = a->len; i--; ) { 67 | if (a->v[i] == entry) 68 | return (ssize_t)i; 69 | } 70 | return -1; 71 | } 72 | 73 | void ArrayRemove(Array* a, u32 start, u32 count) { 74 | assert(start + count <= a->len); 75 | // ArrayRemove( [0 1 2 3 4 5 6 7] start=2 count=3 ) => [0 1 5 6 7] 76 | // 77 | for (u32 i = start + count; i < a->len; i++) { 78 | a->v[i - count] = a->v[i]; 79 | } 80 | // [0 1 2 3 4 5 6 7] a->v[5-3] = a->v[5] => [0 1 5 3 4 5 6 7] 81 | // ^ i 82 | // 83 | // [0 1 2 3 4 5 6 7] a->v[6-3] = a->v[6] => [0 1 5 6 4 5 6 7] 84 | // ^ i 85 | // 86 | // [0 1 2 3 4 5 6 7] a->v[7-3] = a->v[7] => [0 1 5 6 7 5 6 7] 87 | // ^ i 88 | // 89 | // len -= count => [0 1 5 6 7] 90 | a->len -= count; 91 | } 92 | 93 | 94 | // ArrayCopy copies src of srclen to a, starting at a.v[start], growing a if needed using m. 95 | void ArrayCopy(Array* a, u32 start, const void* src, u32 srclen, Mem mem) { 96 | u32 capNeeded = start + srclen; 97 | if (capNeeded > a->cap) { 98 | if (a->v == NULL) { 99 | // initial allocation to exactly the size needed 100 | a->v = (void*)memalloc(mem, sizeof(void*) * capNeeded); 101 | a->cap = capNeeded; 102 | a->onstack = false; 103 | } else { 104 | ArrayGrow(a, capNeeded - a->cap, mem); 105 | } 106 | } 107 | memcpy(&a->v[start], src, srclen * sizeof(void*)); 108 | a->len = MAX(a->len, start + srclen); 109 | } 110 | -------------------------------------------------------------------------------- /src/co/util/error.c: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | #include "error.h" 3 | 4 | typedef struct TLS { 5 | ErrorValue v; 6 | char* msgbuf; // message storage 7 | size_t msgbufz; // size of msgbuf 8 | } TLS; 9 | 10 | static thread_local TLS g_err_storage = {0}; 11 | 12 | 13 | Error err_make(int code, const char* message) { 14 | ErrorValue* v = &g_err_storage.v; 15 | v->code = code; 16 | v->message = message; 17 | return v; 18 | } 19 | 20 | 21 | static void msgbuf_grow(TLS* st, size_t len) { 22 | len = MIN(512, align2(len, sizeof(void*))); 23 | st->msgbuf = memrealloc(MemHeap, st->msgbuf, len); 24 | st->msgbufz = len; 25 | } 26 | 27 | 28 | Error err_makefv(int code, const char* fmt, va_list ap) { 29 | auto st = &g_err_storage; 30 | 31 | // start by making an educated guess for space needed: 2x that of the format + nul 32 | size_t len = (strlen(fmt) * 2) + 1; 33 | 34 | va_list ap2; 35 | while (1) { 36 | if (st->msgbufz < len) { 37 | msgbuf_grow(st, len); 38 | if (st->msgbuf == NULL) { 39 | // memory allocation failure 40 | return err_make(code, "(err_make failed to memalloc)"); 41 | } 42 | } 43 | va_copy(ap2, ap); // copy va_list as we might read it twice 44 | int n = vsnprintf(st->msgbuf, len, fmt, ap2); 45 | va_end(ap2); 46 | if (n < (int)len) { 47 | // ok; result fit in buf. 48 | // Theoretically vsnprintf might return -1 on error, but AFAIK no implementation does 49 | // unless len > INT_MAX, so we are likely fine with ignoring that case here. 50 | len = (size_t)n; 51 | break; 52 | } 53 | // vsnprintf tells us how much space it needs 54 | len = (size_t)n + 1; 55 | } 56 | 57 | st->v.code = code; 58 | st->v.message = st->msgbuf; 59 | return &st->v; 60 | } 61 | 62 | 63 | Error err_makef(int code, const char* fmt, ...) { 64 | va_list ap; 65 | va_start(ap, fmt); 66 | Error e = err_makefv(code, fmt, ap); 67 | va_end(ap); 68 | return e; 69 | } 70 | 71 | 72 | R_TEST(error) { 73 | assertnull(ErrorNone); 74 | assertnotnull(err_make(0, "")); 75 | Error e = err_makef(123, "hello %u (%s)", 45u, "lol"); 76 | assertcstreq(err_msg(e), "hello 45 (lol)"); 77 | } 78 | -------------------------------------------------------------------------------- /src/co/util/error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct ErrorValue ErrorValue; 4 | typedef const ErrorValue* Error; 5 | 6 | #define ErrorNone ((Error)NULL) 7 | 8 | Error err_make(int code, const char* message); 9 | Error err_makef(int code, const char* messagefmt, ...) ATTR_FORMAT(printf, 2, 3); 10 | Error err_makefv(int code, const char* messagefmt, va_list); 11 | 12 | static int err_code(Error nullable e); 13 | static const char* err_msg(Error nullable e); 14 | 15 | // ————————————————————————————————————————————————————————————————————————————————— 16 | // implementation 17 | 18 | struct ErrorValue { 19 | int code; 20 | const char* message; 21 | }; 22 | 23 | inline static int err_code(Error nullable e) { return e ? e->code : 0; } 24 | inline static const char* err_msg(Error nullable e) { return e ? e->message : ""; } 25 | -------------------------------------------------------------------------------- /src/co/util/hashmap.h: -------------------------------------------------------------------------------- 1 | // Note: intentionally not "#pragma once" 2 | ASSUME_NONNULL_BEGIN 3 | 4 | // example: 5 | // #define HASHMAP_NAME FooMap 6 | // #define HASHMAP_KEY Foo 7 | // #define HASHMAP_VALUE char* 8 | #ifndef HASHMAP_NAME 9 | #error "please define HASHMAP_NAME" 10 | #endif 11 | 12 | // HASHMAP_NAME defines a hash map 13 | typedef struct { 14 | u32 cap; // number of buckets 15 | u32 len; // number of key-value entries 16 | u32 flags; // internal 17 | Mem mem; // memory allocator 18 | void* buckets; // internal 19 | } HASHMAP_NAME; 20 | 21 | 22 | #define _HM_MAKE_FN_NAME(a, b) a ## b 23 | #define _HM_FUN(prefix, name) _HM_MAKE_FN_NAME(prefix, name) 24 | #define HM_FUN(name) _HM_FUN(HASHMAP_NAME, name) 25 | 26 | 27 | #ifdef HASHMAP_INCLUDE_DECLARATIONS 28 | // Prototype declarations: 29 | // Normally these are copy-pasted and hand-converted in the user-level header 30 | // for better documentation, but you can define HASHMAP_INCLUDE_DECLARATIONS to 31 | // have these be auto-generated. 32 | 33 | #ifndef HASHMAP_KEY 34 | #error "please define HASHMAP_KEY" 35 | #endif 36 | #ifndef HASHMAP_VALUE 37 | #error "please define HASHMAP_VALUE" 38 | #endif 39 | 40 | 41 | // New creates a new map with initbuckets intial buckets. 42 | HASHMAP_NAME* HM_FUN(New)(u32 initbuckets, Mem) 43 | 44 | // Free frees all memory of a map, including the map's memory. 45 | // Use Free when you created a map with New. 46 | // Use Dispose when you manage the memory of the map yourself and used Init. 47 | void HM_FUN(Free)(HASHMAP_NAME*); 48 | 49 | // Init initializes a map structure. initbuckets is the number of initial buckets. 50 | void HM_FUN(Init)(HASHMAP_NAME*, u32 initbuckets, Mem mem); 51 | 52 | // Dispose frees buckets data (but not the hashmap itself.) 53 | // The hashmap is invalid after this call. Call Init to reuse. 54 | void HM_FUN(Dispose)(HASHMAP_NAME*); 55 | 56 | // Len returns the number of entries currently in the map 57 | static u32 HM_FUN(Len)(const HASHMAP_NAME*); 58 | 59 | // Get searches for key. Returns value, or NULL if not found. 60 | HASHMAP_VALUE nullable HM_FUN(Get)(const HASHMAP_NAME*, HASHMAP_KEY key); 61 | 62 | // Set inserts key=value into m. Returns the replaced value or NULL if not found. 63 | HASHMAP_VALUE nullable HM_FUN(Set)(HASHMAP_NAME*, HASHMAP_KEY key, HASHMAP_VALUE value); 64 | 65 | // Del removes value for key. Returns the removed value or NULL if not found. 66 | HASHMAP_VALUE nullable HM_FUN(Del)(HASHMAP_NAME*, HASHMAP_KEY key); 67 | 68 | // Clear removes all entries. In contrast to Free, map remains valid. 69 | void HM_FUN(Clear)(HASHMAP_NAME*); 70 | 71 | // Iterator function type. Set stop=true to stop iteration. 72 | typedef void(*HM_FUN(Iterator))(HASHMAP_KEY key, HASHMAP_VALUE value, bool* stop, void* userdata); 73 | 74 | // Iter iterates over entries of the map. 75 | void HM_FUN(Iter)(const HASHMAP_NAME*, HM_FUN(Iterator)*, void* userdata); 76 | 77 | 78 | #undef _HM_MAKE_FN_NAME 79 | #undef _HM_FUN 80 | #undef HM_FUN 81 | 82 | #endif /* HASHMAP_INCLUDE_DECLARATIONS */ 83 | 84 | 85 | inline static u32 HM_FUN(Len)(const HASHMAP_NAME* h) { 86 | return h->len; 87 | } 88 | 89 | 90 | ASSUME_NONNULL_END 91 | -------------------------------------------------------------------------------- /src/co/util/ptrmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // PtrMap maps (const void*) => (void*) 4 | // 5 | #define HASHMAP_NAME PtrMap 6 | #include "hashmap.h" 7 | #undef HASHMAP_NAME 8 | 9 | ASSUME_NONNULL_BEGIN 10 | 11 | // PtrMapInit initializes a map structure. initbuckets is the number of initial buckets. 12 | void PtrMapInit(PtrMap*, u32 initbuckets, Mem mem); 13 | 14 | static bool PtrMapIsInit(const PtrMap*); 15 | 16 | // PtrMapDispose frees heap memory used by a map, but leaves PtrMap untouched. 17 | void PtrMapDispose(PtrMap*); 18 | 19 | // Creates and initializes a new PtrMap in mem, or global memory if mem is NULL. 20 | PtrMap* PtrMapNew(u32 initbuckets, Mem mem); 21 | 22 | // PtrMapFree frees PtrMap along with its data. 23 | void PtrMapFree(PtrMap*); 24 | 25 | // PtrMapLen returns the number of entries currently in the map 26 | static u32 PtrMapLen(const PtrMap*); 27 | 28 | // PtrMapGet searches for key. Returns value, or NULL if not found. 29 | void* nullable PtrMapGet(const PtrMap*, const void* key); 30 | 31 | // PtrMapSet inserts key=value into m. Returns the replaced value or NULL if not found. 32 | void* nullable PtrMapSet(PtrMap*, const void* key, void* value); 33 | 34 | // PtrMapDel removes value for key. Returns the removed value or NULL if not found. 35 | void* nullable PtrMapDel(PtrMap*, const void* key); 36 | 37 | // PtrMapClear removes all entries. In contrast to PtrMapFree, map remains valid. 38 | void PtrMapClear(PtrMap*); 39 | 40 | // Iterator function type. Set stop=true to stop iteration. 41 | typedef void(PtrMapIterator)(const void* key, void* value, bool* stop, void* userdata); 42 | 43 | // PtrMapIter iterates over entries of the map. 44 | void PtrMapIter(const PtrMap*, PtrMapIterator*, void* userdata); 45 | 46 | 47 | inline static bool PtrMapIsInit(const PtrMap* m) { return m->buckets != NULL; } 48 | 49 | ASSUME_NONNULL_END 50 | -------------------------------------------------------------------------------- /src/co/util/rtimer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tmpstr.h" 3 | #include "tstyle.h" 4 | #include "rtimer.h" 5 | 6 | void rtimer_start(RTimer* rt) { 7 | rt->nstime = nanotime(); 8 | getrusage(RUSAGE_SELF, &rt->ru); 9 | } 10 | 11 | static u64 rtimer_real_time_duration(RTimer* rt) { 12 | return nanotime() - rt->nstime; 13 | } 14 | 15 | // static u64 rtimer_user_cpu_duration(RTimer* rt) { 16 | // struct rusage ru; 17 | // getrusage(RUSAGE_SELF, &ru); 18 | // u64 ns = (u64)(ru.ru_utime.tv_sec - rt->ru.ru_utime.tv_sec) * 1000000000; 19 | // return ns + (u64)(ru.ru_utime.tv_usec - rt->ru.ru_utime.tv_usec) * 1000; // libc with usec 20 | // // return ns + (u64)(ru.ru_utime.tv_nsec - rt->ru.ru_utime.tv_nsec); // libc with nsec 21 | // } 22 | 23 | u64 rtimer_duration(RTimer* rt) { 24 | //return rtimer_user_cpu_duration(rt); 25 | return rtimer_real_time_duration(rt); 26 | } 27 | 28 | Str rtimer_duration_str(RTimer* rt, Str s) { 29 | u64 duration = rtimer_duration(rt); 30 | char buf[40]; 31 | auto buflen = fmtduration(buf, countof(buf), duration); 32 | return str_append(s, buf, buflen); 33 | } 34 | 35 | void rtimer_log(RTimer* rt, const char* fmt, ...) { 36 | u64 duration = rtimer_duration(rt); 37 | 38 | Str* sp = tmpstr_get(); 39 | Str s = *sp; 40 | auto style = TSTyleForStderr(); 41 | s = str_appendcstr(s, style[TStyle_lightpurple]); 42 | s = str_appendcstr(s, "◔ "); 43 | 44 | char durbuf[40]; 45 | auto durbuflen = (u32)fmtduration(durbuf, countof(durbuf), duration); 46 | s = str_append(s, durbuf, durbuflen); 47 | 48 | // pad 49 | const char* spaces = " "; 50 | const u32 spaceslen = (u32)strlen(spaces); 51 | if (durbuflen < spaceslen) 52 | s = str_append(s, spaces, spaceslen - durbuflen); 53 | 54 | s = str_appendc(s, ' '); 55 | 56 | va_list ap; 57 | va_start(ap, fmt); 58 | s = str_appendfmtv(s, fmt, ap); 59 | va_end(ap); 60 | 61 | s = str_appendcstr(s, style[TStyle_none]); 62 | s = str_appendc(s, '\n'); 63 | fwrite(s, str_len(s), 1, stderr); 64 | *sp = s; // store back 65 | } 66 | -------------------------------------------------------------------------------- /src/co/util/rtimer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | ASSUME_NONNULL_BEGIN 3 | 4 | typedef struct RTimer { 5 | struct rusage ru; 6 | u64 nstime; 7 | } RTimer; 8 | 9 | void rtimer_start(RTimer* rt); 10 | u64 rtimer_duration(RTimer* rt); 11 | Str rtimer_duration_str(RTimer* rt, Str s); 12 | void rtimer_log(RTimer* rt, const char* fmt, ...); 13 | 14 | ASSUME_NONNULL_END 15 | -------------------------------------------------------------------------------- /src/co/util/sexpr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | ASSUME_NONNULL_BEGIN 3 | 4 | typedef struct SExpr SExpr; 5 | struct SExpr { 6 | SExpr* nullable next; 7 | enum { SExprList, SExprAtom } type; 8 | union { 9 | struct { 10 | u8 kind; // '(' or '[' or '{' 11 | SExpr* nullable head; 12 | } list; 13 | struct { 14 | const char* name; 15 | u32 namelen; 16 | } atom; 17 | }; 18 | }; 19 | 20 | typedef enum { 21 | SExprFmtDefault = 0, // separate values with spaces 22 | SExprFmtPretty = 1 << 0, // separate values with linebreaks and indentation 23 | } SExprFmtFlags; 24 | 25 | SExpr* sexpr_parse(const u8* src, u32 srclen, Mem mem); 26 | void sexpr_free(SExpr* n, Mem mem); // mem must be same as used with sexpr_parse 27 | Str sexpr_fmt(const SExpr* n, Str s, SExprFmtFlags); 28 | Str sexpr_prettyprint(Str dst, const char* src, u32 srclen); // returns dst with fmt appended 29 | 30 | ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /src/co/util/stk_array.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // STK_ARRAY is a simple array allocated on the stack if the size needed at runtime 4 | // is less or equal to the memory allocated on stack, else uses heap memory. 5 | // 6 | // STK_ARRAY_DEFINE(myarray,int, /* comptime on-stack count: */ 8); 7 | // STK_ARRAY_INIT(myarray,mem, /* runtime min count: */ needcount); 8 | // myarray[needcount - 1] = lastitem; 9 | // STK_ARRAY_DISPOSE(myarray); 10 | // 11 | #define STK_ARRAY_DEFINE(NAME,T,STKCAP) \ 12 | T NAME##_stk_[STKCAP]; \ 13 | T* NAME = NAME##_stk_; \ 14 | Mem NAME##_mem_ = NULL 15 | 16 | #define STK_ARRAY_INIT(NAME,mem,MINLEN) \ 17 | if ((MINLEN) > countof(NAME##_stk_)) { \ 18 | NAME##_mem_ = mem; \ 19 | NAME = memalloc((mem), sizeof(NAME##_stk_[0]) * (MINLEN)); \ 20 | } 21 | 22 | #define STK_ARRAY_MAKE(NAME,mem,T,STKCAP,MINLEN) \ 23 | STK_ARRAY_DEFINE(NAME,T,STKCAP); \ 24 | STK_ARRAY_INIT(NAME,mem,MINLEN) 25 | 26 | #define STK_ARRAY_DISPOSE(NAME) do { \ 27 | if (NAME##_mem_) { memfree(NAME##_mem_, NAME); } \ 28 | } while(0) 29 | -------------------------------------------------------------------------------- /src/co/util/str_extras.c: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | #include "stk_array.h" 3 | #include "str_extras.h" 4 | 5 | Str str_fmtpat(Str s, Mem mem, const char* fmt, u32 kvc, const char** kvv) { 6 | assertf(kvc % 2 == 0, "kvc=%u must be an even number", kvc); 7 | u32 fmtlen = strlen(fmt); assert_debug(fmtlen < 0x7fffffff); 8 | s = str_makeroom(s, fmtlen * 2); 9 | u32 chunk_start = 0; 10 | u32 keynest = 0; // "{" nesting level 11 | 12 | #define FLUSH_CHUNK(chunk_end) do { \ 13 | if (chunk_end > chunk_start) { \ 14 | s = str_append(s, &fmt[chunk_start], chunk_end - chunk_start); \ 15 | chunk_start = chunk_end; \ 16 | } \ 17 | } while(0) 18 | 19 | for (u32 i = 0; i < fmtlen; i++) { 20 | again: 21 | switch (fmt[i]) { 22 | case '\\': // note: not supported for keys 23 | FLUSH_CHUNK(i); 24 | i++; 25 | if (i < fmtlen) 26 | s = str_appendc(s, fmt[i]); 27 | chunk_start = i + 1; 28 | break; 29 | case '{': 30 | keynest++; 31 | if (keynest == 1) { 32 | FLUSH_CHUNK(i); 33 | chunk_start = i + 1; 34 | } 35 | break; 36 | case '}': 37 | if (keynest > 0) { 38 | keynest--; 39 | if (keynest == 0) { 40 | const char* key = &fmt[chunk_start]; 41 | u32 keylen = i - chunk_start; 42 | chunk_start = i + 1; 43 | if (keylen > 0) for (u32 j = 0; j < kvc; j += 2) { 44 | const char* k = kvv[j]; 45 | if (k[0] == key[0] && strncmp(k, key, keylen) == 0) { 46 | const char* v = kvv[j + 1]; 47 | s = str_appendcstr(s, v); 48 | i++; 49 | goto again; 50 | } 51 | } 52 | s = str_appendcstr(s, ""); 55 | } 56 | } 57 | break; 58 | default: 59 | break; 60 | } 61 | } 62 | 63 | FLUSH_CHUNK(fmtlen); 64 | #undef FLUSH_CHUNK 65 | 66 | return s; 67 | } 68 | 69 | R_TEST(str_fmtpat) { 70 | const char* kv[] = { 71 | "var1", "value1", 72 | "var2", "value2", 73 | }; 74 | const char* fmt = "foo {var1} bar {var2} \\{var1} {} baz {var3}."; 75 | Str s = str_fmtpat(str_new(0), MemHeap, fmt, countof(kv), kv); 76 | assertcstreq(s, "foo value1 bar value2 {var1} baz ."); 77 | str_free(s); 78 | } 79 | -------------------------------------------------------------------------------- /src/co/util/str_extras.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | Str str_fmtpat(Str s, Mem mem, const char* fmt, u32 kvc, const char** kvv); 3 | -------------------------------------------------------------------------------- /src/co/util/symmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "sym.h" 3 | 4 | // SymMap is a hash map that maps Sym => pointer 5 | #define HASHMAP_NAME SymMap 6 | #include "hashmap.h" 7 | #undef HASHMAP_NAME 8 | 9 | ASSUME_NONNULL_BEGIN 10 | 11 | // Creates and initializes a new SymMap in mem, or global memory if mem is NULL. 12 | SymMap* SymMapNew(u32 initbuckets, Mem mem); 13 | 14 | // SymMapInit initializes a map structure. initbuckets is the number of initial buckets. 15 | void SymMapInit(SymMap*, u32 initbuckets, Mem mem); 16 | 17 | // SymMapFree frees SymMap along with its data. 18 | void SymMapFree(SymMap*); 19 | 20 | // SymMapDispose frees heap memory used by a map, but not free the SymMap struct itself. 21 | void SymMapDispose(SymMap*); 22 | 23 | // SymMapLen returns the number of entries currently in the map 24 | static u32 SymMapLen(const SymMap*); 25 | 26 | // SymMapGet searches for key. Returns value, or NULL if not found. 27 | void* nullable SymMapGet(const SymMap*, Sym key); 28 | 29 | // SymMapSet inserts key=value into m. Returns the replaced value or NULL if not found. 30 | void* nullable SymMapSet(SymMap*, Sym key, void* value); 31 | 32 | // SymMapDel removes value for key. Returns the removed value or NULL if not found. 33 | void* nullable SymMapDel(SymMap*, Sym key); 34 | 35 | // SymMapClear removes all entries. In contrast to SymMapFree, map remains valid. 36 | void SymMapClear(SymMap*); 37 | 38 | // Iterator function type. Set stop=true to stop iteration. 39 | typedef void(*SymMapIterator)(Sym key, void* value, bool* stop, void* nullable userdata); 40 | 41 | // SymMapIter iterates over entries of the map. 42 | void SymMapIter(const SymMap*, SymMapIterator, void* nullable userdata); 43 | 44 | ASSUME_NONNULL_END 45 | -------------------------------------------------------------------------------- /src/co/util/tmpstr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tmpstr.h" 3 | 4 | // _tmpstr holds per-thread temporary string buffers for use by fmtnode and fmtast. 5 | static thread_local struct { 6 | u32 index; // next buffer index (effective index = index % TMPSTR_MAX_CONCURRENCY) 7 | Str bufs[TMPSTR_MAX_CONCURRENCY]; 8 | } _tmpstr = {0}; 9 | 10 | 11 | Str* tmpstr_get() { 12 | u32 bufindex = _tmpstr.index % TMPSTR_MAX_CONCURRENCY; 13 | _tmpstr.index++; 14 | Str s = _tmpstr.bufs[bufindex]; 15 | if (!s) { 16 | _tmpstr.bufs[bufindex] = str_new(64); 17 | } else { 18 | str_trunc(s); 19 | } 20 | return &_tmpstr.bufs[bufindex]; 21 | } 22 | -------------------------------------------------------------------------------- /src/co/util/tmpstr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | ASSUME_NONNULL_BEGIN 3 | 4 | // TMPSTR_MAX_CONCURRENCY is the limit of concurrent valid buffers returned by tmpstr_get. 5 | // For example, if TMPSTR_MAX_CONCURRENCY==2 then: 6 | // Str* a = tmpstr_get(); 7 | // Str* b = tmpstr_get(); 8 | // Str* c = tmpstr_get(); // same as a 9 | // 10 | #define TMPSTR_MAX_CONCURRENCY 8 11 | 12 | // tmpstr_get allocates the next temporary string buffer. 13 | // It is thread safe. 14 | // 15 | // Strs returned by this function are managed in a circular-buffer fashion; calling tmpstr_get 16 | // many times will eventually return the same Str. See TMPSTR_MAX_CONCURRENCY. 17 | // 18 | // If you return a temporary string to a caller, make sure to annotate its type as ConstStr 19 | // to communicate that the user must not modify it. 20 | // 21 | // Example: 22 | // ConstStr fmtnode(const Node* n) { 23 | // Str* sp = tmpstr_get(); // allocate 24 | // *sp = NodeStr(*sp, n); // use and update pointer 25 | // return *sp; // return to user 26 | // } 27 | // 28 | Str* tmpstr_get(); 29 | 30 | 31 | ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /src/co/util/tstyle.c: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | #include "tstyle.h" 3 | // #include // for isatty() 4 | 5 | const char* TStyle16[_TStyle_MAX] = { 6 | #define I_ENUM(name, c16, cRGB) "\x1b[" c16 "m", 7 | TSTYLE_STYLES(I_ENUM) 8 | #undef I_ENUM 9 | }; 10 | 11 | const char* TStyleRGB[_TStyle_MAX] = { 12 | #define I_ENUM(name, c16, cRGB) "\x1b[" cRGB "m", 13 | TSTYLE_STYLES(I_ENUM) 14 | #undef I_ENUM 15 | }; 16 | 17 | const char* TStyleNone[_TStyle_MAX] = { 18 | #define I_ENUM(name, c16, cRGB) "", 19 | TSTYLE_STYLES(I_ENUM) 20 | #undef I_ENUM 21 | }; 22 | 23 | 24 | bool TSTyleStdoutIsTTY() { 25 | static int g = -1; 26 | if (g == -1) 27 | g = isatty(STDOUT_FILENO) ? 1 : 0; 28 | return !!g; 29 | } 30 | 31 | bool TSTyleStderrIsTTY() { 32 | static int g = -1; 33 | if (g == -1) 34 | g = isatty(STDERR_FILENO) ? 1 : 0; 35 | return !!g; 36 | } 37 | 38 | TStyleTable TSTyleForTerm() { 39 | static TStyleTable t = NULL; 40 | if (t == NULL) { 41 | t = TStyleNone; 42 | const char* TERM = getenv("TERM"); 43 | if (TERM) { 44 | if (strstr(TERM, "256color")) { 45 | t = TStyleRGB; 46 | } else if (strstr(TERM, "xterm") || strstr(TERM, "screen") || strstr(TERM, "vt100")) { 47 | t = TStyle16; 48 | } 49 | } 50 | } 51 | return t; 52 | } 53 | 54 | TStyleTable TSTyleForStdout() { 55 | return TSTyleStdoutIsTTY() ? TSTyleForTerm() : TStyleNone; 56 | } 57 | 58 | TStyleTable TSTyleForStderr() { 59 | return TSTyleStdoutIsTTY() ? TSTyleForTerm() : TStyleNone; 60 | } 61 | 62 | // ---------------------------------------------------------------------------------------------- 63 | 64 | void StyleStackInit(StyleStack* sstack, TStyleTable styles) { 65 | ArrayInitWithStorage(&sstack->stack, sstack->stack_storage, countof(sstack->stack_storage)); 66 | assertnotnull(styles); 67 | sstack->styles = styles; 68 | } 69 | 70 | void StyleStackDispose(StyleStack* sstack) { 71 | ArrayFree(&sstack->stack, MemHeap); 72 | } 73 | 74 | static Str style_append(StyleStack* sstack, Str s, const char* suffix) { 75 | u32 len = (u32)strlen(suffix); 76 | sstack->nbyteswritten += len; 77 | return str_append(s, suffix, len); 78 | } 79 | 80 | static Str style_apply(StyleStack* sstack, Str s) { 81 | if (sstack->stack.len == 0) 82 | return style_append(sstack, s, sstack->styles[TStyle_none]); 83 | bool nofg = true; 84 | bool nobg = true; 85 | s = str_makeroom(s, sstack->stack.len * 8); 86 | for (u32 i = 0; i < sstack->stack.len; i++) { 87 | const char* style = (const char*)sstack->stack.v[i]; 88 | s = style_append(sstack, s, style); 89 | if (style[2] == '3' || style[2] == '9') { 90 | nofg = false; 91 | } else if (style[2] == '4') { 92 | nobg = false; 93 | } 94 | } 95 | if (nofg) 96 | s = style_append(sstack, s, sstack->styles[TStyle_defaultfg]); 97 | if (nobg) 98 | s = style_append(sstack, s, sstack->styles[TStyle_defaultbg]); 99 | return s; 100 | } 101 | 102 | Str StyleStackPush(StyleStack* sstack, Str s, TStyle style) { 103 | if (sstack->styles == TStyleNone) 104 | return s; 105 | const char* stylestr = sstack->styles[style]; 106 | ArrayPush(&sstack->stack, (void*)stylestr, MemHeap); 107 | return style_apply(sstack, s); 108 | } 109 | 110 | Str StyleStackPop(StyleStack* sstack, Str s) { 111 | if (sstack->styles == TStyleNone) 112 | return s; 113 | ArrayPop(&sstack->stack); 114 | return style_apply(sstack, s); 115 | } 116 | -------------------------------------------------------------------------------- /src/co/util/tstyle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "array.h" 3 | 4 | #define TSTYLE_STYLES(_) \ 5 | /* Name 16 RGB */ \ 6 | _(none, "0", "0") \ 7 | _(nocolor, "39", "39") \ 8 | _(defaultfg, "39", "39") \ 9 | _(defaultbg, "49", "49") \ 10 | _(bold, "1", "1") \ 11 | _(dim, "2", "2") \ 12 | _(nodim, "22", "22") \ 13 | _(italic, "3", "3") \ 14 | _(underline, "4", "4") \ 15 | _(inverse, "7", "7") \ 16 | _(white, "37", "38;2;255;255;255") \ 17 | _(grey, "90", "38;5;244") \ 18 | _(black, "30", "38;5;16") \ 19 | _(blue, "94", "38;5;75") \ 20 | _(lightblue, "94", "38;5;117") \ 21 | _(cyan, "96", "38;5;87") \ 22 | _(green, "92", "38;5;84") \ 23 | _(lightgreen, "92", "38;5;157") \ 24 | _(magenta, "95", "38;5;213") \ 25 | _(purple, "35", "38;5;141") \ 26 | _(lightpurple, "35", "38;5;183") \ 27 | _(pink, "35", "38;5;211") \ 28 | _(red, "91", "38;2;255;110;80") \ 29 | _(yellow, "33", "38;5;227") \ 30 | _(lightyellow, "93", "38;5;229") \ 31 | _(orange, "33", "38;5;215") \ 32 | /*END DEF_NODE_KINDS*/ 33 | 34 | // TStyle_red, TStyle_bold, etc. 35 | typedef enum { 36 | #define I_ENUM(name, c16, cRGB) TStyle_##name, 37 | TSTYLE_STYLES(I_ENUM) 38 | #undef I_ENUM 39 | _TStyle_MAX, 40 | } TStyle; 41 | 42 | // // Str functions. I.e. Str str_tstyle_red(Str s) => s with red wrapped around it 43 | // #define I_ENUM(name, c16, cRGB) Str str_tstyle_##name(Str s); 44 | // TSTYLE_STYLES(I_ENUM) 45 | // #undef I_ENUM 46 | // // Str str_tstyle_##name(Str s) { return sdscat(s, TStyleTable[TStyle_bold]); } 47 | 48 | 49 | extern const char* TStyle16[_TStyle_MAX]; 50 | extern const char* TStyleRGB[_TStyle_MAX]; 51 | extern const char* TStyleNone[_TStyle_MAX]; 52 | 53 | typedef const char** TStyleTable; 54 | 55 | bool TSTyleStdoutIsTTY(); 56 | bool TSTyleStderrIsTTY(); 57 | TStyleTable TSTyleForTerm(); // best for the current terminal 58 | TStyleTable TSTyleForStdout(); // best for the current terminal on stdout 59 | TStyleTable TSTyleForStderr(); // best for the current terminal on stderr 60 | 61 | typedef struct StyleStack { 62 | TStyleTable styles; 63 | Array stack; // [const char*] 64 | const char* stack_storage[4]; 65 | u32 nbyteswritten; 66 | } StyleStack; 67 | 68 | void StyleStackInit(StyleStack* sstack, TStyleTable styles); 69 | void StyleStackDispose(StyleStack* sstack); 70 | Str StyleStackPush(StyleStack* sstack, Str s, TStyle style); 71 | Str StyleStackPop(StyleStack* sstack, Str s); 72 | -------------------------------------------------------------------------------- /src/rt-test/rt-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | ASSUME_NONNULL_BEGIN 5 | 6 | #define GREEN "\e[1;32m" 7 | #define YELLOW "\e[1;33m" 8 | #define PURPLE "\e[1;35m" 9 | 10 | #define REINTERPRET_CAST(dsttype, value) ({ \ 11 | DIAGNOSTIC_IGNORE_PUSH(-Wstrict-aliasing) \ 12 | __typeof__(value) v = value; dsttype v2 = *((dsttype*)&v); \ 13 | DIAGNOSTIC_IGNORE_POP \ 14 | v2; \ 15 | }) 16 | 17 | 18 | static void fn3(uintptr_t arg1) { 19 | dlog(PURPLE "fn3 coroutine. arg1=%f", REINTERPRET_CAST(double, arg1)); 20 | dlog(PURPLE "EXIT"); 21 | } 22 | 23 | static void fn2() { 24 | dlog(YELLOW "fn2 coroutine"); 25 | dlog(YELLOW "spawn fn3"); 26 | t_spawn(fn3, REINTERPRET_CAST(uintptr_t, 12.34)); 27 | dlog(YELLOW "calling t_yield()"); 28 | t_yield(); 29 | dlog(YELLOW "back from yield"); 30 | // // msleep(1000); 31 | dlog(YELLOW "EXIT"); 32 | } 33 | 34 | static void fn1(uintptr_t arg1) { 35 | #define GREEN "\e[1;32m" 36 | dlog(GREEN "main coroutine. arg1=%zu", arg1); 37 | 38 | dlog(GREEN "spawn fn2"); 39 | t_spawn(fn2, 0); 40 | 41 | // // t_spawn_custom(fn2, /*stackmem*/NULL, /*stacksize*/4096*4); 42 | 43 | // static u8 smolstack[4096]; 44 | // t_spawn_custom(fn2, 0, 0, smolstack, sizeof(smolstack)); 45 | 46 | // // test to ensure user stacks are correctly aligned: 47 | // t_spawn_custom(fn2, &smolstack[1], sizeof(smolstack)-1); 48 | 49 | // #define spawnb(expr) ({ \ 50 | // newproc((EntryFun)&&label1, NULL, 0, NULL, 0); \ 51 | // label1: expr; \ 52 | // }) 53 | // spawnb(fn3(123, 4.56)); 54 | 55 | // msleep(1000); 56 | 57 | dlog(GREEN "calling t_yield()"); 58 | t_yield(); 59 | dlog(GREEN "back from yield; calling t_yield()"); 60 | t_yield(); 61 | dlog(GREEN "back from yield"); 62 | 63 | dlog(GREEN "EXIT"); 64 | } 65 | 66 | int main(int argc, const char** argv) { 67 | sched_main(fn1, 123); // never returns 68 | return 0; 69 | } 70 | 71 | ASSUME_NONNULL_END 72 | -------------------------------------------------------------------------------- /src/rt/chan-wip/chan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | ASSUME_NONNULL_BEGIN 3 | 4 | typedef struct Chan Chan; 5 | 6 | Chan* ChanNew(Mem mem, size_t elemsize, u32 cap); 7 | void ChanFree(Chan*); 8 | bool ChanSend(Chan*, const void* elem); 9 | void* ChanRecv(Chan*); 10 | 11 | ASSUME_NONNULL_END 12 | -------------------------------------------------------------------------------- /src/rt/fctx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | typedef void* fctx_t; 3 | 4 | typedef struct fctx_transfer_t { 5 | fctx_t ctx; 6 | void* data; 7 | } fctx_transfer_t; 8 | 9 | // sp = top of stack 10 | fctx_t make_fcontext(void* sp, size_t size, void(*fn)(fctx_transfer_t)); 11 | fctx_transfer_t jump_fcontext(fctx_t const to, void* vp); 12 | -------------------------------------------------------------------------------- /src/rt/sched.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | ASSUME_NONNULL_BEGIN 3 | 4 | // Main scheduling concepts: 5 | typedef struct T T; // Task (coroutine; "g" in Go parlance) 6 | typedef struct M M; // Machine (OS thread) 7 | typedef struct P P; // Processor (execution resource required to execute a T) 8 | // M must have an associated P to execute T, 9 | // however a M can be blocked or in a syscall w/o an associated P. 10 | 11 | typedef void(*EntryFun)(uintptr_t arg1); 12 | 13 | // Scheduler entry point. fn is the main coroutine body. Never returns. 14 | void _Noreturn sched_main(EntryFun fn, uintptr_t arg1); 15 | 16 | // sched_spawn schedules a new coroutine. 17 | // Returns 0 on success and -1 on error, in which case errno is set. 18 | int sched_spawn(EntryFun fn, uintptr_t arg1, void* nullable stackmem, size_t stacksize); 19 | 20 | #define t_spawn(fn, arg1) \ 21 | sched_spawn(fn, arg1, /*stackmem*/NULL, /*stacksize*/0); 22 | 23 | #define t_spawn_custom(fn, arg1, stackmem, stacksize) \ 24 | sched_spawn(fn, arg1, stackmem, stacksize); 25 | 26 | void t_yield(); 27 | 28 | 29 | ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /src/xxhash/LICENSE: -------------------------------------------------------------------------------- 1 | xxHash Library 2 | Copyright (c) 2012-2020 Yann Collet 3 | All rights reserved. 4 | 5 | BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, this 14 | list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | ---------------------------------------------------- 29 | 30 | xxhsum command line interface 31 | Copyright (c) 2013-2020 Yann Collet 32 | All rights reserved. 33 | 34 | GPL v2 License 35 | 36 | This program is free software; you can redistribute it and/or modify 37 | it under the terms of the GNU General Public License as published by 38 | the Free Software Foundation; either version 2 of the License, or 39 | (at your option) any later version. 40 | 41 | This program is distributed in the hope that it will be useful, 42 | but WITHOUT ANY WARRANTY; without even the implied warranty of 43 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 44 | GNU General Public License for more details. 45 | 46 | You should have received a copy of the GNU General Public License along 47 | with this program; if not, write to the Free Software Foundation, Inc., 48 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 49 | -------------------------------------------------------------------------------- /src/xxhash/cmakelists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(xxhash VERSION 0.1.0 LANGUAGES C) 3 | 4 | include($ENV{CKIT_DIR}/ckit.cmake) 5 | ckit_configure_project(C) 6 | ckit_require_package(rbase) 7 | 8 | add_library(${PROJECT_NAME} 9 | xxhash.c 10 | ) 11 | target_compile_options(${PROJECT_NAME} PRIVATE -Wno-implicit-fallthrough) 12 | target_link_libraries(${PROJECT_NAME} rbase) 13 | 14 | # ckit_define_test(${PROJECT_NAME}) 15 | -------------------------------------------------------------------------------- /src/xxhash/xxhash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * xxHash - Extremely Fast Hash algorithm 3 | * Copyright (C) 2012-2020 Yann Collet 4 | * 5 | * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are 9 | * met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above 14 | * copyright notice, this list of conditions and the following disclaimer 15 | * in the documentation and/or other materials provided with the 16 | * distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * You can contact the author at: 31 | * - xxHash homepage: https://www.xxhash.com 32 | * - xxHash source repository: https://github.com/Cyan4973/xxHash 33 | */ 34 | 35 | 36 | /* 37 | * xxhash.c instantiates functions defined in xxhash.h 38 | */ 39 | 40 | #define XXH_STATIC_LINKING_ONLY /* access advanced declarations */ 41 | #define XXH_IMPLEMENTATION /* access definitions */ 42 | 43 | #include "xxhash.h" 44 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ "$1" == "-w" ]; then 3 | shift 4 | exec ckit watch -wf=test/parse test "$@" 5 | fi 6 | exec ckit test "$@" 7 | -------------------------------------------------------------------------------- /test/parse/global-ideal-const.co: -------------------------------------------------------------------------------- 1 | x = 4 2 | 3 | /*!AST types usecount attrs 4 | (var x @const @unused @pub 5 | (0 refs) 6 | (IntLit 4 ) 7 | ) 8 | */ 9 | -------------------------------------------------------------------------------- /test/parse/indent-blocks.co: -------------------------------------------------------------------------------- 1 | fun A() 2 | a = 1 3 | if a < 3 4 | a = 5 5 | a 6 | 7 | fun B() { 8 | a = 1 9 | if a < 3 10 | a = 5 11 | a 12 | } 13 | 14 | fun C() { 15 | a = 1 16 | if a < 3 { a = 5 } 17 | a 18 | } 19 | 20 | fun D() 21 | a = 1 22 | if a < 3 { 23 | a = 5 24 | } 25 | a 26 | 27 | // line comment 28 | //!special 29 | /* block 30 | comment */ 31 | 32 | /*!AST 33 | ; should all parse identically 34 | (Fun A (params nil) (body (Block 35 | (var a (IntLit 1)) 36 | (If (cond (BinOp < (Id a (var a)) (IntLit 3))) 37 | (then (Assign (left (var a)) (right (IntLit 5))) )) 38 | (Id a (var a)) ))) 39 | 40 | (Fun B (params nil) (body (Block 41 | (var a (IntLit 1)) 42 | (If (cond (BinOp < (Id a (var a)) (IntLit 3))) 43 | (then (Assign (left (var a)) (right (IntLit 5))) )) 44 | (Id a (var a)) ))) 45 | 46 | (Fun C (params nil) (body (Block 47 | (var a (IntLit 1)) 48 | (If (cond (BinOp < (Id a (var a)) (IntLit 3))) 49 | (then (Assign (left (var a)) (right (IntLit 5))) )) 50 | (Id a (var a)) ))) 51 | 52 | (Fun D (params nil) (body (Block 53 | (var a (IntLit 1)) 54 | (If (cond (BinOp < (Id a (var a)) (IntLit 3))) 55 | (then (Assign (left (var a)) (right (IntLit 5))) )) 56 | (Id a (var a)) ))) 57 | 58 | 59 | */ 60 | -------------------------------------------------------------------------------- /test/parse/let-at-end-of-block.co: -------------------------------------------------------------------------------- 1 | fun a() { 2 | b() 3 | x = 4 4 | } 5 | fun b() { 6 | x int 7 | } 8 | fun c() -> x = 4 9 | fun d() -> var x int 10 | // fun d() -> x int // error since x is in rvalue position 11 | 12 | /*!AST 13 | (Fun a (params nil) (body 14 | (Block 15 | (Call (Id b nil) nil) 16 | (var x (IntLit 4))))) 17 | (Fun b (params nil) (body (var x nil))) 18 | (Fun c (params nil) (body (var x (IntLit 4)))) 19 | (Fun d (params nil) (body (var x nil))) 20 | 21 | */ 22 | --------------------------------------------------------------------------------