├── .gitignore ├── LICENSE ├── README.md ├── build.js ├── docs ├── .gitignore ├── _config.yml ├── _includes │ ├── highlight.html │ └── icons │ │ ├── logo.svg │ │ └── reset.svg ├── _layouts │ ├── default.html │ └── page.html ├── _scripts │ ├── build-co-lib.sh │ └── optimgs.sh ├── doc │ ├── chaitin │ │ ├── chaitin.js │ │ ├── common.js │ │ ├── index.css │ │ ├── index.html │ │ ├── slc.js │ │ └── svg.js │ ├── index.md │ ├── ir.md │ ├── patterns.md │ ├── r │ │ ├── build-pkg.svg │ │ ├── compiler-pipeline.svg │ │ ├── parser-pipeline.svg │ │ └── shared-resources.svg │ ├── spec.md │ ├── stack.md │ └── stack │ │ ├── index.md │ │ ├── stack-01.svg │ │ ├── stack-02.svg │ │ ├── stack-03.svg │ │ ├── stack-04.svg │ │ ├── stack-05.svg │ │ ├── stack-06.svg │ │ ├── stack-07.svg │ │ ├── stack-08.svg │ │ ├── stack-09.svg │ │ ├── stack-10.svg │ │ ├── stack-11.svg │ │ └── stack-12.svg ├── index.md ├── play │ ├── co.js │ ├── co.js.map │ └── index.html ├── r │ ├── favicon.ico │ ├── favicon.svg │ ├── highlight.js │ ├── initdoc.js │ ├── style.css │ ├── ted.css │ ├── ted.js │ └── uibindings.js └── serve.sh ├── example ├── base64.xl ├── basic.xl ├── bufcmp.xl ├── cyclic-type-ref.co ├── deadcode.co ├── declarations.xl ├── fun-return-types.xl ├── function-call-rest.co ├── function-call.co ├── functions.co ├── generic-typevar-sep.co ├── generics.co ├── id1.xl ├── identifiers.xl ├── imports.xl ├── inline-branch-elim.co ├── lists.co ├── literal-num.xl ├── macro-idea1.co ├── mix1.xl ├── numbers.xl ├── operators.xl ├── ownership.xl ├── record.co ├── return-types.xl ├── scope.xl ├── scope2.xl ├── scope3.xl ├── scope4.xl ├── slice-and-index.xl ├── ssa-bug1.co ├── ssa1.co ├── text.xl ├── tuples.xl ├── typeless-fun.xl └── types.xl ├── misc ├── fmtduration.c ├── gen-keywords.js ├── gen-unicode-data.js ├── gen-wasm.js ├── gen.js ├── language.md ├── parse-debugger.html ├── test-nbits.js └── test_rewrite_rules.co ├── package-lock.json ├── package.json ├── src ├── all_tests.ts ├── api.ts ├── arch │ ├── README.md │ ├── arch_covm.rewrite │ ├── arch_covm_ops.ts │ ├── arch_generic_ops.ts │ ├── describe.ts │ ├── gen.ts │ ├── ops_template.ts │ ├── run.sh │ └── tsconfig.json ├── asm │ ├── asm.ts │ └── fun.ts ├── ast.ts ├── ast │ ├── astio.ts │ ├── gen │ │ ├── ast.ts │ │ ├── main.ts │ │ ├── run.sh │ │ └── tsconfig.json │ ├── nodes.ts │ ├── repr.ts │ ├── scope.ts │ ├── test │ │ ├── ast_test.ts │ │ ├── empty.co │ │ ├── identifiers.co │ │ ├── literal-number-i64.co │ │ ├── template-expansion.co │ │ ├── template-future-def.co │ │ ├── template-pre-def.co │ │ ├── template-var-sep.co │ │ └── template.co │ ├── transform.ts │ └── visit.ts ├── bind.ts ├── btree.ts ├── bytestr.ts ├── cli.ts ├── debug.ts ├── diag.ts ├── error.ts ├── global.d.ts ├── global.js ├── int64.ts ├── int64.wast ├── int64_rand.ts ├── int64_rand.wast ├── int64_test.ts ├── intgraph.ts ├── intgraph_test.ts ├── intparse.ts ├── ir │ ├── arch.ts │ ├── arch_info.ts │ ├── blocktree.ts │ ├── builder.ts │ ├── config.ts │ ├── consteval.ts │ ├── copyelim.ts │ ├── deadcode.ts │ ├── dom.ts │ ├── layout.ts │ ├── localslot.ts │ ├── loopnest.ts │ ├── lower.ts │ ├── op.ts │ ├── ops.ts │ ├── opselect.ts │ ├── opt_cf.ts │ ├── opt_dce.ts │ ├── passes.ts │ ├── phielim.ts │ ├── postorder.ts │ ├── reg.ts │ ├── reg_desiredstate.ts │ ├── regalloc.ts │ ├── repr.ts │ ├── rewrite.ts │ ├── rewrite_covm.ts │ ├── shortcircuit.ts │ ├── ssa.ts │ └── vm.ts ├── main.ts ├── num.ts ├── numconv.ts ├── numeval.ts ├── nummath.ts ├── parse-debugger.ts ├── parser.ts ├── path.ts ├── pathmap.ts ├── pos.ts ├── resolve.ts ├── scan_num_test.ts ├── scanner.ts ├── scanner_test.ts ├── sexpr.ts ├── strtou.ts ├── template.ts ├── termstyle.ts ├── test.ts ├── time.ts ├── time_fmtduration.wast ├── token.ts ├── typecompat.ts ├── typeset.ts ├── unicode.ts ├── universe.ts ├── utf8.ts ├── util.ts └── vm │ └── vm.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.d 4 | *.gz 5 | /*.sublime-* 6 | src/*.wast.wasm 7 | _local 8 | /.build-cache 9 | /dist 10 | /report.* 11 | .DS_Store 12 | node_modules 13 | misc/UnicodeData.txt 14 | misc/emoji-data.txt 15 | * copy 16 | 17 | # code generator program output: 18 | src/**/.src-*.js 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Rasmus Andersson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Co programming language 2 | 3 | A programming language and optimizing compiler. 4 | 5 | This is a project about: 6 | 7 | - Designing a practical, general-purpose programming language 8 | - Statically typed, but the compiler does as much for you as possible 9 | - Simple syntax, few built-ins 10 | - More like Go than JavaScript 11 | - Optimizing compiler 12 | - Input-language agnostic 13 | - Intermediate Representation in SSA form 14 | - Flexible target architecture (x86, amd64, vm, wasm, etc.) 15 | - Pipelined rather than progressive 16 | - scanning source code into the parser 17 | - parser passes AST chunks to the IR builder 18 | - IR builder optimizes the chunks and then passes them to the code generator 19 | - All this happens in a streaming fashion 20 | - But each step can be easily separated and run independendly, making 21 | it easy to inspect the state of the compiler at various steps of the 22 | process. 23 | - Using no exernal libraries 24 | - Portable (can run in a web browser) 25 | - Simplify codebase 26 | - Minimize risk of security concerns 27 | - Maximize startup performance 28 | 29 | 30 | ## Using 31 | 32 | Requirements: [nodejs](https://nodejs.org/) >=8.0 33 | 34 | Building: 35 | - Setup: `npm install` 36 | - Build incrementally: `./build.js -w` 37 | - Run: `./dist/co.g` 38 | 39 | Some useful things: 40 | - Build debug version in one go: `./build.js` 41 | - Build optimized production version: `./build.js -O` 42 | - Run unit tests before main program: `./dist/co.g -test` 43 | - Run unit tests and exit: `./dist/co.g -test-only` 44 | - Print source diagnostics report and exit: `./dist/co.g -nobuild` 45 | - Live coding setup: 46 | - Terminal 1: `./build.js -w` 47 | - Terminal 2: `autorun dist/co.g` 48 | - You'll need [`autorun`](https://github.com/rsms/autorun) 49 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | /_site 2 | .jekyll* 3 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | port: 3000 2 | lsi: false 3 | permalink: /:title 4 | markdown: kramdown 5 | # Since GH pages override this to "true", we test this value to see if we are running locally 6 | safe: false 7 | include: [".well-known"] 8 | # highlighter: none 9 | # syntax_highlighter: none 10 | kramdown: 11 | input: GFM 12 | auto_ids: true 13 | hard_wrap: false 14 | # transliterated_header_ids: true 15 | # syntax_highlighter: null 16 | # syntax_highlighter: "coderay" 17 | syntax_highlighter_opts: 18 | disable: true 19 | -------------------------------------------------------------------------------- /docs/_includes/highlight.html: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /docs/_includes/icons/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_includes/icons/reset.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | {% capture build_version 2 | %}{{ site.time | date: "%Y%m%d%H%M%S" }}{% 3 | endcapture %}{% 4 | capture url_root 5 | %}{% if site.safe == false %}/{% else %}/co/{% endif 6 | %}{% 7 | endcapture %} 8 | 9 | 10 | 11 | {% if page.title %}{{ page.title }} — {% endif %}The Co programming language 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% if page.title %} 23 | 24 | 25 | {% if page.description %} 26 | 27 | 28 | 29 | {% else %} 30 | 31 | {% endif %} 32 | {% else %} 33 | 34 | {% endif %} 35 | {% if page.og_image_url %} 36 | 37 | 38 | {% endif %} 39 | 40 | 41 | 42 | 43 | 44 | 45 | {% if page.header == "minimal" %} 46 | 47 | {% include icons/logo.svg %} 48 | 49 | {% else %} 50 | 51 | {% include icons/logo.svg %} 52 | 53 | 69 | 83 | {% endif %} 84 | 85 | {{ content }} 86 | 87 | 88 | 89 | {% include highlight.html %} 90 | 91 | 92 | -------------------------------------------------------------------------------- /docs/_layouts/page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 |
5 | {{ content }} 6 |
7 | -------------------------------------------------------------------------------- /docs/_scripts/build-co-lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | cd "$(dirname "$0")/../.." 3 | 4 | ./build.js -O -lib -o docs/play/co.js 5 | -------------------------------------------------------------------------------- /docs/_scripts/optimgs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -o errexit -o pipefail -e 3 | cd "$(dirname "$0")/.." 4 | 5 | # svg 6 | for f in _includes/icons/*.svg r/*.svg doc/r/*.svg; do 7 | echo "svgo $f" 8 | svgo --config='{"plugins":[{"transformsWithOnePath":{}},{"removeViewBox":{}},{"removeAttrs":{"attrs":["figma.+"]}},{"removeTitle":{}},{"removeDesc":{"removeAny":true}}]}' \ 9 | --multipass \ 10 | -q \ 11 | "$f" & 12 | done 13 | 14 | pushd r >/dev/null 15 | for f in *.ico; do 16 | echo "pngcrush $f" 17 | TMPNAME=.$f.tmp 18 | (pngcrush -q "$f" "$TMPNAME" && mv -f "$TMPNAME" "$f") & 19 | done 20 | popd 21 | 22 | wait 23 | -------------------------------------------------------------------------------- /docs/doc/chaitin/chaitin.js: -------------------------------------------------------------------------------- 1 | 2 | class InterferenceGraph { 3 | constructor(svg) { 4 | this.nodes = {} 5 | this.edges = {} 6 | this.svg = Svg(svg) 7 | } 8 | 9 | // renderGraph( svgel : SvgElement, graph : Map> ) : void 10 | renderGraph(values) { 11 | // console.log('edges', JSON.stringify(edges)) 12 | let nvals = values.size 13 | 14 | // 360 deg in radians = PI•2 15 | let radmax = Math.PI * 2 16 | 17 | // radius and text offset of a value circle 18 | let circleR = 18 19 | let circleSpacing = circleR * 2 20 | let textYOffset = 4 21 | 22 | // guide outer margin 23 | let guideMargin = circleR 24 | 25 | // graph svg object 26 | let svg = this.svg 27 | 28 | // empty svg 29 | while (svg.n.childNodes.length) { 30 | svg.n.removeChild(svg.n.childNodes[0]) 31 | } 32 | 33 | // guide radius 34 | // maximum guide is size of graph minus some outer margin 35 | let guideRMax = (Math.min(svg.height, svg.width) / 2) - guideMargin 36 | // guide size is circumference computed from number of values + spacing 37 | // Circumference -> Radius: R = C/(2•PI) 38 | let guideR = (((circleR * 2) + circleSpacing) * nvals) / radmax 39 | // scale down if guideR > guideRMax 40 | let scale = 1, pt = 1 41 | if (guideR > guideRMax) { 42 | scale = guideRMax / guideR 43 | pt = 1 / scale 44 | let width = svg.width / scale 45 | let height = svg.height / scale 46 | svg.viewBox = { 47 | x: -(width - svg.width) / 2, 48 | y: -(height - svg.height) / 2, 49 | width, 50 | height 51 | } 52 | } 53 | 54 | let sceneCenter = { x: svg.width/2, y: svg.height/2 } 55 | 56 | let guideCircle = svg.add('circle', { 57 | class: 'guide', 58 | cx: sceneCenter.x, cy: sceneCenter.y, 59 | r: guideR, 60 | }) 61 | 62 | // nodes contains info on all node graphics 63 | let nodes = {} // { id : { x : int, y : int, circleNode : SvgNode } } 64 | 65 | // groups for nodes 66 | let nodesg = svg.add('g', { class: 'nodes' }) 67 | 68 | // // reverse order of names since the graph is built bottom-up, but 69 | // // really we want to think of it as top-down (the SLC, that is.) 70 | // let names = Array.from(values.keys()).reverse() 71 | 72 | // sort order of names since the graph is built bottom-up 73 | let names = Array.from(values.keys()).sort() 74 | 75 | // create nodes for names 76 | for (let i = 0; i < names.length; i++) { 77 | let name = names[i] 78 | 79 | let rad = ((i / nvals) * radmax) + (Math.PI*1.5) 80 | // Math.PI*1.5 = start at top 81 | let x = sceneCenter.x + (guideR * Math.cos(rad)) 82 | let y = sceneCenter.y + (guideR * Math.sin(rad)) 83 | 84 | let g = nodesg.add('g', { class: 'val' }) 85 | let circleNode = g.add('circle', { 86 | class: 'node', 87 | cx: x, 88 | cy: y, 89 | r: circleR, 90 | 'stroke-width': 1 * pt, 91 | fill: 'none', 92 | }) 93 | g.add('text', { x, y: y + textYOffset, 'text-anchor': 'middle' }, name) 94 | 95 | nodes[name] = { x, y, g, circleNode } 96 | } 97 | 98 | // relationships 99 | let relsg = svg.add('g', { class: 'rels' }) 100 | let edges = {} 101 | 102 | for (let p of values) { 103 | let id1 = p[0] 104 | for (let id2 of p[1]) { 105 | let key = this.edgeKey(id1, id2) 106 | if (!(key in edges)) { 107 | edges[key] = { ids: [ id1, id2 ] } 108 | } 109 | } 110 | } 111 | 112 | for (let key in edges) { 113 | let v = edges[key] 114 | 115 | let node1 = nodes[v.ids[0]] 116 | let node2 = nodes[v.ids[1]] 117 | 118 | let x1 = node1.x, y1 = node1.y 119 | let x2 = node2.x, y2 = node2.y 120 | 121 | // move line points out to edges of the circles (offset by circleR) 122 | let angle = Math.atan2(y2 - y1, x2 - x1) 123 | let offsx = Math.cos(angle) * circleR 124 | let offsy = Math.sin(angle) * circleR 125 | x1 += offsx 126 | y1 += offsy 127 | x2 -= offsx 128 | y2 -= offsy 129 | 130 | let line = relsg.add('line', { 131 | x1, y1, x2, y2, 132 | 'stroke-width': 1 * pt, 133 | class: 'rel', 134 | }) 135 | 136 | v.line = line 137 | } 138 | 139 | this.nodes = nodes 140 | this.edges = edges 141 | } 142 | 143 | 144 | edgeKey(id1, id2) { 145 | return id1 < id2 ? (id1 + " " + id2) : (id2 + " " + id1) 146 | } 147 | 148 | } // class Chaitin 149 | -------------------------------------------------------------------------------- /docs/doc/chaitin/common.js: -------------------------------------------------------------------------------- 1 | function $(q, e) { 2 | return (e || document).querySelector(q) 3 | } 4 | 5 | function $$(q, e) { 6 | return [].slice.apply((e || document).querySelectorAll(q)) 7 | } 8 | 9 | // monotime() :number 10 | let monotime = ( 11 | typeof performance != 'undefined' && performance.now ? 12 | performance.now.bind(performance) : 13 | () => (new Date()).getTime() * 1000 14 | ) 15 | 16 | let _templateCache = {} 17 | function template(id) { 18 | let t = _templateCache[id] 19 | if (!t) { 20 | t = document.getElementById('template-' + id) 21 | if (!t) { 22 | console.error('template "'+id+'" not found') 23 | return 24 | } 25 | t.parentNode.removeChild(t) 26 | t.removeAttribute('id') 27 | _templateCache[id] = t 28 | } 29 | return t.cloneNode(true) 30 | } 31 | -------------------------------------------------------------------------------- /docs/doc/chaitin/slc.js: -------------------------------------------------------------------------------- 1 | // straight-line code 2 | (function(slc){ 3 | 4 | 5 | function tokenize(src) { 6 | return src 7 | .replace(/^\/\/[^\n]*/gm, '') 8 | .replace(/[\n;]+/g, ';') 9 | .split(/[\t ]*\b[\t ]*/) 10 | } 11 | 12 | function value(vars, id, op, operands, consts) { 13 | // console.log('value', id, op, operands) 14 | if (operands) for (let id of operands) { 15 | let v = vars.get(id) 16 | if (!v) { 17 | vars.set(id, { id: id, type: 'implicit', uses: 1 }) 18 | } else { 19 | v.uses++ 20 | } 21 | } 22 | 23 | let v = { 24 | id, 25 | op, 26 | operands, 27 | consts, 28 | uses: 0, 29 | } 30 | vars.set(id, v) 31 | return v 32 | } 33 | 34 | function parse(src) { 35 | let tokens = tokenize(src.trim()) 36 | // console.log(tokens.join(' ')) 37 | let dst, op, operands, consts, seenAssign 38 | let values = [] 39 | let vars = new Map() 40 | 41 | const reset = () => { 42 | dst = '' 43 | op = '=' 44 | operands = [] 45 | consts = [] 46 | seenAssign = false 47 | } 48 | 49 | reset() 50 | 51 | for (let i = 0; i < tokens.length; i++) { 52 | let t = tokens[i] 53 | switch (t) { 54 | 55 | case '': 56 | break 57 | 58 | case '=': 59 | seenAssign = true 60 | break 61 | 62 | case ';': 63 | values.push(value(vars, dst, op, operands, consts)) 64 | reset() 65 | break 66 | 67 | case '+': 68 | case '-': 69 | case '*': 70 | case '/': 71 | op = t 72 | break 73 | 74 | default: 75 | if (!t[0].match(/\w/)) { 76 | // bad syntax. e.g. "+;" from "a = b +" 77 | throw new Error('invalid name ' + JSON.stringify(t)) 78 | } 79 | if (dst == '') { 80 | dst = t 81 | } else { 82 | if (t.match(/^\d/)) { 83 | consts.push(t) 84 | } else { 85 | operands.push(t) 86 | } 87 | } 88 | 89 | } 90 | } 91 | 92 | if (seenAssign) { 93 | values.push(value(vars, dst, op, operands, consts)) 94 | } 95 | 96 | return values 97 | } 98 | 99 | 100 | 101 | slc.parse = parse 102 | 103 | 104 | })(this.slc = {}); 105 | -------------------------------------------------------------------------------- /docs/doc/chaitin/svg.js: -------------------------------------------------------------------------------- 1 | 2 | function SvgNode(tag, attrs, text) { 3 | var n = document.createElementNS('http://www.w3.org/2000/svg', tag) 4 | if (attrs) for (let k in attrs) { 5 | n.setAttributeNS(null, k, attrs[k]) 6 | } 7 | if (text) n.textContent = text 8 | return Object.create(SvgNode.prototype, { 9 | n: {enumerable:true, value:n}, 10 | _attrs: { value:attrs }, 11 | }) 12 | } 13 | 14 | // add(tag, attrs?, text?) 15 | SvgNode.prototype.add = function (tag, attrs, text) { 16 | let n = SvgNode(tag, attrs, text) 17 | this.n.appendChild(n.n) 18 | return n 19 | } 20 | 21 | // get attr(name) => value 22 | // set attr(name, value) 23 | SvgNode.prototype.attr = function (name, value) { 24 | if (arguments.length > 1) { 25 | this._attrs[name] = value 26 | this.n.setAttributeNS(null, name, value) 27 | } else { 28 | return ( 29 | name in this._attrs ? this._attrs[name] : 30 | this.n.getAttributeNS(null, name) 31 | ) 32 | } 33 | } 34 | 35 | 36 | 37 | function Svg(n) { 38 | return Object.create(Svg.prototype, { 39 | n: {enumerable:true, value:n}, 40 | _attrs: { value: {} }, 41 | }) 42 | } 43 | 44 | Svg.prototype.add = SvgNode.prototype.add 45 | Svg.prototype.attr = SvgNode.prototype.attr 46 | 47 | Object.defineProperties(Svg.prototype, { 48 | 49 | width: { enumerable: true, 50 | set(width) { this.n.setAttribute('width', this._width = width) }, 51 | get() { 52 | return ( 53 | this._width !== undefined ? this._width : 54 | this._width = +this.n.getAttribute('width') 55 | ) 56 | } 57 | }, 58 | 59 | height: { enumerable: true, 60 | set(height) { this.n.setAttribute('height', this._height = height) }, 61 | get() { 62 | return ( 63 | this._height !== undefined ? this._height : 64 | this._height = +this.n.getAttribute('height') 65 | ) 66 | } 67 | }, 68 | 69 | viewBox: { enumerable: true, 70 | set({x=0, y=0, width=100, height=100}) { 71 | this._viewBox = {x, y, width, height} 72 | this.n.setAttribute('viewBox', `${x} ${y} ${width} ${height}`) 73 | }, 74 | get() { 75 | if (!this._viewBox) { 76 | let v = this.n.getAttribute('viewBox').split(/\s+/) 77 | this._viewBox = {x:0, y:0, width: this.width, height: this.height} 78 | if (v.length == 4) { 79 | this._viewBox.x = +v[0] 80 | this._viewBox.y = +v[1] 81 | this._viewBox.width = +v[2] 82 | this._viewBox.height = +v[3] 83 | } 84 | } 85 | return this._viewBox 86 | } 87 | } 88 | }) 89 | -------------------------------------------------------------------------------- /docs/doc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Documentation 4 | --- 5 | 6 | # Documentation 7 | 8 | 9 | ### [Language specification](spec.html) 10 | 11 | A slightly outdated document that will eventually be broken into 12 | smaller pieces. 13 | 14 | 15 | ## Architecture 16 | 17 | Code is structured into packages where a package can import other packages 18 | to make use of their code and data. 19 | A package is usually built from a number of source files, though source code 20 | need not be coming from files. 21 | Each package has its own namespace which holds all declarations, independently 22 | of where the declarations appear (i.e. source files.) 23 | 24 | ![build-pkg](r/build-pkg.svg) 25 | 26 | A package is built in a pipeline fashion, where each source file is 27 | concurrently processed. 28 | 29 | The parser pipeline converts UTF8 data into abstract syntax trees. 30 | It transforms bytes into tokens which are interpreted in a highly 31 | syntax-aware fashion, producing AST structures that represent the program 32 | in a lossless format. 33 | 34 | ![parser-pipeline](r/parser-pipeline.svg) 35 | 36 | 37 | The compiler pipeline operates at the granularity of functions and is mostly 38 | focused on Intermediate Representation (IR) code. 39 | An IR builder takes a function in AST form and builds a set of basic blocks 40 | in SSA form to represent the function at a essentially language-agnostic level. 41 | Optimizations are applied to the IR as a sequence of "passes", where some are 42 | always enabled and some are only enabled when "optimized" code is requested, 43 | trading time for code quality. 44 | 45 | ![compiler-pipeline](r/compiler-pipeline.svg) 46 | 47 | The IR builder is somewhat language-aware, as it converts AST into IR, but 48 | subsequent oprimization passes and code generation is language-agnostic and 49 | it would be possible to make use of this "back end" for other languages with 50 | similar runtime semantics to Co. 51 | 52 | An unresolved reference is an identifier which is unavailable or unknown at 53 | the time it is referenced. Usually this identifier is defined later in code, 54 | or in a separate source file. Anyhow, the compiler will stuff away functions 55 | which has unresolved references into a "wait set". 56 | Whenever an identifier is defined which causes a previously-unresolved 57 | function to become resolved (i.e. when all its references are known), that 58 | function is lifted out from the "wait set" and is compiled to completion. 59 | 60 | The parser and compiler pipeline makes use of some shared resources: 61 | 62 | ![shared-resources](r/shared-resources.svg) 63 | 64 | - Parser is a reusable resource that can parse source code from a list of bytes. 65 | - Universe represents built-in types and values and is shared amongst all 66 | parsers. 67 | - String set holds interned byte strings, allowing identifiers to internally 68 | be referenced by pointers rather than strings. 69 | - Type set holds interned user-type definitions, allowing types to internally 70 | be identified by pointers. 71 | - Type resolver is a reusable resource for resolving types from AST. 72 | It is allocated on a per-package basis. 73 | - IR builder is a resource that can either be provided with a parser 74 | allocation for online building of IR, 75 | or used separately to build IR from AST. 76 | - Package binder completes cross-references inside a package and manages 77 | importing of other packages. It can be run online by the parser, 78 | or as a separate step on AST. 79 | 80 | 81 | 82 | ## Topics 83 | 84 | [Intermediate representation (IR)](ir.html) — details on how code is 85 | represented internally and how SSA is used for optimizations. 86 | 87 | [Call stack](stack/) — discusses the call stack 88 | 89 | [Patterns](patterns.html) — 90 | Observations of common patterns in general programming and inspiration for Co 91 | 92 | [Chaitin-Briggs register allocation demo](chaitin/) — 93 | visualization of the Chaitin-Briggs graph-coloring register allocation 94 | algorithm. 95 | -------------------------------------------------------------------------------- /docs/doc/ir.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: IR 4 | --- 5 | 6 | # Intermediate Representation (IR) 7 | 8 | When a program is compiled, it passes through a "pipeline" 9 | which can be visualized like this: 10 | 11 | `source text` —> `tokens` —> `AST` —> `IR code` —> `target code` 12 | 13 | First, source text is parsed into an Abstract Syntax tree. This is where 14 | syntactic transformations, scope resolution, variable resolution and 15 | type resolution happens, as well as most error checking. 16 | After some piece of source code has been successfully 17 | translated into AST, Intermediate Representation (just "IR" from here on) is 18 | built from the AST. 19 | 20 | Here's a short example, starting with source code: 21 | 22 | ```kt 23 | fun foo(x, y int) int { 24 | if x > 3 { 25 | y++ 26 | } 27 | x + y 28 | } 29 | ``` 30 | 31 | The following AST is built from parsing the `foo` function: 32 | 33 | ```txt 34 | (fun <(int, int)->int> foo ((x int) (y int)) 35 | (block 36 | (if (GT x 3) 37 | (assign (y) INC) 38 | ) 39 | (return (ADD x y)) 40 | ) 41 | ) 42 | ``` 43 | 44 | And here is the IR generated for that function's AST: 45 | 46 | ```go 47 | foo (int int)->int 48 | b0: // function's entry block 49 | v1 = LoadParam [0] // load parameter x 50 | v2 = LoadParam [1] // load parameter y 51 | v3 = i32Const [3] // load constant 3 52 | v4 = i32Gt_u v1 v3 // x > 3 53 | v5 = i32Const [1] // load constant 1 54 | if v4 —> b1 b2 // if ... then goto b1, else goto b2 55 | 56 | b1: <— b0 // "then" branch of "if" 57 | v6 = i32Add v2 v5 // y + 1 58 | cont —> b2 // unconditionally continue to block 2 59 | 60 | b2: <— b0, b1 // end "if" ("else" branch) 61 | v8 = Phi v2 v6 // note that y varies with entry branch 62 | v9 = i32Add v1 v8 // x + y 63 | ret v9 // return x from function 64 | ``` 65 | 66 | We can observe a few things about this process: 67 | 68 | - The AST could be converted back to source code without loss of information, 69 | but the IR couldn't. 70 | - The IR is more verbose than the AST, but also a lot simpler, 71 | with just a single basic operation per line. 72 | - Branches are separated into blocks (aka "basic blocks") that connect to 73 | each other (`b0`, `b1`, etc are "blocks".) 74 | - The IR uses 75 | [SSA form](https://en.wikipedia.org/wiki/Static_single_assignment_form) which 76 | is why we don't see any traditional variable assignments, but instead just 77 | numbered variables (e.g. `v3`, `v10` etc.) 78 | - `x++` was simplified to `x = x + 1` 79 | - It looks similar to assembly language. This makes it easier to translate the 80 | IR to the target code. 81 | 82 | 83 | ## Internal constructs 84 | 85 | **Op** is an enum representing all basic operations (instructions). 86 | e.g. `i32Gt_u` for "compare two unsigned 32-bit integers with greater-then". 87 | There are a few special operations, like `Phi` which represents a SSA phi 88 | (branch merge point.) The set of operations closely matches the 89 | [instruction set of WebAssembly](https://webassembly.github.io/spec/core/syntax/instructions.html) 90 | 91 | **Value** is "a line of IR"; holds operation and operands in a 92 | Three-Address Code fashion. Uniquely numbered within a function. 93 | 94 | **Block** is basic block containing `Value`s. 95 | Contains links to predecessor blocks in `.preds` and to successors in `.succs`. 96 | Blocks forms the 97 | [Control Flow Graph](https://en.wikipedia.org/wiki/Control_flow_graph) 98 | of a function. 99 | 100 | **BlockKind** denotes what specific kind a block is; how a block exists. 101 | The `.control` property of the block controls the path taken from a block. 102 | 103 | | kind | control (x) | successors | notes 104 | | ---------- | -------------- | -------------- | -------- 105 | | `Plain` | *(nil)* | next | e.g. cont/goto 106 | | `If` | boolean | then, else 107 | | `Ret` | memory | *(none)* 108 | | `First` | | next | used by lowering pass 109 | 110 | **Fun** represents a function and contains a list of all blocks that comprises 111 | the function. `Fun` also maintains a constant cache and handles value and block 112 | ID allocation. 113 | 114 | **IRBuilder** builds IR for a package on a per-function basis 115 | -------------------------------------------------------------------------------- /docs/r/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsms/co/08634a87a6bc2d08f0cec58c798b9ef10e583536/docs/r/favicon.ico -------------------------------------------------------------------------------- /docs/r/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/r/initdoc.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | // analytics 4 | window.dataLayer = window.dataLayer || []; 5 | function g(){dataLayer.push(arguments);} 6 | g('js', new Date()); 7 | g('config', 'UA-105091131-3'); 8 | 9 | // helpers 10 | function $(q, e) { 11 | return (e || document).querySelector(q) 12 | } 13 | function $$(q, e) { 14 | return Array.prototype.slice.call((e || document).querySelectorAll(q)) 15 | } 16 | 17 | // header anchors 18 | var L = 1; 19 | while (L < 5) { 20 | $$('.content h'+L+'[id]').forEach(function(e, i) { 21 | if (e.id) { 22 | var a = document.createElement('a') 23 | a.href = '#' + e.id 24 | a.innerHTML = e.innerHTML 25 | e.innerText = '' 26 | e.appendChild(a) 27 | } 28 | }) 29 | L++ 30 | } 31 | 32 | // add'l CSS styles 33 | var _extraStyleSheet 34 | function getExtraStyleSheet() { 35 | if (!_extraStyleSheet) { 36 | // Create the