├── .gitignore ├── Cargo.toml ├── LICENSE_APACHE ├── LICENSE_MIT ├── README.md ├── demo.html ├── demo.js ├── pkg ├── polysubml_demo.js └── polysubml_demo_bg.wasm ├── rustfmt.toml ├── src ├── ast.rs ├── bound_pairs_set.rs ├── codegen.rs ├── core.rs ├── grammar.lalr ├── instantiate.rs ├── js.rs ├── lib.rs ├── main.rs ├── parse_types.rs ├── reachability.rs ├── spans.rs ├── type_errors.rs ├── typeck.rs ├── unwindmap.rs └── utils.rs └── tests └── combined.ml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | bin/ 5 | pkg/ 6 | wasm-pack.log 7 | 8 | local.html 9 | src/grammar.rs 10 | 11 | test*.ml 12 | perf.* 13 | *.svg 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "polysubml-demo" 3 | version = "1.0.0" 4 | authors = ["Robert Grosse "] 5 | license = "Apache-2.0/MIT" 6 | edition = "2024" 7 | 8 | [lib] 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [features] 12 | default = ["console_error_panic_hook"] 13 | 14 | [dependencies] 15 | lalrpop-util = { version = "0.20.2", features = ["lexer"] } 16 | wasm-bindgen = "0.2.63" 17 | 18 | # The `console_error_panic_hook` crate provides better debugging of panics by 19 | # logging them with `console.error`. This is great for development, but requires 20 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 21 | # code size when deploying. 22 | console_error_panic_hook = { version = "0.1.6", optional = true } 23 | lasso = "0.7.3" 24 | itertools = "0.14.0" 25 | 26 | [dev-dependencies] 27 | wasm-bindgen-test = "0.3.13" 28 | 29 | [profile.release] 30 | # Tell `rustc` to optimize for small code size. 31 | opt-level = "s" 32 | -------------------------------------------------------------------------------- /LICENSE_APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /LICENSE_MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2025 Robert Grosse 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PolySubML demo 5 | 6 | 7 | 8 | 9 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /demo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | let mod = null; 5 | const mod_promise = import('./pkg/polysubml_demo.js').then( 6 | m => (mod = m, mod.default())); 7 | 8 | class PolysubmlDemo extends HTMLElement { 9 | constructor() { 10 | // Always call super first in constructor 11 | super(); 12 | 13 | // Create a shadow root 14 | // const shadow = this.attachShadow({mode: 'open'}); 15 | // shadow.innerHTML = HTML; 16 | const shadow = this.attachInternals().shadowRoot; 17 | 18 | mod_promise.then( 19 | wasm => initializeRepl(shadow, mod.State.new(), Printer)).catch( 20 | e => {shadow.getElementById('loading').textContent = 'Failed to load demo: ' + e}); 21 | 22 | } 23 | } 24 | customElements.define('polysubml-demo', PolysubmlDemo); 25 | 26 | 27 | function initializeRepl(root, compiler, Printer) { 28 | console.log('Initializing REPL'); 29 | const container = root.getElementById('container'); 30 | const output = root.getElementById('output'); 31 | const prompt = root.getElementById('prompt'); 32 | const editor = root.getElementById('editor'); 33 | 34 | function addOutput(line, cls) { 35 | const l = document.createElement('pre'); 36 | l.textContent = line; 37 | if (cls) { 38 | l.classList.add(cls); 39 | } 40 | output.appendChild(l); 41 | return l; 42 | } 43 | 44 | const $ = Object.create(null); 45 | const history = []; 46 | let history_offset = -1; 47 | 48 | function execCode(script) { 49 | let compiled; 50 | try { 51 | if (!compiler.process(script)) {return [false, compiler.get_err()];} 52 | compiled = '(' + compiler.get_output() + ')'; 53 | } catch (e) { 54 | return [false, 'Internal compiler error: ' + e.toString() + 55 | '\nIf you see this message, please file an issue on Github with the code required to trigger this error.']; 56 | } 57 | 58 | try { 59 | const p = new Printer; 60 | const val = eval(compiled); 61 | p.visitRoot(val); 62 | return [true, p.parts.join('')]; 63 | } catch (e) { 64 | return [false, 'An error occurred during evaluation in the repl: ' + e.toString()]; 65 | } 66 | } 67 | 68 | function processCode(script) { 69 | const [success, res] = execCode(script); 70 | addOutput(res, success ? 'success' : 'error'); 71 | // scroll output window to the bottom 72 | output.scrollTop = output.scrollHeight; 73 | return success; 74 | } 75 | 76 | 77 | function processReplInput(line) { 78 | line = line.trim(); 79 | if (!line) {return;} 80 | 81 | history_offset = -1; 82 | if (history[history.length-1] !== line) {history.push(line);} 83 | // \u00a0 = non breaking space 84 | addOutput('>>\u00a0' + line, 'input'); 85 | processCode(line); 86 | } 87 | 88 | root.getElementById('compile-and-run').addEventListener('click', e => { 89 | const s = editor.value.trim(); 90 | if (!s) {return;} 91 | 92 | // Clear repl output 93 | output.textContent = ''; 94 | compiler.reset(); 95 | if (processCode(s)) {prompt.focus({preventScroll: true})} 96 | }); 97 | 98 | // Implement repl command history 99 | prompt.addEventListener('keydown', e => { 100 | switch (e.key) { 101 | case 'ArrowDown': history_offset -= 1; break; 102 | case 'ArrowUp': history_offset += 1; break; 103 | default: return; 104 | } 105 | e.preventDefault(); 106 | 107 | if (history_offset >= history.length) {history_offset = history.length - 1;} 108 | if (history_offset < 0) {history_offset = 0;} 109 | prompt.value = history[history.length - history_offset - 1]; 110 | }); 111 | 112 | // If they click in the space below the prompt, focus on the prompt to make it easier to select 113 | root.getElementById('space-below-prompt').addEventListener('click', e => { 114 | e.preventDefault(); 115 | prompt.focus({preventScroll: true}); 116 | }); 117 | 118 | root.getElementById('rhs-form').addEventListener('submit', e => { 119 | e.preventDefault(); 120 | const s = prompt.value.trim(); 121 | prompt.value = ''; 122 | 123 | if (!s) {return;} 124 | processReplInput(s); 125 | }); 126 | 127 | container.classList.remove('loading'); 128 | prompt.disabled = false; 129 | container.removeChild(root.getElementById('loading')); 130 | console.log('Initialized REPL'); 131 | 132 | // Run the example code 133 | processCode(editor.value.trim()) 134 | } 135 | 136 | class Printer { 137 | constructor() { 138 | this.parts = []; 139 | this.seen = new WeakSet; 140 | this.current_size = 0; 141 | } 142 | 143 | push(s) { 144 | this.parts.push(s); 145 | this.current_size += s.length; 146 | } 147 | 148 | visitRoot(e) { 149 | this.seen = new WeakSet; 150 | this.current_size = 0; 151 | this.visit(e); 152 | } 153 | 154 | visit(e) { 155 | const type = typeof e; 156 | if (type === 'boolean' || type === 'bigint') {this.push(e.toString()); return;} 157 | if (type === 'string') {this.push(JSON.stringify(e)); return;} 158 | if (type === 'number') { 159 | let s = e.toString(); 160 | if (/^-?\d+$/.test(s)) {s += '.0'} 161 | this.push(s); 162 | return; 163 | } 164 | if (type === 'function') {this.push(''); return;} 165 | if (type === 'symbol') {this.push(''); return;} 166 | if (e === null) {this.push('null'); return;} 167 | if (e === undefined) {this.push(''); return;} 168 | 169 | if (this.seen.has(e)) {this.push('...'); return;} 170 | this.seen.add(e); 171 | 172 | const LIMIT = 80; 173 | if (this.current_size > LIMIT) {this.push('...'); return;} 174 | 175 | if (e.$tag) { 176 | this.push(e.$tag); 177 | if (!e.$val || typeof e.$val !== 'object') { 178 | this.push(' '); 179 | } 180 | this.visit(e.$val); 181 | } else { 182 | // Tuple-like objects 183 | const entries = new Map(Object.entries(e)); 184 | if (entries.size >= 2 && [...Array(entries.size).keys()].every(i => entries.has('_'+i))) { 185 | this.push('('); 186 | for (let i=0; i < entries.size; ++i) { 187 | if (i>0) {this.push(', ')} 188 | if (this.current_size > LIMIT) {this.push('...'); break;} 189 | 190 | this.visit(entries.get('_'+i)); 191 | } 192 | this.push(')'); 193 | } else { 194 | this.push('{'); 195 | let first = true; 196 | for (const [k, v] of entries) { 197 | if (!first) {this.push('; ')} 198 | first = false; 199 | if (this.current_size > LIMIT) {this.push('...'); break;} 200 | 201 | this.push(k + '='); 202 | this.visit(v); 203 | } 204 | this.push('}'); 205 | } 206 | } 207 | } 208 | 209 | println(...args) { 210 | for (let arg of args) { 211 | if (typeof arg === 'string') { 212 | this.push(arg); 213 | } else { 214 | this.visitRoot(arg); 215 | } 216 | 217 | this.push(' '); 218 | } 219 | this.parts.pop(); 220 | this.push('\n'); 221 | } 222 | 223 | // print(e) {this.visit(e); return this.parts.join('');} 224 | } 225 | 226 | // This function exists to be called from within PolySubML code 227 | // and is available implicitly when we eval() the compiled code in execCode. 228 | function loop(expr) { 229 | let v = expr(); 230 | while (v.$tag === 'Continue') { 231 | v = expr(); 232 | } 233 | return v.$val; 234 | } -------------------------------------------------------------------------------- /pkg/polysubml_demo.js: -------------------------------------------------------------------------------- 1 | let wasm; 2 | 3 | const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); 4 | 5 | if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; 6 | 7 | let cachedUint8ArrayMemory0 = null; 8 | 9 | function getUint8ArrayMemory0() { 10 | if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { 11 | cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); 12 | } 13 | return cachedUint8ArrayMemory0; 14 | } 15 | 16 | function getStringFromWasm0(ptr, len) { 17 | ptr = ptr >>> 0; 18 | return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); 19 | } 20 | 21 | let WASM_VECTOR_LEN = 0; 22 | 23 | const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); 24 | 25 | const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 26 | ? function (arg, view) { 27 | return cachedTextEncoder.encodeInto(arg, view); 28 | } 29 | : function (arg, view) { 30 | const buf = cachedTextEncoder.encode(arg); 31 | view.set(buf); 32 | return { 33 | read: arg.length, 34 | written: buf.length 35 | }; 36 | }); 37 | 38 | function passStringToWasm0(arg, malloc, realloc) { 39 | 40 | if (realloc === undefined) { 41 | const buf = cachedTextEncoder.encode(arg); 42 | const ptr = malloc(buf.length, 1) >>> 0; 43 | getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); 44 | WASM_VECTOR_LEN = buf.length; 45 | return ptr; 46 | } 47 | 48 | let len = arg.length; 49 | let ptr = malloc(len, 1) >>> 0; 50 | 51 | const mem = getUint8ArrayMemory0(); 52 | 53 | let offset = 0; 54 | 55 | for (; offset < len; offset++) { 56 | const code = arg.charCodeAt(offset); 57 | if (code > 0x7F) break; 58 | mem[ptr + offset] = code; 59 | } 60 | 61 | if (offset !== len) { 62 | if (offset !== 0) { 63 | arg = arg.slice(offset); 64 | } 65 | ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; 66 | const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); 67 | const ret = encodeString(arg, view); 68 | 69 | offset += ret.written; 70 | ptr = realloc(ptr, len, offset, 1) >>> 0; 71 | } 72 | 73 | WASM_VECTOR_LEN = offset; 74 | return ptr; 75 | } 76 | 77 | const StateFinalization = (typeof FinalizationRegistry === 'undefined') 78 | ? { register: () => {}, unregister: () => {} } 79 | : new FinalizationRegistry(ptr => wasm.__wbg_state_free(ptr >>> 0, 1)); 80 | 81 | export class State { 82 | 83 | static __wrap(ptr) { 84 | ptr = ptr >>> 0; 85 | const obj = Object.create(State.prototype); 86 | obj.__wbg_ptr = ptr; 87 | StateFinalization.register(obj, obj.__wbg_ptr, obj); 88 | return obj; 89 | } 90 | 91 | __destroy_into_raw() { 92 | const ptr = this.__wbg_ptr; 93 | this.__wbg_ptr = 0; 94 | StateFinalization.unregister(this); 95 | return ptr; 96 | } 97 | 98 | free() { 99 | const ptr = this.__destroy_into_raw(); 100 | wasm.__wbg_state_free(ptr, 0); 101 | } 102 | /** 103 | * @returns {State} 104 | */ 105 | static new() { 106 | const ret = wasm.state_new(); 107 | return State.__wrap(ret); 108 | } 109 | /** 110 | * @param {string} source 111 | * @returns {boolean} 112 | */ 113 | process(source) { 114 | const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 115 | const len0 = WASM_VECTOR_LEN; 116 | const ret = wasm.state_process(this.__wbg_ptr, ptr0, len0); 117 | return ret !== 0; 118 | } 119 | /** 120 | * @returns {string | undefined} 121 | */ 122 | get_output() { 123 | const ret = wasm.state_get_output(this.__wbg_ptr); 124 | let v1; 125 | if (ret[0] !== 0) { 126 | v1 = getStringFromWasm0(ret[0], ret[1]).slice(); 127 | wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); 128 | } 129 | return v1; 130 | } 131 | /** 132 | * @returns {string | undefined} 133 | */ 134 | get_err() { 135 | const ret = wasm.state_get_err(this.__wbg_ptr); 136 | let v1; 137 | if (ret[0] !== 0) { 138 | v1 = getStringFromWasm0(ret[0], ret[1]).slice(); 139 | wasm.__wbindgen_free(ret[0], ret[1] * 1, 1); 140 | } 141 | return v1; 142 | } 143 | reset() { 144 | wasm.state_reset(this.__wbg_ptr); 145 | } 146 | } 147 | 148 | async function __wbg_load(module, imports) { 149 | if (typeof Response === 'function' && module instanceof Response) { 150 | if (typeof WebAssembly.instantiateStreaming === 'function') { 151 | try { 152 | return await WebAssembly.instantiateStreaming(module, imports); 153 | 154 | } catch (e) { 155 | if (module.headers.get('Content-Type') != 'application/wasm') { 156 | console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); 157 | 158 | } else { 159 | throw e; 160 | } 161 | } 162 | } 163 | 164 | const bytes = await module.arrayBuffer(); 165 | return await WebAssembly.instantiate(bytes, imports); 166 | 167 | } else { 168 | const instance = await WebAssembly.instantiate(module, imports); 169 | 170 | if (instance instanceof WebAssembly.Instance) { 171 | return { instance, module }; 172 | 173 | } else { 174 | return instance; 175 | } 176 | } 177 | } 178 | 179 | function __wbg_get_imports() { 180 | const imports = {}; 181 | imports.wbg = {}; 182 | imports.wbg.__wbindgen_init_externref_table = function() { 183 | const table = wasm.__wbindgen_export_0; 184 | const offset = table.grow(4); 185 | table.set(0, undefined); 186 | table.set(offset + 0, undefined); 187 | table.set(offset + 1, null); 188 | table.set(offset + 2, true); 189 | table.set(offset + 3, false); 190 | ; 191 | }; 192 | imports.wbg.__wbindgen_throw = function(arg0, arg1) { 193 | throw new Error(getStringFromWasm0(arg0, arg1)); 194 | }; 195 | 196 | return imports; 197 | } 198 | 199 | function __wbg_init_memory(imports, memory) { 200 | 201 | } 202 | 203 | function __wbg_finalize_init(instance, module) { 204 | wasm = instance.exports; 205 | __wbg_init.__wbindgen_wasm_module = module; 206 | cachedUint8ArrayMemory0 = null; 207 | 208 | 209 | wasm.__wbindgen_start(); 210 | return wasm; 211 | } 212 | 213 | function initSync(module) { 214 | if (wasm !== undefined) return wasm; 215 | 216 | 217 | if (typeof module !== 'undefined') { 218 | if (Object.getPrototypeOf(module) === Object.prototype) { 219 | ({module} = module) 220 | } else { 221 | console.warn('using deprecated parameters for `initSync()`; pass a single object instead') 222 | } 223 | } 224 | 225 | const imports = __wbg_get_imports(); 226 | 227 | __wbg_init_memory(imports); 228 | 229 | if (!(module instanceof WebAssembly.Module)) { 230 | module = new WebAssembly.Module(module); 231 | } 232 | 233 | const instance = new WebAssembly.Instance(module, imports); 234 | 235 | return __wbg_finalize_init(instance, module); 236 | } 237 | 238 | async function __wbg_init(module_or_path) { 239 | if (wasm !== undefined) return wasm; 240 | 241 | 242 | if (typeof module_or_path !== 'undefined') { 243 | if (Object.getPrototypeOf(module_or_path) === Object.prototype) { 244 | ({module_or_path} = module_or_path) 245 | } else { 246 | console.warn('using deprecated parameters for the initialization function; pass a single object instead') 247 | } 248 | } 249 | 250 | if (typeof module_or_path === 'undefined') { 251 | module_or_path = new URL('polysubml_demo_bg.wasm', import.meta.url); 252 | } 253 | const imports = __wbg_get_imports(); 254 | 255 | if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { 256 | module_or_path = fetch(module_or_path); 257 | } 258 | 259 | __wbg_init_memory(imports); 260 | 261 | const { instance, module } = await __wbg_load(await module_or_path, imports); 262 | 263 | return __wbg_finalize_init(instance, module); 264 | } 265 | 266 | export { initSync }; 267 | export default __wbg_init; 268 | -------------------------------------------------------------------------------- /pkg/polysubml_demo_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Storyyeller/polysubml-demo/6e2beedb969750213622a7f7dce6867ce25b1817/pkg/polysubml_demo_bg.wasm -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 125 2 | imports_granularity = "Item" -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | use crate::spans::Span; 2 | use crate::spans::SpanMaker; 3 | use crate::spans::Spanned; 4 | 5 | pub struct ParserContext<'a, 'input> { 6 | pub span_maker: SpanMaker<'input>, 7 | pub strings: &'a mut lasso::Rodeo, 8 | } 9 | pub type StringId = lasso::Spur; 10 | 11 | #[derive(Debug, Clone)] 12 | pub enum Literal { 13 | Bool, 14 | Float, 15 | Int, 16 | Str, 17 | } 18 | 19 | #[derive(Debug, Clone)] 20 | pub enum Op { 21 | Add, 22 | Sub, 23 | Mult, 24 | Div, 25 | Rem, 26 | 27 | Lt, 28 | Lte, 29 | Gt, 30 | Gte, 31 | 32 | Eq, 33 | Neq, 34 | } 35 | 36 | pub type OpType = (Option, Literal); 37 | pub const INT_OP: OpType = (Some(Literal::Int), Literal::Int); 38 | pub const FLOAT_OP: OpType = (Some(Literal::Float), Literal::Float); 39 | pub const STR_OP: OpType = (Some(Literal::Str), Literal::Str); 40 | pub const INT_CMP: OpType = (Some(Literal::Int), Literal::Bool); 41 | pub const FLOAT_CMP: OpType = (Some(Literal::Float), Literal::Bool); 42 | pub const ANY_CMP: OpType = (None, Literal::Bool); 43 | 44 | type LetDefinition = (LetPattern, Box); 45 | pub type LetRecDefinition = (StringId, Spanned); 46 | 47 | #[derive(Debug, Clone)] 48 | pub enum LetPattern { 49 | Case(Spanned, Box), 50 | Record(Spanned<(Vec, Vec<(Spanned, Box)>)>), 51 | Var((Option, Span), Option), 52 | } 53 | 54 | #[derive(Debug, Clone, Copy)] 55 | pub struct TypeParam { 56 | pub name: Spanned, 57 | pub alias: Spanned, 58 | } 59 | impl TypeParam { 60 | pub fn new(name: Spanned, alias: Option>) -> Self { 61 | let alias = alias.unwrap_or(name); 62 | Self { name, alias } 63 | } 64 | } 65 | 66 | #[derive(Debug, Clone, Copy)] 67 | pub enum InstantiateSourceKind { 68 | ImplicitCall, 69 | ImplicitRecord, 70 | ExplicitParams(bool), 71 | } 72 | 73 | pub type KeyPairExprSub = (Spanned, Box, Option); 74 | pub type KeyPairExpr = (Spanned, Box, bool, Option); 75 | 76 | #[derive(Debug, Clone)] 77 | pub enum Expr { 78 | BinOp(Box, Span, Box, Span, OpType, Op, Span), 79 | Block(Vec, Box), 80 | Call(Box, Box, Span), 81 | Case(Spanned, Box), 82 | FieldAccess(Box, Spanned, Span), 83 | FieldSet(Box, Spanned, Box, Span), 84 | FuncDef(Spanned<(Option>, Spanned, Option, Box)>), 85 | If(Spanned>, Box, Box), 86 | InstantiateExist(Box, Spanned>, InstantiateSourceKind, Span), 87 | InstantiateUni( 88 | Spanned>, 89 | Spanned>, 90 | InstantiateSourceKind, 91 | Span, 92 | ), 93 | Literal(Literal, Spanned), 94 | Loop(Box, Span), 95 | Match(Spanned>, Vec<(Spanned, Box)>, Span), 96 | Record(Vec, Span), 97 | Typed(Box, STypeExpr), 98 | Variable(Spanned), 99 | } 100 | 101 | #[derive(Debug, Clone)] 102 | pub enum FieldTypeDecl { 103 | Imm(STypeExpr), 104 | RWSame(STypeExpr), 105 | RWPair(STypeExpr, STypeExpr), 106 | } 107 | pub type KeyPairType = (Spanned, FieldTypeDecl); 108 | 109 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 110 | pub enum PolyKind { 111 | Universal, 112 | Existential, 113 | } 114 | 115 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 116 | pub enum JoinKind { 117 | Union, 118 | Intersect, 119 | } 120 | 121 | #[derive(Debug, Clone)] 122 | pub enum TypeExpr { 123 | Bot, 124 | Case(Vec<(Spanned, Box)>), 125 | Func(Box, Box), 126 | Hole, 127 | Ident(StringId), 128 | Poly(Vec, Box, PolyKind), 129 | Record(Vec), 130 | RecursiveDef(StringId, Box), 131 | Top, 132 | VarJoin(JoinKind, Vec), 133 | } 134 | pub type STypeExpr = Spanned; 135 | 136 | #[derive(Debug, Clone)] 137 | pub enum Statement { 138 | Empty, 139 | Expr(Expr), 140 | LetDef(LetDefinition), 141 | LetRecDef(Vec), 142 | Println(Vec), 143 | } 144 | 145 | // Helper function for processing a list of sub-ast nodes, adding the _n fields, and creating a parent node 146 | pub fn make_tuple_ast( 147 | vals: Spanned>>, 148 | strings: &mut lasso::Rodeo, 149 | mut make_field: impl FnMut(Spanned, T) -> FieldT, 150 | make_result: impl FnOnce(Vec, Span) -> T, 151 | ) -> T { 152 | let (mut vals, full_span) = vals; 153 | if vals.len() <= 1 { 154 | return vals.pop().unwrap().0; 155 | } 156 | 157 | // Tuple 158 | let fields = vals 159 | .into_iter() 160 | .enumerate() 161 | .map(|(i, (val, span))| { 162 | let name = format!("_{}", i); 163 | let name = strings.get_or_intern(&name); 164 | make_field((name, span), val) 165 | }) 166 | .collect(); 167 | make_result(fields, full_span) 168 | } 169 | 170 | pub fn make_join_ast(kind: JoinKind, mut children: Vec) -> TypeExpr { 171 | if children.len() <= 1 { 172 | children.pop().unwrap().0 173 | } else { 174 | TypeExpr::VarJoin(kind, children) 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/bound_pairs_set.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::collections::HashSet; 3 | use std::rc::Rc; 4 | 5 | use crate::core::VarSpec; 6 | use crate::parse_types::SourceLoc; 7 | 8 | #[derive(Debug, Clone, Default, PartialEq, Eq)] 9 | struct BoundPairsSetSub(HashMap); 10 | impl BoundPairsSetSub { 11 | fn filter_left(&mut self, mut f: impl FnMut(SourceLoc) -> bool) { 12 | self.0.retain(|k, v| f(*k)); 13 | } 14 | 15 | fn filter_right(&mut self, mut f: impl FnMut(SourceLoc) -> bool) { 16 | self.0.retain(|k, v| f(*v)); 17 | } 18 | 19 | fn push(&mut self, pair: (SourceLoc, SourceLoc)) { 20 | self.0.insert(pair.0, pair.1); 21 | } 22 | 23 | fn update_intersect(&mut self, other: &Self) { 24 | self.0.retain(|k, v| other.get(*k, *v)); 25 | } 26 | 27 | fn update_intersect_flipped(&mut self, other: &Self) { 28 | self.0.retain(|k, v| other.get(*v, *k)); 29 | } 30 | 31 | fn get(&self, loc1: SourceLoc, loc2: SourceLoc) -> bool { 32 | self.0.get(&loc1).copied() == Some(loc2) 33 | } 34 | } 35 | 36 | #[derive(Debug, Clone, Default)] 37 | pub struct BoundPairsSet(Option>, bool); 38 | impl BoundPairsSet { 39 | fn as_ptr(&self) -> (Option<*const BoundPairsSetSub>, bool) { 40 | (self.0.as_ref().map(Rc::as_ptr), self.1) 41 | } 42 | 43 | pub fn clear(&mut self) { 44 | *self = Self::default(); 45 | } 46 | 47 | fn mutate(&mut self, f: impl FnOnce(&mut BoundPairsSetSub)) -> bool { 48 | let mut sub = self.0.as_ref().map(|r| BoundPairsSetSub::clone(r)).unwrap_or_default(); 49 | f(&mut sub); 50 | let sub = if sub.0.is_empty() { None } else { Some(Rc::new(sub)) }; 51 | 52 | // If there were no actual changes, reuse the existing value rather than the copied one 53 | // Also return if there was a change or not 54 | if sub != self.0 { 55 | self.0 = sub; 56 | true 57 | } else { 58 | false 59 | } 60 | } 61 | 62 | pub fn filter_left(&mut self, f: impl FnMut(SourceLoc) -> bool) { 63 | if self.1 { 64 | self.mutate(|sub| sub.filter_right(f)); 65 | } else { 66 | self.mutate(|sub| sub.filter_left(f)); 67 | } 68 | } 69 | 70 | pub fn filter_right(&mut self, f: impl FnMut(SourceLoc) -> bool) { 71 | if self.1 { 72 | self.mutate(|sub| sub.filter_left(f)); 73 | } else { 74 | self.mutate(|sub| sub.filter_right(f)); 75 | } 76 | } 77 | 78 | pub fn push(&mut self, mut pair: (SourceLoc, SourceLoc)) { 79 | if self.1 { 80 | pair = (pair.1, pair.0); 81 | } 82 | self.mutate(|sub| sub.push(pair)); 83 | } 84 | 85 | pub fn flip(&self) -> Self { 86 | Self(self.0.clone(), !self.1) 87 | } 88 | 89 | pub fn update_intersect(&mut self, other: &Self) -> bool { 90 | // Skip intersection when self === other 91 | if self.as_ptr() == other.as_ptr() { 92 | return false; 93 | } 94 | 95 | let flip = self.1 != other.1; 96 | if let Some(other) = other.0.as_ref() { 97 | if flip { 98 | self.mutate(|sub| sub.update_intersect_flipped(other)) 99 | } else { 100 | self.mutate(|sub| sub.update_intersect(other)) 101 | } 102 | } else { 103 | self.clear(); 104 | true 105 | } 106 | } 107 | 108 | pub fn get(&self, loc1: SourceLoc, loc2: SourceLoc) -> bool { 109 | if let Some(sub) = self.0.as_ref() { 110 | if self.1 { sub.get(loc2, loc1) } else { sub.get(loc1, loc2) } 111 | } else { 112 | // Empty set 113 | false 114 | } 115 | } 116 | 117 | // Return true if there's any (loc, loc2) in self such that (loc, name) is in lhs and (loc2, name) is in rhs 118 | pub fn disjoint_union_vars_have_match<'a>(&self, mut lhs: &'a HashSet, mut rhs: &'a HashSet) -> bool { 119 | if self.1 { 120 | (lhs, rhs) = (rhs, lhs); 121 | } 122 | 123 | if let Some(sub) = self.0.as_ref() { 124 | for spec in lhs.iter().copied() { 125 | if let Some(loc2) = sub.0.get(&spec.loc).copied() { 126 | let expect = VarSpec { 127 | loc: loc2, 128 | name: spec.name, 129 | }; 130 | if rhs.contains(&expect) { 131 | return true; 132 | } 133 | } else { 134 | continue; 135 | } 136 | } 137 | } else { 138 | // Empty set 139 | } 140 | 141 | false 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/codegen.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::mem::swap; 3 | 4 | use crate::ast; 5 | use crate::ast::StringId; 6 | use crate::js; 7 | use crate::unwindmap::UnwindMap; 8 | 9 | pub struct ModuleBuilder { 10 | scope_expr: js::Expr, 11 | scope_counter: u64, 12 | param_counter: u64, 13 | // For choosing new var names 14 | var_counter: u64, 15 | // ML name -> JS expr for current scope 16 | bindings: UnwindMap, 17 | } 18 | impl ModuleBuilder { 19 | pub fn new() -> Self { 20 | Self { 21 | scope_expr: js::var("$".to_string()), 22 | scope_counter: 0, 23 | param_counter: 0, 24 | var_counter: 0, 25 | bindings: UnwindMap::new(), 26 | } 27 | } 28 | 29 | fn set_binding(&mut self, k: StringId, v: js::Expr) { 30 | self.bindings.insert(k, v); 31 | } 32 | 33 | fn new_var_name(&mut self) -> String { 34 | let js_name = format!("v{}", self.var_counter); 35 | self.var_counter += 1; 36 | js_name 37 | } 38 | 39 | fn new_temp_var(&mut self) -> js::Expr { 40 | let js_name = format!("t{}", self.var_counter); 41 | self.var_counter += 1; 42 | 43 | js::field(self.scope_expr.clone(), js_name) 44 | } 45 | 46 | fn new_var(&mut self, ml_name: StringId) -> js::Expr { 47 | let js_name = self.new_var_name(); 48 | let expr = js::field(self.scope_expr.clone(), js_name); 49 | self.set_binding(ml_name, expr.clone()); 50 | expr 51 | } 52 | 53 | fn new_scope_name(&mut self) -> String { 54 | let js_name = format!("s{}", self.scope_counter); 55 | self.scope_counter += 1; 56 | js_name 57 | } 58 | 59 | fn new_param_name(&mut self) -> String { 60 | let js_name = format!("p{}", self.param_counter); 61 | self.param_counter += 1; 62 | js_name 63 | } 64 | } 65 | pub struct Context<'a>(pub &'a mut ModuleBuilder, pub &'a lasso::Rodeo); 66 | impl<'a> Context<'a> { 67 | fn ml_scope(&mut self, cb: impl FnOnce(&mut Self) -> T) -> T { 68 | let n = self.bindings.unwind_point(); 69 | let res = cb(self); 70 | self.bindings.unwind(n); 71 | res 72 | } 73 | 74 | fn fn_scope(&mut self, cb: impl FnOnce(&mut Self) -> T) -> T { 75 | let old_var_counter = self.var_counter; 76 | let old_param_counter = self.param_counter; 77 | let old_scope_counter = self.scope_counter; 78 | self.var_counter = 0; 79 | 80 | let res = self.ml_scope(cb); 81 | 82 | self.var_counter = old_var_counter; 83 | self.param_counter = old_param_counter; 84 | self.scope_counter = old_scope_counter; 85 | res 86 | } 87 | 88 | fn get(&self, id: StringId) -> &'a str { 89 | self.1.resolve(&id) 90 | } 91 | 92 | fn get_new(&self, id: StringId) -> String { 93 | self.1.resolve(&id).to_owned() 94 | } 95 | } 96 | impl<'a> core::ops::Deref for Context<'a> { 97 | type Target = ModuleBuilder; 98 | 99 | fn deref(&self) -> &Self::Target { 100 | &self.0 101 | } 102 | } 103 | impl<'a> core::ops::DerefMut for Context<'a> { 104 | fn deref_mut(&mut self) -> &mut Self::Target { 105 | &mut self.0 106 | } 107 | } 108 | 109 | fn compile(ctx: &mut Context<'_>, expr: &ast::Expr) -> js::Expr { 110 | match expr { 111 | ast::Expr::BinOp(lhs_expr, lhs_span, rhs_expr, rhs_span, op_type, op, full_span) => { 112 | let lhs = compile(ctx, lhs_expr); 113 | let rhs = compile(ctx, rhs_expr); 114 | let jsop = match op { 115 | ast::Op::Add => js::Op::Add, 116 | ast::Op::Sub => js::Op::Sub, 117 | ast::Op::Mult => js::Op::Mult, 118 | ast::Op::Div => js::Op::Div, 119 | ast::Op::Rem => js::Op::Rem, 120 | 121 | ast::Op::Lt => js::Op::Lt, 122 | ast::Op::Lte => js::Op::Lte, 123 | ast::Op::Gt => js::Op::Gt, 124 | ast::Op::Gte => js::Op::Gte, 125 | 126 | ast::Op::Eq => js::Op::Eq, 127 | ast::Op::Neq => js::Op::Neq, 128 | }; 129 | js::binop(lhs, rhs, jsop) 130 | } 131 | ast::Expr::Block(statements, rest_expr) => { 132 | ctx.ml_scope(|ctx| { 133 | let mut exprs = Vec::new(); // a list of assignments, followed by rest 134 | 135 | for stmt in statements { 136 | compile_statement(ctx, &mut exprs, stmt); 137 | } 138 | 139 | exprs.push(compile(ctx, rest_expr)); 140 | js::comma_list(exprs) 141 | }) 142 | } 143 | ast::Expr::Call(func, arg, _) => { 144 | let lhs = compile(ctx, func); 145 | let rhs = compile(ctx, arg); 146 | js::call(lhs, rhs) 147 | } 148 | ast::Expr::Case((tag, _), expr) => { 149 | let tag = js::lit(format!("\"{}\"", ctx.get(*tag))); 150 | let expr = compile(ctx, expr); 151 | js::obj(vec![("$tag".to_string(), tag), ("$val".to_string(), expr)]) 152 | } 153 | ast::Expr::FieldAccess(lhs_expr, (name, _), _) => { 154 | let lhs = compile(ctx, lhs_expr); 155 | js::field(lhs, ctx.get_new(*name)) 156 | } 157 | ast::Expr::FieldSet(lhs_expr, (name, _), rhs_expr, _) => { 158 | let mut exprs = Vec::new(); 159 | 160 | let lhs_temp_var = ctx.new_temp_var(); 161 | exprs.push(js::assign(lhs_temp_var.clone(), compile(ctx, lhs_expr))); 162 | let lhs = js::field(lhs_temp_var, ctx.get_new(*name)); 163 | 164 | let res_temp_var = ctx.new_temp_var(); 165 | exprs.push(js::assign(res_temp_var.clone(), lhs.clone())); 166 | exprs.push(js::assign(lhs.clone(), compile(ctx, rhs_expr))); 167 | exprs.push(res_temp_var); 168 | 169 | js::comma_list(exprs) 170 | } 171 | ast::Expr::FuncDef(((_, (arg_pattern, _), _, body_expr), _)) => { 172 | ctx.fn_scope(|ctx| { 173 | let new_scope_name = ctx.new_scope_name(); 174 | let mut scope_expr = js::var(new_scope_name.clone()); 175 | swap(&mut scope_expr, &mut ctx.scope_expr); 176 | 177 | ////////////////////////////////////////////////////// 178 | let js_pattern = compile_let_pattern(ctx, arg_pattern).unwrap_or_else(|| js::var("_".to_string())); 179 | let body = compile(ctx, body_expr); 180 | ////////////////////////////////////////////////////// 181 | 182 | swap(&mut scope_expr, &mut ctx.scope_expr); 183 | js::func(js_pattern, new_scope_name, body) 184 | }) 185 | } 186 | ast::Expr::If((cond_expr, _), then_expr, else_expr) => { 187 | let cond_expr = compile(ctx, cond_expr); 188 | let then_expr = compile(ctx, then_expr); 189 | let else_expr = compile(ctx, else_expr); 190 | js::ternary(cond_expr, then_expr, else_expr) 191 | } 192 | ast::Expr::InstantiateExist(expr, _, _, _) => compile(ctx, expr), 193 | ast::Expr::InstantiateUni((expr, _), _, _, _) => compile(ctx, expr), 194 | ast::Expr::Literal(type_, (code, _)) => { 195 | let mut code = code.clone(); 196 | if let ast::Literal::Int = type_ { 197 | code.push_str("n"); 198 | } 199 | if code.starts_with("-") { 200 | js::unary_minus(js::lit(code[1..].to_string())) 201 | } else { 202 | js::lit(code) 203 | } 204 | } 205 | ast::Expr::Loop(body, _) => { 206 | let lhs = js::var("loop".to_string()); 207 | let rhs = compile(ctx, body); 208 | let rhs = js::func(js::var("_".to_string()), "_2".to_string(), rhs); 209 | js::call(lhs, rhs) 210 | } 211 | ast::Expr::Match((match_expr, _), cases, _) => { 212 | let temp_var = ctx.new_temp_var(); 213 | let part1 = js::assign(temp_var.clone(), compile(ctx, match_expr)); 214 | 215 | let tag_expr = js::field(temp_var.clone(), "$tag".to_string()); 216 | let val_expr = js::field(temp_var.clone(), "$val".to_string()); 217 | 218 | let mut branches = Vec::new(); 219 | let mut wildcard = None; 220 | for ((pattern, _), rhs_expr) in cases { 221 | use ast::LetPattern::*; 222 | match pattern { 223 | Case((tag, _), sub_pattern) => { 224 | ctx.ml_scope(|ctx| { 225 | let mut exprs = Vec::new(); 226 | compile_let_pattern_flat(ctx, &mut exprs, sub_pattern, val_expr.clone()); 227 | exprs.push(compile(ctx, rhs_expr)); 228 | branches.push((ctx.get(*tag), js::comma_list(exprs))); 229 | }); 230 | } 231 | _ => { 232 | wildcard = Some(ctx.ml_scope(|ctx| { 233 | let mut exprs = Vec::new(); 234 | compile_let_pattern_flat(ctx, &mut exprs, pattern, temp_var.clone()); 235 | exprs.push(compile(ctx, rhs_expr)); 236 | js::comma_list(exprs) 237 | })); 238 | } 239 | } 240 | } 241 | 242 | let mut res = wildcard.unwrap_or_else(|| branches.pop().unwrap().1); 243 | while let Some((tag, rhs_expr)) = branches.pop() { 244 | assert!(tag.len() > 0); 245 | let cond = js::eqop(tag_expr.clone(), js::lit(format!("\"{}\"", tag))); 246 | res = js::ternary(cond, rhs_expr, res); 247 | } 248 | js::comma_pair(part1, res) 249 | } 250 | ast::Expr::Record(fields, span) => js::obj( 251 | fields 252 | .iter() 253 | .map(|((name, _), expr, _, _)| (ctx.get_new(*name), compile(ctx, expr))) 254 | .collect(), 255 | ), 256 | ast::Expr::Typed(expr, _) => compile(ctx, expr), 257 | ast::Expr::Variable((name, _)) => ctx.bindings.get(name).unwrap().clone(), 258 | } 259 | } 260 | 261 | fn compile_let_pattern_flat(ctx: &mut Context<'_>, out: &mut Vec, pat: &ast::LetPattern, rhs: js::Expr) { 262 | use ast::LetPattern::*; 263 | match pat { 264 | Case(_, val_pat) => { 265 | // rhs.$val 266 | let rhs = js::field(rhs, "$val".to_string()); 267 | compile_let_pattern_flat(ctx, out, val_pat, rhs); 268 | } 269 | Record(((_, pairs), _)) => { 270 | // Assign the rhs to a temporary value, and then do a = temp.foo for each field 271 | let lhs = ctx.new_temp_var(); 272 | out.push(js::assign(lhs.clone(), rhs)); 273 | 274 | for ((name, _), pat) in pairs.iter() { 275 | compile_let_pattern_flat(ctx, out, pat, js::field(lhs.clone(), ctx.get_new(*name))); 276 | } 277 | } 278 | 279 | Var((ml_name, _), _) => { 280 | if let Some(ml_name) = ml_name { 281 | let lhs = ctx.new_var(*ml_name); 282 | out.push(js::assign(lhs, rhs)); 283 | } 284 | } 285 | } 286 | } 287 | 288 | fn compile_let_pattern(ctx: &mut Context<'_>, pat: &ast::LetPattern) -> Option { 289 | use ast::LetPattern::*; 290 | Some(match pat { 291 | Case(_, val_pat) => js::obj(vec![("$val".to_string(), compile_let_pattern(ctx, &*val_pat)?)]), 292 | Record(((_, pairs), _)) => js::obj( 293 | pairs 294 | .iter() 295 | .filter_map(|((name, _), pat)| Some((ctx.get_new(*name), compile_let_pattern(ctx, &*pat)?))) 296 | .collect(), 297 | ), 298 | 299 | Var((ml_name, _), _) => { 300 | let js_arg = js::var(ctx.new_param_name()); 301 | let ml_name = ml_name.as_ref()?; 302 | ctx.set_binding(*ml_name, js_arg.clone()); 303 | js_arg 304 | } 305 | }) 306 | } 307 | 308 | fn compile_statement(ctx: &mut Context<'_>, exprs: &mut Vec, stmt: &ast::Statement) { 309 | use ast::Statement::*; 310 | match stmt { 311 | Empty => {} 312 | Expr(expr) => exprs.push(compile(ctx, expr)), 313 | LetDef((pat, var_expr)) => { 314 | let rhs = compile(ctx, var_expr); 315 | compile_let_pattern_flat(ctx, exprs, pat, rhs); 316 | } 317 | LetRecDef(defs) => { 318 | let mut vars = Vec::new(); 319 | let mut rhs_exprs = Vec::new(); 320 | for (name, _) in defs { 321 | vars.push(ctx.new_var(*name)) 322 | } 323 | for (_, (expr, _)) in defs { 324 | rhs_exprs.push(compile(ctx, expr)) 325 | } 326 | 327 | for (lhs, rhs) in vars.into_iter().zip(rhs_exprs) { 328 | exprs.push(js::assign(lhs, rhs)); 329 | } 330 | } 331 | Println(args) => { 332 | let args = args.iter().map(|expr| compile(ctx, expr)).collect(); 333 | exprs.push(js::println(args)); 334 | } 335 | } 336 | } 337 | 338 | pub fn compile_script(ctx: &mut Context<'_>, parsed: &[ast::Statement]) -> js::Expr { 339 | let mut exprs = Vec::new(); 340 | 341 | for item in parsed { 342 | compile_statement(ctx, &mut exprs, item); 343 | } 344 | 345 | if exprs.is_empty() { 346 | js::lit("0".to_string()) 347 | } else { 348 | js::comma_list(exprs) 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /src/core.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::HashMap; 3 | use std::collections::HashSet; 4 | use std::error; 5 | use std::fmt; 6 | use std::rc::Rc; 7 | 8 | use crate::ast::InstantiateSourceKind; 9 | use crate::ast::PolyKind; 10 | use crate::ast::StringId; 11 | use crate::bound_pairs_set::BoundPairsSet; 12 | use crate::instantiate::InstantionContext; 13 | use crate::instantiate::Substitutions; 14 | use crate::parse_types::PolyDeps; 15 | use crate::parse_types::SourceLoc; 16 | use crate::reachability; 17 | use crate::reachability::EdgeDataTrait; 18 | use crate::reachability::ExtNodeDataTrait; 19 | use crate::reachability::Reachability; 20 | use crate::reachability::TypeNodeInd; 21 | use crate::spans::Span; 22 | use crate::spans::SpannedError as TypeError; 23 | use crate::type_errors::HoleSrc; 24 | use crate::type_errors::PartialTypeError; 25 | use crate::type_errors::immutable_field_err; 26 | use crate::type_errors::missing_field_err; 27 | use crate::type_errors::poisoned_poly_err; 28 | use crate::type_errors::type_escape_error; 29 | use crate::type_errors::type_mismatch_err; 30 | use crate::type_errors::unhandled_variant_err; 31 | use crate::unwindmap::sorted; 32 | 33 | const NONE: TypeNodeInd = TypeNodeInd(usize::MAX); 34 | pub const INVALID: TypeNodeInd = TypeNodeInd(usize::MAX - 1); 35 | 36 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 37 | pub struct Value(pub TypeNodeInd); 38 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 39 | pub struct Use(pub TypeNodeInd); 40 | 41 | /// Tracks which types were in scope when a given hole was created 42 | /// This is an integer which is incremeneted whenever one or more 43 | /// types are added to the bindings. 44 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 45 | pub struct ScopeLvl(pub u32); 46 | 47 | #[derive(Debug)] 48 | pub struct TypeCtor { 49 | pub name: StringId, 50 | pub span: Option, // None for builtin type ctors 51 | pub scopelvl: ScopeLvl, 52 | debug: String, 53 | } 54 | impl TypeCtor { 55 | fn new(name: StringId, span: Option, scopelvl: ScopeLvl) -> Self { 56 | let debug = format!("{}@{:?}", name.into_inner(), span); 57 | Self { 58 | name, 59 | span, 60 | scopelvl, 61 | debug, 62 | } 63 | } 64 | } 65 | 66 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 67 | pub struct TypeCtorInd(pub usize); 68 | 69 | #[derive(Debug)] 70 | pub struct PolyHeadData { 71 | pub kind: PolyKind, 72 | pub loc: SourceLoc, 73 | pub params: Box<[(StringId, Span)]>, 74 | } 75 | 76 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 77 | pub struct VarSpec { 78 | pub loc: SourceLoc, 79 | pub name: StringId, 80 | } 81 | 82 | // Heads will be cloned during instantiation in order to work around the borrow checker 83 | #[derive(Debug, Clone)] 84 | pub enum VTypeHead { 85 | VUnion(Vec), 86 | VInstantiateExist { 87 | // Only mutated during instantiation process and during cleanup, but use RefCell for simplicity 88 | params: Rc>>, 89 | target: Value, 90 | src_template: (Span, InstantiateSourceKind), 91 | }, 92 | 93 | VTop, 94 | VFunc { 95 | arg: Use, 96 | ret: Value, 97 | }, 98 | VObj { 99 | fields: HashMap, Span)>, 100 | }, 101 | VCase { 102 | case: (StringId, Value), 103 | }, 104 | VAbstract { 105 | ty: TypeCtorInd, 106 | }, 107 | 108 | VPolyHead(Rc, Value, bool), 109 | VTypeVar(VarSpec), 110 | VDisjointIntersect(HashSet, Option), 111 | } 112 | 113 | #[derive(Debug, Clone)] 114 | pub enum UTypeHead { 115 | UIntersection(Vec), 116 | UInstantiateUni { 117 | // Only mutated during instantiation process and during cleanup, but use RefCell for simplicity 118 | params: Rc>>, 119 | target: Use, 120 | src_template: (Span, InstantiateSourceKind), 121 | }, 122 | 123 | UBot, 124 | UFunc { 125 | arg: Value, 126 | ret: Use, 127 | }, 128 | UObj { 129 | fields: HashMap, Span)>, 130 | }, 131 | UCase { 132 | cases: HashMap, 133 | wildcard: Option, 134 | }, 135 | UAbstract { 136 | ty: TypeCtorInd, 137 | }, 138 | UPolyHead(Rc, Use, bool), 139 | UTypeVar(VarSpec), 140 | UDisjointUnion(HashSet, Option), 141 | } 142 | pub type VTypeNode = (VTypeHead, Span, PolyDeps); 143 | pub type UTypeNode = (UTypeHead, Span, PolyDeps); 144 | 145 | enum CheckHeadsResult { 146 | Done, 147 | Instantiate { 148 | poly: Rc, 149 | substitution_params: Rc>>, 150 | src_template: (Span, InstantiateSourceKind), 151 | reason: FlowReason, 152 | 153 | // If poly.kind is Universal, instantiate lhs then flow lhs' -> rhs 154 | // otherwise, instantiate rhs then flow lhs -> rhs' 155 | lhs_sub: Value, 156 | rhs_sub: Use, 157 | }, 158 | } 159 | 160 | fn check_heads( 161 | strings: &mut lasso::Rodeo, 162 | type_ctors: &[TypeCtor], 163 | lhs_ind: Value, 164 | lhs: &VTypeNode, 165 | rhs_ind: Use, 166 | rhs: &UTypeNode, 167 | mut edge_context: TypeEdge, 168 | out: &mut Vec<(Value, Use, TypeEdge)>, 169 | ) -> Result { 170 | use CheckHeadsResult::*; 171 | use UTypeHead::*; 172 | use VTypeHead::*; 173 | edge_context.reason = FlowReason::Check(lhs_ind, rhs_ind); 174 | 175 | // Remove unused context 176 | // edge_context.bound_pairs.filter(|&(a, b)| lhs.2.get(a) && rhs.2.get(b)); 177 | edge_context.bound_pairs.filter_left(|a| lhs.2.get(a)); 178 | edge_context.bound_pairs.filter_right(|a| rhs.2.get(a)); 179 | 180 | // First handle (non-disjoint) unions and intersections 181 | if let &VUnion(ref types) = &lhs.0 { 182 | for lhs2 in types.iter().copied() { 183 | out.push((lhs2, rhs_ind, edge_context.clone())); 184 | } 185 | return Ok(Done); 186 | } else if let &UIntersection(ref types) = &rhs.0 { 187 | for rhs2 in types.iter().copied() { 188 | out.push((lhs_ind, rhs2, edge_context.clone())); 189 | } 190 | return Ok(Done); 191 | } 192 | 193 | // Now handle disjoint unions and intersections 194 | if let &VDisjointIntersect(ref vars1, def1) = &lhs.0 { 195 | match &rhs.0 { 196 | &UDisjointUnion(ref vars2, def2) => { 197 | if edge_context.bound_pairs.disjoint_union_vars_have_match(vars1, vars2) { 198 | return Ok(Done); 199 | } 200 | } 201 | &UTypeVar(tv2) => { 202 | let mut vars2 = HashSet::new(); 203 | vars2.insert(tv2); 204 | if edge_context.bound_pairs.disjoint_union_vars_have_match(vars1, &vars2) { 205 | return Ok(Done); 206 | } 207 | } 208 | _ => {} 209 | }; 210 | 211 | if let Some(lhs2) = def1 { 212 | out.push((lhs2, rhs_ind, edge_context)); 213 | return Ok(Done); 214 | } 215 | } else if let &UDisjointUnion(ref vars2, def2) = &rhs.0 { 216 | // Case where lhs is DisjointIntersect was already handled above, so we only need to check for lone TypeVar 217 | if let &VTypeVar(tv1) = &lhs.0 { 218 | let mut vars1 = HashSet::new(); 219 | vars1.insert(tv1); 220 | if edge_context.bound_pairs.disjoint_union_vars_have_match(&vars1, vars2) { 221 | return Ok(Done); 222 | } 223 | } 224 | 225 | if let Some(rhs2) = def2 { 226 | out.push((lhs_ind, rhs2, edge_context)); 227 | return Ok(Done); 228 | } 229 | } 230 | 231 | // Now check to see if we need to instantiate polymorphic types 232 | // Important: Only do this after checking for unions and intersections 233 | if let &VInstantiateExist { 234 | target, 235 | ref params, 236 | src_template, 237 | } = &lhs.0 238 | { 239 | if let &UPolyHead(ref poly, rhs_sub, poison) = &rhs.0 { 240 | if poly.kind == PolyKind::Existential { 241 | if poison { 242 | return Err(poisoned_poly_err(lhs.1)); 243 | } 244 | return Ok(CheckHeadsResult::Instantiate { 245 | poly: poly.clone(), 246 | substitution_params: params.clone(), 247 | src_template, 248 | reason: edge_context.reason, 249 | lhs_sub: target, 250 | rhs_sub, 251 | }); 252 | } 253 | } 254 | out.push((target, rhs_ind, edge_context)); 255 | return Ok(Done); 256 | } else if let &UInstantiateUni { 257 | target, 258 | ref params, 259 | src_template, 260 | } = &rhs.0 261 | { 262 | if let &VPolyHead(ref poly, lhs_sub, poison) = &lhs.0 { 263 | if poly.kind == PolyKind::Universal { 264 | if poison { 265 | return Err(poisoned_poly_err(rhs.1)); 266 | } 267 | return Ok(CheckHeadsResult::Instantiate { 268 | poly: poly.clone(), 269 | substitution_params: params.clone(), 270 | src_template, 271 | reason: edge_context.reason, 272 | lhs_sub, 273 | rhs_sub: target, 274 | }); 275 | } 276 | } 277 | out.push((lhs_ind, target, edge_context)); 278 | return Ok(Done); 279 | } 280 | 281 | match (&lhs.0, &rhs.0) { 282 | // Check for polymorphic heads and update the edge context, then recurse 283 | (&VPolyHead(ref lhs_poly, lhs_t, _), &UPolyHead(ref rhs_poly, rhs_t, _)) => { 284 | edge_context.bound_pairs.push((lhs_poly.loc, rhs_poly.loc)); 285 | out.push((lhs_t, rhs_t, edge_context)); 286 | } 287 | (&VPolyHead(ref lhs_poly, lhs_t, _), _) => { 288 | out.push((lhs_t, rhs_ind, edge_context)); 289 | } 290 | (_, &UPolyHead(ref rhs_poly, rhs_t, _)) => { 291 | out.push((lhs_ind, rhs_t, edge_context)); 292 | } 293 | 294 | // Check for basic types - the type constructors on both sides have to match. 295 | ( 296 | &VFunc { 297 | arg: arg1, ret: ret1, .. 298 | }, 299 | &UFunc { 300 | arg: arg2, ret: ret2, .. 301 | }, 302 | ) => { 303 | // flip the order since arguments are contravariant 304 | out.push((arg2, arg1, edge_context.flip())); 305 | out.push((ret1, ret2, edge_context)); 306 | } 307 | (&VObj { fields: ref fields1 }, &UObj { fields: ref fields2 }) => { 308 | // Check if the accessed field is defined 309 | for (name, &(rhs_r, rhs_w, rhs_span)) in fields2.iter() { 310 | if let Some(&(lhs_r, lhs_w, lhs_span)) = fields1.get(name) { 311 | out.push((lhs_r, rhs_r, edge_context.clone())); 312 | 313 | // Check for mutability 314 | if let Some(rhs_w) = rhs_w { 315 | if let Some(lhs_w) = lhs_w { 316 | // Contravariant 317 | out.push((rhs_w, lhs_w, edge_context.flip())); 318 | } else { 319 | return Err(immutable_field_err(lhs_span, rhs_span, strings.resolve(&name))); 320 | } 321 | } 322 | } else { 323 | return Err(missing_field_err(lhs.1, rhs_span, strings.resolve(&name))); 324 | } 325 | } 326 | } 327 | ( 328 | &VCase { case: (name, lhs2) }, 329 | &UCase { 330 | cases: ref cases2, 331 | wildcard, 332 | }, 333 | ) => { 334 | // Check if the right case is handled 335 | if let Some(rhs2) = cases2.get(&name).copied() { 336 | out.push((lhs2, rhs2, edge_context)); 337 | } else if let Some(rhs2) = wildcard { 338 | out.push((lhs_ind, rhs2, edge_context)); 339 | } else { 340 | return Err(unhandled_variant_err(lhs, rhs, strings.resolve(&name))); 341 | } 342 | } 343 | 344 | (&VAbstract { ty: ty_ind1 }, &UAbstract { ty: ty_ind2 }) => { 345 | let ty_def1 = &type_ctors[ty_ind1.0]; 346 | let ty_def2 = &type_ctors[ty_ind2.0]; 347 | if ty_ind1 == ty_ind2 { 348 | if edge_context.scopelvl < ty_def1.scopelvl { 349 | return Err(type_escape_error(strings, ty_def1, lhs, rhs, edge_context.scopelvl)); 350 | } 351 | } else { 352 | return Err(type_mismatch_err(strings, type_ctors, lhs, rhs)); 353 | } 354 | } 355 | 356 | (&VTypeVar(tv1), &UTypeVar(tv2)) => { 357 | if tv1.name != tv2.name || !edge_context.bound_pairs.get(tv1.loc, tv2.loc) { 358 | return Err(type_mismatch_err(strings, type_ctors, lhs, rhs)); 359 | } 360 | } 361 | 362 | _ => { 363 | return Err(type_mismatch_err(strings, type_ctors, lhs, rhs)); 364 | } 365 | }; 366 | Ok(Done) 367 | } 368 | 369 | #[derive(Debug, Clone, Copy)] 370 | pub struct InferenceVarData { 371 | pub scopelvl: ScopeLvl, 372 | pub src: HoleSrc, 373 | } 374 | 375 | #[derive(Debug)] 376 | pub enum TypeNode { 377 | Var(InferenceVarData), 378 | Value(VTypeNode), 379 | Use(UTypeNode), 380 | 381 | // Invariant: No placeholders exist when flow() is called, so they are never present during type checking. 382 | Placeholder, 383 | } 384 | impl TypeNode { 385 | fn scopelvl(&self) -> ScopeLvl { 386 | use TypeNode::*; 387 | match self { 388 | Var(data) => data.scopelvl, 389 | _ => ScopeLvl(u32::MAX), 390 | } 391 | } 392 | } 393 | impl ExtNodeDataTrait for TypeNode { 394 | fn truncate(&mut self, i: TypeNodeInd) { 395 | if let TypeNode::Value((VTypeHead::VInstantiateExist { params, .. }, ..)) = self { 396 | params 397 | .borrow_mut() 398 | .retain(|_, (v, u)| (v.0 < i || v.0 == NONE) && (u.0 < i || u.0 == NONE)); 399 | } 400 | if let TypeNode::Use((UTypeHead::UInstantiateUni { params, .. }, ..)) = self { 401 | params 402 | .borrow_mut() 403 | .retain(|_, (v, u)| (v.0 < i || v.0 == NONE) && (u.0 < i || u.0 == NONE)); 404 | } 405 | } 406 | } 407 | 408 | /// Used to track the reason a flow edge was added so we can backtrack when printing errors 409 | #[derive(Debug, Clone, Copy)] 410 | pub enum FlowReason { 411 | Root(Span), 412 | Transitivity(TypeNodeInd), 413 | Check(Value, Use), 414 | } 415 | 416 | #[derive(Debug, Clone)] 417 | pub struct TypeEdge { 418 | scopelvl: ScopeLvl, 419 | bound_pairs: BoundPairsSet, 420 | pub reason: FlowReason, 421 | } 422 | impl TypeEdge { 423 | fn flip(&self) -> Self { 424 | let mut new = self.clone(); 425 | new.bound_pairs = new.bound_pairs.flip(); 426 | new 427 | } 428 | } 429 | impl EdgeDataTrait for TypeEdge { 430 | fn expand(mut self, hole: &TypeNode, ind: TypeNodeInd) -> Self { 431 | self.scopelvl = std::cmp::min(self.scopelvl, hole.scopelvl()); 432 | self.reason = FlowReason::Transitivity(ind); 433 | self 434 | } 435 | 436 | fn update(&mut self, other: &Self) -> bool { 437 | let mut changed = false; 438 | if other.scopelvl < self.scopelvl { 439 | self.scopelvl = other.scopelvl; 440 | changed = true; 441 | } 442 | if self.bound_pairs.update_intersect(&other.bound_pairs) { 443 | changed = true; 444 | } 445 | 446 | changed 447 | } 448 | } 449 | 450 | pub struct TypeCheckerCore { 451 | // Only public for instantiation.rs 452 | pub r: reachability::Reachability, 453 | pub type_ctors: Vec, 454 | pub flowcount: u32, 455 | pub varcount: u32, 456 | } 457 | impl TypeCheckerCore { 458 | pub fn new() -> Self { 459 | Self { 460 | r: Reachability::new(), 461 | type_ctors: Vec::new(), 462 | flowcount: 0, 463 | varcount: 0, 464 | } 465 | } 466 | 467 | pub fn add_type_ctor(&mut self, ty: TypeCtor) -> TypeCtorInd { 468 | let i = self.type_ctors.len(); 469 | self.type_ctors.push(ty); 470 | TypeCtorInd(i) 471 | } 472 | pub fn add_builtin_type(&mut self, name: StringId) -> TypeCtorInd { 473 | self.add_type_ctor(TypeCtor::new(name, None, ScopeLvl(0))) 474 | } 475 | pub fn add_abstract_type(&mut self, name: StringId, span: Span, scopelvl: ScopeLvl) -> TypeCtorInd { 476 | // println!("new abs ctor {} {}", name.into_inner(), self.type_ctors.len()); 477 | self.add_type_ctor(TypeCtor::new(name, Some(span), scopelvl)) 478 | } 479 | 480 | fn new_edge_context(&self, reason: FlowReason, scopelvl: ScopeLvl) -> TypeEdge { 481 | TypeEdge { 482 | scopelvl, 483 | bound_pairs: BoundPairsSet::default(), 484 | reason, 485 | } 486 | } 487 | 488 | pub fn flow( 489 | &mut self, 490 | strings: &mut lasso::Rodeo, 491 | lhs: Value, 492 | rhs: Use, 493 | expl_span: Span, 494 | scopelvl: ScopeLvl, 495 | ) -> Result<(), TypeError> { 496 | self.flowcount += 1; 497 | // println!("flow #{}: {}->{}", self.flowcount, lhs.0.0, rhs.0.0); 498 | 499 | let mut pending_edges = vec![(lhs, rhs, self.new_edge_context(FlowReason::Root(expl_span), scopelvl))]; 500 | let mut type_pairs_to_check = Vec::new(); 501 | while let Some((lhs, rhs, edge_context)) = pending_edges.pop() { 502 | // Check for top/bottom types 503 | if lhs.0 == NONE || rhs.0 == NONE { 504 | continue; 505 | } 506 | 507 | self.r.add_edge(lhs.0, rhs.0, edge_context, &mut type_pairs_to_check); 508 | 509 | // Check if adding that edge resulted in any new type pairs needing to be checked 510 | while let Some((lhs, rhs, edge_context)) = type_pairs_to_check.pop() { 511 | if let TypeNode::Value(lhs_head) = self.r.get(lhs).unwrap() { 512 | if let TypeNode::Use(rhs_head) = self.r.get(rhs).unwrap() { 513 | let lhs = Value(lhs); 514 | let rhs = Use(rhs); 515 | 516 | let res = check_heads( 517 | strings, 518 | &self.type_ctors, 519 | lhs, 520 | lhs_head, 521 | rhs, 522 | rhs_head, 523 | edge_context, 524 | &mut pending_edges, 525 | ); 526 | let res = match res { 527 | Ok(v) => v, 528 | Err(mut e) => { 529 | e.add_hole_int(self, strings, (lhs, rhs)); 530 | return e.into(); 531 | } 532 | }; 533 | 534 | // Handle any followup operations that require mutation 535 | // e.g. function instantation 536 | self.flow_sub_mut(res, &mut pending_edges, scopelvl); 537 | } 538 | } 539 | } 540 | } 541 | assert!(pending_edges.is_empty() && type_pairs_to_check.is_empty()); 542 | Ok(()) 543 | } 544 | 545 | fn flow_sub_mut(&mut self, res: CheckHeadsResult, out: &mut Vec<(Value, Use, TypeEdge)>, scopelvl: ScopeLvl) { 546 | match res { 547 | CheckHeadsResult::Done => {} 548 | CheckHeadsResult::Instantiate { 549 | poly, 550 | substitution_params, 551 | src_template, 552 | reason, 553 | lhs_sub, 554 | rhs_sub, 555 | } => { 556 | // Domain expansion - for type parameters not already specified, substitute them 557 | // with a new inference variable. The same inference variable will be used for 558 | // all instantiations of that parameter with the same instantiation node. 559 | let mut params_mut = substitution_params.borrow_mut(); 560 | for (name, _) in poly.params.iter().copied() { 561 | // println!("inserting var for {}", name.into_inner()); 562 | params_mut 563 | .entry(name) 564 | .or_insert_with(|| self.var(HoleSrc::Instantiation(src_template, name), scopelvl)); 565 | } 566 | drop(params_mut); 567 | 568 | // Now do the actual instantiation 569 | let params = substitution_params.borrow(); 570 | let mut ctx = InstantionContext::new(self, Substitutions::Type(¶ms), poly.loc); 571 | 572 | // Functions can only be instantiated when they have no free variables, 573 | // so using empty context is ok. 574 | match poly.kind { 575 | PolyKind::Universal => { 576 | let new = ctx.instantiate_val(lhs_sub); 577 | // println!("instantiate {}->{}", lhs_sub.0.0, new.0.0); 578 | out.push((new, rhs_sub, self.new_edge_context(reason, scopelvl))); 579 | } 580 | PolyKind::Existential => { 581 | let new = ctx.instantiate_use(rhs_sub); 582 | out.push((lhs_sub, new, self.new_edge_context(reason, scopelvl))); 583 | } 584 | } 585 | } 586 | } 587 | } 588 | 589 | pub fn new_val(&mut self, val_type: VTypeHead, span: Span, deps: Option) -> Value { 590 | // println!("val[{}] = {:?}", self.r.len(), val_type); 591 | Value(self.r.add_node(TypeNode::Value((val_type, span, deps.unwrap_or_default())))) 592 | } 593 | 594 | pub fn new_use(&mut self, constraint: UTypeHead, span: Span, deps: Option) -> Use { 595 | // println!("use[{}] = {:?}", self.r.len(), constraint); 596 | Use(self.r.add_node(TypeNode::Use((constraint, span, deps.unwrap_or_default())))) 597 | } 598 | 599 | pub fn var(&mut self, src: HoleSrc, scopelvl: ScopeLvl) -> (Value, Use) { 600 | let data = InferenceVarData { scopelvl, src }; 601 | let i = self.r.add_node(TypeNode::Var(data)); 602 | self.varcount += 1; 603 | // println!("var #{}: {} {:?}", self.flowcount, i.0, data); 604 | (Value(i), Use(i)) 605 | } 606 | 607 | pub const fn bot(&self) -> Value { 608 | Value(NONE) 609 | } 610 | pub const fn top_use(&self) -> Use { 611 | Use(NONE) 612 | } 613 | 614 | pub fn simple_val(&mut self, ty: TypeCtorInd, span: Span) -> Value { 615 | self.new_val(VTypeHead::VAbstract { ty }, span, None) 616 | } 617 | pub fn simple_use(&mut self, ty: TypeCtorInd, span: Span) -> Use { 618 | self.new_use(UTypeHead::UAbstract { ty }, span, None) 619 | } 620 | 621 | pub fn obj_use(&mut self, fields: Vec<(StringId, (Use, Option, Span))>, span: Span) -> Use { 622 | let fields = fields.into_iter().collect(); 623 | self.new_use(UTypeHead::UObj { fields }, span, None) 624 | } 625 | 626 | pub fn case_use(&mut self, cases: Vec<(StringId, Use)>, wildcard: Option, span: Span) -> Use { 627 | let cases = cases.into_iter().collect(); 628 | self.new_use(UTypeHead::UCase { cases, wildcard }, span, None) 629 | } 630 | 631 | pub fn val_placeholder(&mut self) -> Value { 632 | Value(self.r.add_node(TypeNode::Placeholder)) 633 | } 634 | pub fn use_placeholder(&mut self) -> Use { 635 | Use(self.r.add_node(TypeNode::Placeholder)) 636 | } 637 | pub fn set_val(&mut self, ph: Value, head: VTypeHead, span: Span, deps: Option) { 638 | // println!("set_val[{}] = {:?}", ph.0.0, head); 639 | let r = self.r.get_mut(ph.0).unwrap(); 640 | if let TypeNode::Placeholder = *r { 641 | *r = TypeNode::Value((head, span, deps.unwrap_or_default())); 642 | } else { 643 | unreachable!(); 644 | } 645 | } 646 | pub fn set_use(&mut self, ph: Use, head: UTypeHead, span: Span, deps: Option) { 647 | // println!("set_use[{}] = {:?}", ph.0.0, head); 648 | let r = self.r.get_mut(ph.0).unwrap(); 649 | if let TypeNode::Placeholder = *r { 650 | *r = TypeNode::Use((head, span, deps.unwrap_or_default())); 651 | } else { 652 | unreachable!(); 653 | } 654 | } 655 | 656 | pub fn custom(&mut self, ty: TypeCtorInd, span: Span) -> (Value, Use) { 657 | ( 658 | self.new_val(VTypeHead::VAbstract { ty }, span, None), 659 | self.new_use(UTypeHead::UAbstract { ty }, span, None), 660 | ) 661 | } 662 | 663 | //////////////////////////////////////////////////////////////////////////////// 664 | pub fn save(&mut self) { 665 | self.r.save(); 666 | } 667 | pub fn revert(&mut self) { 668 | self.r.revert(); 669 | } 670 | pub fn make_permanent(&mut self) { 671 | self.r.make_permanent(); 672 | } 673 | 674 | pub fn num_type_nodes(&self) -> usize { 675 | self.r.len() 676 | } 677 | } 678 | 679 | pub struct SavePoint(TypeCheckerCore); 680 | -------------------------------------------------------------------------------- /src/grammar.lalr: -------------------------------------------------------------------------------- 1 | use lalrpop_util::ParseError; 2 | 3 | use super::ast; // super instead of self because lalrpop wraps this in an internal module 4 | use super::spans; 5 | 6 | grammar(ctx: &mut ast::ParserContext<'_, 'input>); 7 | 8 | extern { 9 | type Error = (&'static str, spans::Span); 10 | } 11 | 12 | // Tokens //////////////////////////////////////////////////////////// 13 | match { 14 | r"\s*" => { }, // The default whitespace skipping is disabled if an `ignore pattern` is specified 15 | r"//[^\n\r]*[\n\r]*" => { }, // Skip `// comments` 16 | r#"\(\*[^*]*\*+(?:[^\)*][^*]*\*+)*\)"# => { }, // Skip `(* comments *)` 17 | } else { 18 | _ 19 | } 20 | 21 | 22 | 23 | Ident: ast::StringId = => ctx.strings.get_or_intern(<>); 24 | Tag: ast::StringId = => ctx.strings.get_or_intern(&<>[1..]); 25 | StringIdent: String = r"[a-z_]\w*"=> String::from(<>); 26 | 27 | IntLiteral: String = { 28 | =>? { 29 | let s2 = s.trim_start_matches('-'); 30 | if s2 != "0" && s2.starts_with("0") { 31 | Err(ParseError::User { 32 | error: ("SyntaxError: Numbers can't contain leading 0s", ctx.span_maker.span(l, r)) 33 | }) 34 | } else { 35 | Ok(String::from(s)) 36 | } 37 | }, 38 | }; 39 | FloatLiteral: String = 40 | => String::from(<>); 41 | StringLiteral: String = 42 | => String::from(<>); 43 | 44 | 45 | // make sure __proto__ is not considered a valid identifier 46 | Illegal = "__proto__"; 47 | 48 | 49 | // Macros //////////////////////////////////////////////////////////// 50 | Box: Box = { 51 | => Box::new(<>), 52 | } 53 | SepList: Vec = { 54 | Sep)*> => { 55 | let mut v = v; 56 | v.push(e); 57 | v 58 | } 59 | }; 60 | SepListOpt: Vec = { 61 | SepList, 62 | => Vec::new(), 63 | }; 64 | 65 | 66 | 67 | 68 | 69 | 70 | Spanned: spans::Spanned = { 71 | => (val, ctx.span_maker.span(l, r)) 72 | }; 73 | 74 | // Types ///////////////////////////////////////////////////////////// 75 | KeyPairType: ast::KeyPairType = { 76 | > ":" => (name, ast::FieldTypeDecl::Imm(ty)), 77 | "mut" > ":" => (name, ast::FieldTypeDecl::RWSame(ty)), 78 | "mut" > ":" "<-" => (name, ast::FieldTypeDecl::RWPair(ty, ty2)), 79 | } 80 | RecordTypeParamList = ("type" ";")*; 81 | 82 | RecordTypeSub = "{" > "}"; 83 | RecordType: ast::TypeExpr = { 84 | Spanned => { 85 | let ((types, fields), span) = <>; 86 | let base_type = ast::TypeExpr::Record(fields); 87 | 88 | if !types.is_empty() { 89 | ast::TypeExpr::Poly(types, Box::new((base_type, span)), ast::PolyKind::Existential) 90 | } else { 91 | base_type 92 | } 93 | } 94 | } 95 | 96 | VariantType = > "of"? >>; 97 | CaseType: ast::TypeExpr = { 98 | "[" > "]" => ast::TypeExpr::Case(<>) 99 | } 100 | 101 | SimpleType: ast::TypeExpr = { 102 | Ident => { 103 | match ctx.strings.resolve(&<>) { 104 | "any" => ast::TypeExpr::Top, 105 | "never" => ast::TypeExpr::Bot, 106 | "_" => ast::TypeExpr::Hole, 107 | _ => ast::TypeExpr::Ident(<>), 108 | } 109 | }, 110 | 111 | RecordType, 112 | CaseType, 113 | "(" ")", 114 | } 115 | 116 | TupleType: ast::TypeExpr = { 117 | Spanned, "*">> => { 118 | ast::make_tuple_ast(<>, &mut ctx.strings, 119 | |name, sub| (name, ast::FieldTypeDecl::Imm((sub, name.1))), 120 | |fields, full_span| ast::TypeExpr::Record(fields)) 121 | } 122 | } 123 | 124 | IntersectType: ast::TypeExpr = { 125 | SepList, "&"> => ast::make_join_ast(ast::JoinKind::Intersect, <>) 126 | } 127 | UnionType: ast::TypeExpr = { 128 | SepList, "|"> => ast::make_join_ast(ast::JoinKind::Union, <>) 129 | } 130 | 131 | NoFunType = UnionType; 132 | SNoFunType = Spanned; 133 | 134 | TypeParamAlias = "as" >; 135 | TypeParam: ast::TypeParam = { 136 | > => ast::TypeParam::new(<>), 137 | } 138 | FuncTypeParamList = "type" "."; 139 | FuncTypeSub = > "->" >; 140 | FuncType: ast::TypeExpr = { 141 | Spanned => { 142 | let ((types, arg, ret), span) = <>; 143 | let base_type = ast::TypeExpr::Func(arg, ret); 144 | 145 | if let Some(types) = types { 146 | ast::TypeExpr::Poly(types, Box::new((base_type, span)), ast::PolyKind::Universal) 147 | } else { 148 | base_type 149 | } 150 | } 151 | } 152 | 153 | Type = { 154 | NoFunType, 155 | FuncType, 156 | "rec" "=" > => ast::TypeExpr::RecursiveDef(<>), 157 | } 158 | SType = Spanned; 159 | 160 | 161 | 162 | ////////////////////////////////////////////////////////////////////// 163 | // Expressions /////////////////////////////////////////////////////// 164 | ////////////////////////////////////////////////////////////////////// 165 | 166 | 167 | // SimpleExpr //////////////////////////////////////////////////////// 168 | FieldAccess = Spanned<("." )>; 169 | FieldAccessExpr: ast::Expr = { 170 | Spanned<(Box FieldAccess)> => { 171 | let ((lhs, rhs), full_span) = <>; 172 | ast::Expr::FieldAccess(lhs, rhs, full_span) 173 | } 174 | } 175 | 176 | InstantiateParam = { 177 | "=" , 178 | } 179 | InstantiateParams = { 180 | "[" >> "]", 181 | } 182 | InstantiateUni: ast::Expr = { 183 | Spanned<(Spanned> InstantiateParams)> => { 184 | let ((sub, types), span) = <>; 185 | let kind = ast::InstantiateSourceKind::ExplicitParams(types.0.is_empty()); 186 | ast::Expr::InstantiateUni(sub, types, kind, span) 187 | } 188 | } 189 | InstantiateExist: ast::Expr = { 190 | Spanned<(>> "[" "]")> => { 191 | // sub is only spanned here for compatibility with InstantiateUni rule, we don't actually need it 192 | let (((sub, _), types), full_span) = <>; 193 | let sub = match *sub { 194 | ast::Expr::InstantiateExist(sub, ..) => sub, 195 | _ => sub, 196 | }; 197 | 198 | let kind = ast::InstantiateSourceKind::ExplicitParams(types.0.is_empty()); 199 | ast::Expr::InstantiateExist(sub, types, kind, full_span) 200 | } 201 | } 202 | 203 | RhsEqExpr = "=" >; 204 | KeyPairExpr: ast::KeyPairExpr = { 205 | > => { 206 | let ismut = ismut.is_some(); 207 | let expr = expr.unwrap_or_else(|| Box::new(ast::Expr::Variable(name))); 208 | (name, expr, ismut, ty) 209 | }, 210 | } 211 | 212 | RecordTypeHint = "type" "=" ; 213 | RecordTypesList = ( ";")*; 214 | RecordSub = "{" > > "}"; 215 | Record: ast::Expr = { 216 | Spanned => { 217 | let ((types, fields), span) = <>; 218 | let sub = ast::Expr::Record(fields, span); 219 | ast::Expr::InstantiateExist(Box::new(sub), types, ast::InstantiateSourceKind::ImplicitRecord, span) 220 | } 221 | } 222 | 223 | VarOrLiteral: ast::Expr = { 224 | Spanned => 225 | match <>.0.as_str() { 226 | "false" | "true" => ast::Expr::Literal(ast::Literal::Bool, <>), 227 | _ => { 228 | let (s, span) = <>; 229 | ast::Expr::Variable((ctx.strings.get_or_intern(s), span)) 230 | } 231 | } 232 | , 233 | 234 | Spanned => ast::Expr::Literal(ast::Literal::Float, <>), 235 | Spanned => ast::Expr::Literal(ast::Literal::Int, <>), 236 | Spanned => ast::Expr::Literal(ast::Literal::Str, <>), 237 | } 238 | 239 | SimpleExpr = { 240 | FieldAccessExpr, 241 | InstantiateExist, 242 | InstantiateUni, 243 | Record, 244 | VarOrLiteral, 245 | "(" ")", 246 | "(" > ":" ")" => ast::Expr::Typed(<>), 247 | "begin" "end", 248 | } 249 | ////////////////////////////////////////////////////////////////////// 250 | // CallExpr ////////////////////////////////////////////////////////// 251 | Call: ast::Expr = { 252 | Spanned<(Spanned Box)> => { 253 | let (((lhs, lhs_span), rhs), full_span) = <>; 254 | let lhs = match lhs { 255 | ast::Expr::InstantiateUni(..) => lhs, 256 | _ => ast::Expr::InstantiateUni((Box::new(lhs), lhs_span), (vec![], lhs_span), ast::InstantiateSourceKind::ImplicitCall, lhs_span), 257 | }; 258 | ast::Expr::Call(Box::new(lhs), rhs, full_span) 259 | } 260 | } 261 | Case: ast::Expr = { 262 | > > => ast::Expr::Case(<>), 263 | } 264 | 265 | CallExpr = { 266 | SimpleExpr, 267 | Call, 268 | Case, 269 | } 270 | 271 | ////////////////////////////////////////////////////////////////////// 272 | // Binary expressions///////////////////////////////////////////////// 273 | ////////////////////////////////////////////////////////////////////// 274 | BinOp: ast::Expr = { 275 | > > => { 276 | let lhs_span = ctx.span_maker.span(p1, p3); 277 | let rhs_span = ctx.span_maker.span(p2, p4); 278 | let full_span = ctx.span_maker.span(p1, p4); 279 | 280 | ast::Expr::BinOp(lhs, lhs_span, rhs, rhs_span, op.0, op.1, full_span) 281 | }, 282 | }; 283 | 284 | 285 | MultOpSub: (ast::OpType, ast::Op) = { 286 | // Have to make this a separate rule because * is used for tuple types too 287 | "*" => (ast::INT_OP, ast::Op::Mult), 288 | => { 289 | match op { 290 | // "*" => (ast::INT_OP, ast::Op::Mult), 291 | "/" => (ast::INT_OP, ast::Op::Div), 292 | "%" => (ast::INT_OP, ast::Op::Rem), 293 | "*." => (ast::FLOAT_OP, ast::Op::Mult), 294 | "/." => (ast::FLOAT_OP, ast::Op::Div), 295 | "%." => (ast::FLOAT_OP, ast::Op::Rem), 296 | _ => unreachable!(), 297 | } 298 | } 299 | } 300 | MultOp: ast::Expr = BinOp; 301 | 302 | AddOpSub: (ast::OpType, ast::Op) = { 303 | => { 304 | match op { 305 | "+" => (ast::INT_OP, ast::Op::Add), 306 | "-" => (ast::INT_OP, ast::Op::Sub), 307 | "+." => (ast::FLOAT_OP, ast::Op::Add), 308 | "-." => (ast::FLOAT_OP, ast::Op::Sub), 309 | "^" => (ast::STR_OP, ast::Op::Add), 310 | _ => unreachable!(), 311 | } 312 | } 313 | } 314 | AddOp: ast::Expr = BinOp; 315 | 316 | CmpOpSub: (ast::OpType, ast::Op) = { 317 | ]=?\.?|[!=]="> => { 318 | match op { 319 | "<" => (ast::INT_CMP, ast::Op::Lt), 320 | "<=" => (ast::INT_CMP, ast::Op::Lte), 321 | ">" => (ast::INT_CMP, ast::Op::Gt), 322 | ">=" => (ast::INT_CMP, ast::Op::Gte), 323 | 324 | "<." => (ast::FLOAT_CMP, ast::Op::Lt), 325 | "<=." => (ast::FLOAT_CMP, ast::Op::Lte), 326 | ">." => (ast::FLOAT_CMP, ast::Op::Gt), 327 | ">=." => (ast::FLOAT_CMP, ast::Op::Gte), 328 | 329 | "==" => (ast::ANY_CMP, ast::Op::Eq), 330 | "!=" => (ast::ANY_CMP, ast::Op::Neq), 331 | _ => unreachable!(), 332 | } 333 | } 334 | } 335 | CmpOp: ast::Expr = BinOp; 336 | 337 | MultExpr = { 338 | CallExpr, 339 | MultOp, 340 | } 341 | AddExpr = { 342 | MultExpr, 343 | AddOp, 344 | } 345 | CompareExpr = { 346 | AddExpr, 347 | CmpOp, 348 | } 349 | 350 | ////////////////////////////////////////////////////////////////////// 351 | // Patterns ////////////////////////////////////////////////////////// 352 | ////////////////////////////////////////////////////////////////////// 353 | VarPatName: (Option, spans::Span) = { 354 | > => { 355 | let (name, span) = <>; 356 | let name = if ctx.strings.resolve(&name) == "_" {None} else {Some(name)}; 357 | (name, span) 358 | } 359 | } 360 | 361 | TypeAscription = ":" ; 362 | KeyPairPattern: (spans::Spanned, Box) = { 363 | > "=" => (name, Box::new(pat)), 364 | > => (name, Box::new(ast::LetPattern::Var((Some(name.0), name.1), ty))), 365 | } 366 | RecordPatternTypeParams = ("type" ";")*; 367 | RecordLetPattern = { 368 | "{" > "}" 369 | } 370 | TupleOrParensLetPattern: ast::LetPattern = { 371 | Spanned<("(" , ",">> ")")> => { 372 | ast::make_tuple_ast(<>, &mut ctx.strings, 373 | |name, sub| (name, Box::new(sub)), 374 | |fields, full_span| ast::LetPattern::Record(((vec![], fields), full_span))) 375 | } 376 | } 377 | 378 | 379 | LetPatternNoIdent: ast::LetPattern = { 380 | > > => ast::LetPattern::Case(<>), 381 | Spanned => ast::LetPattern::Record(<>), 382 | TupleOrParensLetPattern, 383 | // "(" ")" => <>, 384 | } 385 | // Allow bare identifiers only if they have no type annotation 386 | LetPatternNoTypedIdent: ast::LetPattern = { 387 | => ast::LetPattern::Var(<>, None), 388 | => <>, 389 | } 390 | LetPattern: ast::LetPattern = { 391 | => ast::LetPattern::Var(<>), 392 | => <>, 393 | } 394 | 395 | 396 | 397 | 398 | ////////////////////////////////////////////////////////////////////// 399 | // Top level expressions ///////////////////////////////////////////// 400 | ////////////////////////////////////////////////////////////////////// 401 | CompareOrTupleExpr: ast::Expr = { 402 | Spanned, ",">> => { 403 | ast::make_tuple_ast(<>, &mut ctx.strings, 404 | |name, sub| (name, Box::new(sub), false, None), 405 | |fields, full_span| ast::Expr::Record(fields, full_span)) 406 | } 407 | } 408 | 409 | 410 | FieldSetExpr: ast::Expr = { 411 | Spanned<(> "<-" >)> => { 412 | let ((lhs, field, rhs), span) = <>; 413 | ast::Expr::FieldSet(lhs, field, rhs, span) 414 | } 415 | } 416 | 417 | 418 | FuncDefTypeParamList = "(" "type" ")"; 419 | NoFunTypeAscription = ":" ; 420 | FuncSub = "fun" > "->" >; 421 | FuncDef: ast::Expr = { 422 | Spanned => ast::Expr::FuncDef(<>), 423 | } 424 | 425 | 426 | If: ast::Expr = { 427 | "if" >> "then" > "else" > => ast::Expr::If(<>), 428 | } 429 | 430 | 431 | LetLHS = { 432 | "let" "=" >, 433 | } 434 | LetRHS = { 435 | "in" >, 436 | } 437 | Let: ast::Expr = { 438 | => ast::Expr::Block(vec![ast::Statement::LetDef(lhs)], rhs), 439 | } 440 | 441 | 442 | LetRecDef = { 443 | "=" >, 444 | } 445 | LetRecLHS = { 446 | "let" "rec" >, 447 | } 448 | LetRec: ast::Expr = { 449 | => ast::Expr::Block(vec![ast::Statement::LetRecDef(lhs)], rhs), 450 | } 451 | 452 | LoopExpr: ast::Expr = { 453 | Spanned<("loop" >)> => { 454 | let (expr, span) = <>; 455 | ast::Expr::Loop(expr, span) 456 | } 457 | } 458 | 459 | 460 | MatchArm = { 461 | "|" > "->" >, 462 | } 463 | MatchStart = "match" >; 464 | Match: ast::Expr = { 465 | Spanned<(> "with" )> => { 466 | let ((lhs, arms), span) = <>; 467 | ast::Expr::Match(lhs, arms, span) 468 | } 469 | } 470 | 471 | 472 | 473 | NoSemiExpr = { 474 | CompareOrTupleExpr, 475 | FieldSetExpr, 476 | FuncDef, 477 | If, 478 | Let, 479 | LetRec, 480 | LoopExpr, 481 | Match, 482 | } 483 | Expr: ast::Expr = { 484 | ";")*> => { 485 | if stmts.is_empty() { 486 | rest 487 | } else { 488 | ast::Expr::Block(stmts, Box::new(rest)) 489 | } 490 | } 491 | } 492 | ////////////////////////////////////////////////////////////////////// 493 | 494 | 495 | 496 | Statement: ast::Statement = { 497 | => ast::Statement::LetDef(<>), 498 | => ast::Statement::LetRecDef(<>), 499 | => ast::Statement::Expr(<>), 500 | "print" > => ast::Statement::Println(<>), 501 | => ast::Statement::Empty, 502 | } 503 | 504 | pub Script = { 505 | > 506 | } 507 | -------------------------------------------------------------------------------- /src/instantiate.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::rc::Rc; 3 | 4 | use crate::ast::StringId; 5 | use crate::core::*; 6 | use crate::parse_types::SourceLoc; 7 | 8 | type TypeSubs = HashMap; 9 | type AbstractSubs = HashMap; 10 | struct Replacements(HashMap, HashMap); 11 | 12 | pub enum Substitutions<'a> { 13 | Type(&'a HashMap), 14 | Abs(&'a HashMap), 15 | } 16 | 17 | pub struct InstantionContext<'a> { 18 | core: &'a mut TypeCheckerCore, 19 | subs: Substitutions<'a>, 20 | 21 | root: SourceLoc, 22 | vmap: HashMap, 23 | umap: HashMap, 24 | } 25 | impl InstantionContext<'_> { 26 | pub fn new<'a>(core: &'a mut TypeCheckerCore, subs: Substitutions<'a>, root: SourceLoc) -> InstantionContext<'a> { 27 | let mut new = InstantionContext { 28 | core, 29 | subs, 30 | root, 31 | vmap: HashMap::new(), 32 | umap: HashMap::new(), 33 | }; 34 | // Add bot and top to the map so that we never try to deref them 35 | // since they aren't valid indices. 36 | new.vmap.insert(new.core.bot(), new.core.bot()); 37 | new.umap.insert(new.core.top_use(), new.core.top_use()); 38 | new 39 | } 40 | 41 | pub fn instantiate_val(&mut self, old: Value) -> Value { 42 | if let Some(new) = self.vmap.get(&old) { 43 | return *new; 44 | } 45 | 46 | let node = match self.core.r.get(old.0).unwrap() { 47 | TypeNode::Value(node) => node.clone(), 48 | TypeNode::Var { .. } => return old, 49 | _ => unreachable!(), 50 | }; 51 | // Check if type actually depends on the variables being instantiated, if not return unchanged. 52 | if !node.2.get(self.root) { 53 | self.vmap.insert(old, old); 54 | return old; 55 | } 56 | use VTypeHead::*; 57 | if let VTypeVar(spec) = node.0 { 58 | // If root doesn't match loc, then it shouldn't be in deps and we'd have returned above 59 | assert!(spec.loc == self.root); 60 | let new = match self.subs { 61 | Substitutions::Type(m) => m.get(&spec.name).unwrap().0, 62 | Substitutions::Abs(m) => { 63 | let ty_cond = *m.get(&spec.name).unwrap(); 64 | self.core.simple_val(ty_cond, node.1) 65 | } 66 | }; 67 | self.vmap.insert(old, new); 68 | return new; 69 | } 70 | 71 | let ph = self.core.val_placeholder(); 72 | self.vmap.insert(old, ph); 73 | 74 | let head = match node.0 { 75 | VInstantiateExist { .. } | VTop | VAbstract { .. } | VTypeVar(..) => unreachable!(), 76 | 77 | VUnion(vals) => VUnion(vals.into_iter().map(|v| self.instantiate_val(v)).collect()), 78 | 79 | VFunc { arg, ret } => VFunc { 80 | arg: self.instantiate_use(arg), 81 | ret: self.instantiate_val(ret), 82 | }, 83 | 84 | VObj { fields } => VObj { 85 | fields: fields 86 | .into_iter() 87 | .map(|(k, (rty, wty, span))| { 88 | (k, (self.instantiate_val(rty), wty.map(|w| self.instantiate_use(w)), span)) 89 | }) 90 | .collect(), 91 | }, 92 | 93 | VCase { case: (tag, ty) } => VCase { 94 | case: (tag, self.instantiate_val(ty)), 95 | }, 96 | 97 | VPolyHead(poly, ty, poison) => { 98 | let poison = poison || matches!(self.subs, Substitutions::Type(..)); 99 | VPolyHead(poly, self.instantiate_val(ty), poison) 100 | } 101 | 102 | VDisjointIntersect(vars, default) => { 103 | assert!(!vars.iter().any(|spec| spec.loc == self.root)); 104 | VDisjointIntersect(vars.clone(), default.clone().map(|t| self.instantiate_val(t))) 105 | } 106 | }; 107 | 108 | let mut deps = node.2; 109 | deps.remove(self.root); 110 | self.core.set_val(ph, head, node.1, Some(deps)); 111 | ph 112 | } 113 | 114 | pub fn instantiate_use(&mut self, old: Use) -> Use { 115 | if let Some(new) = self.umap.get(&old) { 116 | return *new; 117 | } 118 | 119 | let node = match self.core.r.get(old.0).unwrap() { 120 | TypeNode::Use(node) => node.clone(), 121 | TypeNode::Var { .. } => return old, 122 | _ => unreachable!(), 123 | }; 124 | // Check if type actually depends on the variables being instantiated, if not return unchanged. 125 | if !node.2.get(self.root) { 126 | self.umap.insert(old, old); 127 | return old; 128 | } 129 | use UTypeHead::*; 130 | if let UTypeVar(spec) = node.0 { 131 | // If root doesn't match loc, then it shouldn't be in deps and we'd have returned above 132 | assert!(spec.loc == self.root); 133 | let new = match self.subs { 134 | Substitutions::Type(m) => m.get(&spec.name).unwrap().1, 135 | Substitutions::Abs(m) => { 136 | let ty_cond = *m.get(&spec.name).unwrap(); 137 | self.core.simple_use(ty_cond, node.1) 138 | } 139 | }; 140 | self.umap.insert(old, new); 141 | return new; 142 | } 143 | 144 | let ph = self.core.use_placeholder(); 145 | self.umap.insert(old, ph); 146 | 147 | let head = match node.0 { 148 | UInstantiateUni { .. } | UBot | UAbstract { .. } | UTypeVar(..) => unreachable!(), 149 | 150 | UIntersection(uses) => UIntersection(uses.into_iter().map(|u| self.instantiate_use(u)).collect()), 151 | 152 | UFunc { arg, ret } => UFunc { 153 | arg: self.instantiate_val(arg), 154 | ret: self.instantiate_use(ret), 155 | }, 156 | 157 | UObj { fields } => UObj { 158 | fields: fields 159 | .into_iter() 160 | .map(|(k, (rty, wty, span))| { 161 | (k, (self.instantiate_use(rty), wty.map(|w| self.instantiate_val(w)), span)) 162 | }) 163 | .collect(), 164 | }, 165 | 166 | UCase { cases, wildcard } => { 167 | assert!(wildcard.is_none()); 168 | UCase { 169 | cases: cases.into_iter().map(|(n, ty)| (n, self.instantiate_use(ty))).collect(), 170 | wildcard: None, 171 | } 172 | } 173 | 174 | UPolyHead(poly, ty, poison) => { 175 | let poison = poison || matches!(self.subs, Substitutions::Type(..)); 176 | UPolyHead(poly, self.instantiate_use(ty), poison) 177 | } 178 | 179 | UDisjointUnion(vars, default) => { 180 | assert!(!vars.iter().any(|spec| spec.loc == self.root)); 181 | UDisjointUnion(vars.clone(), default.clone().map(|t| self.instantiate_use(t))) 182 | } 183 | }; 184 | 185 | let mut deps = node.2; 186 | deps.remove(self.root); 187 | self.core.set_use(ph, head, node.1, Some(deps)); 188 | ph 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/js.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub enum Op { 5 | Add, 6 | Sub, 7 | Mult, 8 | Div, 9 | Rem, 10 | 11 | Lt, 12 | Lte, 13 | Gt, 14 | Gte, 15 | 16 | Eq, 17 | Neq, 18 | } 19 | 20 | ///////////////////////////////////////////////////////////////////////////////////////////// 21 | pub fn assign(lhs: Expr, rhs: Expr) -> Expr { 22 | Expr(Expr2::Assignment(lhs.0.into(), rhs.0.into())) 23 | } 24 | pub fn binop(lhs: Expr, rhs: Expr, op: Op) -> Expr { 25 | Expr(Expr2::BinOp(lhs.0.into(), rhs.0.into(), op)) 26 | } 27 | pub fn call(lhs: Expr, rhs: Expr) -> Expr { 28 | Expr(Expr2::Call(lhs.0.into(), rhs.0.into())) 29 | } 30 | pub fn comma_pair(lhs: Expr, rhs: Expr) -> Expr { 31 | Expr(Expr2::Comma(lhs.0.into(), rhs.0.into())) 32 | } 33 | pub fn unary_minus(rhs: Expr) -> Expr { 34 | Expr(Expr2::Minus(rhs.0.into())) 35 | } 36 | pub fn eqop(lhs: Expr, rhs: Expr) -> Expr { 37 | Expr(Expr2::BinOp(lhs.0.into(), rhs.0.into(), Op::Eq)) 38 | } 39 | pub fn field(lhs: Expr, rhs: String) -> Expr { 40 | Expr(Expr2::Field(lhs.0.into(), rhs)) 41 | } 42 | pub fn lit(code: String) -> Expr { 43 | Expr(Expr2::Literal(code)) 44 | } 45 | pub fn ternary(cond: Expr, e1: Expr, e2: Expr) -> Expr { 46 | Expr(Expr2::Ternary(cond.0.into(), e1.0.into(), e2.0.into())) 47 | } 48 | pub fn var(s: String) -> Expr { 49 | Expr(Expr2::Var(s)) 50 | } 51 | 52 | pub fn comma_list(mut exprs: Vec) -> Expr { 53 | // Reverse the list so we can easily create a left-recursive structure instead of right recursive 54 | exprs.reverse(); 55 | let mut res = exprs.pop().unwrap().0; 56 | while let Some(expr) = exprs.pop() { 57 | res = Expr2::Comma(Box::new(res), expr.0.into()); 58 | } 59 | Expr(res) 60 | } 61 | 62 | pub fn println(exprs: Vec) -> Expr { 63 | Expr(Expr2::Print(exprs.into_iter().map(|e| e.0).collect())) 64 | } 65 | 66 | pub fn func(arg: Expr, scope: String, body: Expr) -> Expr { 67 | Expr(Expr2::ArrowFunc(Box::new(arg.0), scope, Box::new(body.0))) 68 | } 69 | 70 | pub fn obj(fields: Vec<(String, Expr)>) -> Expr { 71 | let mut prop_defs = Vec::new(); 72 | for (name, v) in fields { 73 | prop_defs.push(PropertyDefinition::Named(name, v.0.into())); 74 | } 75 | 76 | Expr(Expr2::Obj(prop_defs)) 77 | } 78 | 79 | #[derive(Clone, Debug)] 80 | pub struct Expr(Expr2); 81 | impl Expr { 82 | // pub fn add_parens(&mut self) { 83 | // self.0.add_parens(); 84 | // } 85 | 86 | pub fn to_source(mut self) -> String { 87 | self.0.add_parens(); 88 | 89 | let mut s = "".to_string(); 90 | self.0.write(&mut s); 91 | s 92 | } 93 | } 94 | 95 | #[derive(Clone, Copy, PartialEq, Eq)] 96 | enum Token { 97 | OTHER, 98 | BRACE, 99 | PAREN, 100 | } 101 | 102 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 103 | enum Precedence { 104 | PRIMARY = 0, 105 | MEMBER, 106 | CALL, 107 | LHS, 108 | UNARY, 109 | EXPONENT, 110 | MULTIPLICATIVE, 111 | ADDITIVE, 112 | SHIFT, 113 | RELATIONAL, 114 | EQUALITY, 115 | LOR, 116 | CONDITIONAL, 117 | ASSIGN, 118 | EXPR, 119 | } 120 | 121 | #[derive(Clone, Debug)] 122 | enum PropertyDefinition { 123 | Named(String, Box), 124 | } 125 | 126 | #[derive(Clone, Debug)] 127 | enum Expr2 { 128 | Paren(Box), 129 | Literal(String), 130 | Obj(Vec), 131 | Var(String), 132 | 133 | Field(Box, String), 134 | 135 | Call(Box, Box), 136 | 137 | Minus(Box), 138 | 139 | BinOp(Box, Box, Op), 140 | 141 | Ternary(Box, Box, Box), 142 | 143 | Assignment(Box, Box), 144 | ArrowFunc(Box, String, Box), 145 | 146 | Comma(Box, Box), 147 | 148 | // Temp hack 149 | Print(Vec), 150 | } 151 | impl Expr2 { 152 | fn precedence(&self) -> Precedence { 153 | use Expr2::*; 154 | use Op::*; 155 | use Precedence::*; 156 | match self { 157 | Paren(..) => PRIMARY, 158 | Literal(..) => PRIMARY, 159 | Obj(..) => PRIMARY, 160 | Var(..) => PRIMARY, 161 | Field(..) => MEMBER, 162 | Call(..) => CALL, 163 | Minus(..) => UNARY, 164 | BinOp(_, _, op) => match op { 165 | Mult | Div | Rem => MULTIPLICATIVE, 166 | Add | Sub => ADDITIVE, 167 | Lt | Lte | Gt | Gte => RELATIONAL, 168 | Eq | Neq => EQUALITY, 169 | }, 170 | Ternary(..) => CONDITIONAL, 171 | Assignment(..) => ASSIGN, 172 | ArrowFunc(..) => ASSIGN, 173 | Comma(..) => EXPR, 174 | Print(..) => CALL, 175 | } 176 | } 177 | 178 | fn first(&self) -> Token { 179 | use Expr2::*; 180 | use Token::*; 181 | match self { 182 | Paren(..) => PAREN, 183 | Literal(..) => OTHER, 184 | Obj(..) => BRACE, 185 | Var(..) => OTHER, 186 | Field(lhs, ..) => lhs.first(), 187 | Call(lhs, ..) => lhs.first(), 188 | Minus(..) => OTHER, 189 | BinOp(lhs, ..) => lhs.first(), 190 | Ternary(lhs, ..) => lhs.first(), 191 | Assignment(lhs, ..) => lhs.first(), 192 | ArrowFunc(..) => PAREN, 193 | Comma(lhs, ..) => lhs.first(), 194 | Print(..) => OTHER, 195 | } 196 | } 197 | 198 | fn write(&self, out: &mut String) { 199 | match self { 200 | Self::Paren(e) => { 201 | *out += "("; 202 | e.write(out); 203 | *out += ")"; 204 | } 205 | Self::Literal(code) => { 206 | *out += code; 207 | } 208 | Self::Obj(fields) => { 209 | *out += "{"; 210 | for prop_def in fields { 211 | use PropertyDefinition::*; 212 | match prop_def { 213 | Named(name, val) => { 214 | *out += "'"; 215 | *out += name; 216 | *out += "': "; 217 | val.write(out); 218 | } 219 | } 220 | 221 | *out += ", "; 222 | } 223 | *out += "}"; 224 | } 225 | Self::Var(name) => { 226 | *out += name; 227 | } 228 | Self::Field(lhs, rhs) => { 229 | lhs.write(out); 230 | *out += "."; 231 | *out += rhs; 232 | } 233 | Self::Call(lhs, rhs) => { 234 | lhs.write(out); 235 | *out += "("; 236 | rhs.write(out); 237 | *out += ")"; 238 | } 239 | Self::Minus(e) => { 240 | *out += "-"; 241 | e.write(out); 242 | } 243 | Self::BinOp(lhs, rhs, op) => { 244 | use Op::*; 245 | let opstr = match op { 246 | Add => "+", 247 | Sub => "- ", 248 | Mult => "*", 249 | Div => "/", 250 | Rem => "%", 251 | 252 | Lt => "<", 253 | Lte => "<=", 254 | Gt => ">", 255 | Gte => ">=", 256 | 257 | Eq => "===", 258 | Neq => "!==", 259 | }; 260 | 261 | lhs.write(out); 262 | *out += opstr; 263 | rhs.write(out); 264 | } 265 | Self::Ternary(cond, e1, e2) => { 266 | cond.write(out); 267 | *out += " ? "; 268 | e1.write(out); 269 | *out += " : "; 270 | e2.write(out); 271 | } 272 | Self::Assignment(lhs, rhs) => { 273 | lhs.write(out); 274 | *out += " = "; 275 | rhs.write(out); 276 | } 277 | Self::ArrowFunc(arg, scope_arg, body) => { 278 | *out += "("; 279 | arg.write(out); 280 | *out += ", "; 281 | *out += scope_arg; 282 | *out += "={}) => "; 283 | body.write(out); 284 | } 285 | Self::Comma(lhs, rhs) => { 286 | lhs.write(out); 287 | *out += ", "; 288 | rhs.write(out); 289 | } 290 | Self::Print(exprs) => { 291 | *out += "p.println("; 292 | for ex in exprs { 293 | ex.write(out); 294 | *out += ", "; 295 | } 296 | *out += ")"; 297 | } 298 | } 299 | } 300 | 301 | fn wrap_in_parens(&mut self) { 302 | use Expr2::*; 303 | let dummy = Literal("".to_string()); 304 | let temp = std::mem::replace(self, dummy); 305 | *self = Paren(Box::new(temp)); 306 | } 307 | 308 | fn ensure(&mut self, required: Precedence) { 309 | if self.precedence() > required { 310 | self.wrap_in_parens(); 311 | } 312 | } 313 | 314 | fn add_parens(&mut self) { 315 | use Precedence::*; 316 | match self { 317 | Self::Paren(e) => { 318 | e.add_parens(); 319 | } 320 | Self::Literal(code) => {} 321 | Self::Obj(fields) => { 322 | for prop_def in fields { 323 | use PropertyDefinition::*; 324 | match prop_def { 325 | Named(name, val) => { 326 | val.add_parens(); 327 | val.ensure(ASSIGN); 328 | } 329 | } 330 | } 331 | } 332 | Self::Var(name) => {} 333 | Self::Field(lhs, rhs) => { 334 | lhs.add_parens(); 335 | lhs.ensure(MEMBER); 336 | } 337 | Self::Call(lhs, rhs) => { 338 | lhs.add_parens(); 339 | lhs.ensure(MEMBER); 340 | rhs.add_parens(); 341 | rhs.ensure(ASSIGN); 342 | } 343 | Self::Minus(e) => { 344 | e.add_parens(); 345 | e.ensure(UNARY); 346 | } 347 | Self::BinOp(lhs, rhs, op) => { 348 | use Op::*; 349 | let req = match op { 350 | Mult | Div | Rem => (MULTIPLICATIVE, EXPONENT), 351 | Add | Sub => (ADDITIVE, MULTIPLICATIVE), 352 | Lt | Lte | Gt | Gte => (RELATIONAL, SHIFT), 353 | Eq | Neq => (EQUALITY, RELATIONAL), 354 | }; 355 | 356 | lhs.add_parens(); 357 | lhs.ensure(req.0); 358 | rhs.add_parens(); 359 | rhs.ensure(req.1); 360 | } 361 | Self::Ternary(cond, e1, e2) => { 362 | cond.add_parens(); 363 | e1.add_parens(); 364 | e1.ensure(ASSIGN); 365 | e2.add_parens(); 366 | e2.ensure(ASSIGN); 367 | } 368 | Self::Assignment(lhs, rhs) => { 369 | lhs.add_parens(); 370 | lhs.ensure(LHS); 371 | rhs.add_parens(); 372 | rhs.ensure(ASSIGN); 373 | } 374 | Self::ArrowFunc(arg, scope_arg, body) => { 375 | arg.add_parens(); 376 | body.add_parens(); 377 | body.ensure(ASSIGN); 378 | // body can't be an expression starting with "{" 379 | if body.first() == Token::BRACE { 380 | body.wrap_in_parens(); 381 | } 382 | } 383 | Self::Comma(lhs, rhs) => { 384 | lhs.add_parens(); 385 | rhs.add_parens(); 386 | rhs.ensure(ASSIGN); 387 | } 388 | Self::Print(exprs) => { 389 | for ex in exprs { 390 | ex.add_parens(); 391 | ex.ensure(PRIMARY); 392 | } 393 | } 394 | } 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_imports)] 3 | #![allow(unused_variables)] 4 | 5 | mod ast; 6 | mod bound_pairs_set; 7 | mod codegen; 8 | mod core; 9 | mod grammar; 10 | mod instantiate; 11 | mod js; 12 | mod parse_types; 13 | mod reachability; 14 | mod spans; 15 | mod type_errors; 16 | mod typeck; 17 | mod unwindmap; 18 | mod utils; 19 | 20 | use lasso::Rodeo; 21 | use wasm_bindgen::prelude::*; 22 | 23 | use std::fmt::Display; 24 | use std::mem; 25 | 26 | use lalrpop_util::ParseError; 27 | 28 | use self::codegen::ModuleBuilder; 29 | use self::grammar::ScriptParser; 30 | use self::spans::SpanMaker; 31 | use self::spans::SpanManager; 32 | use self::spans::SpannedError; 33 | use self::typeck::TypeckState; 34 | 35 | fn convert_parse_error(mut sm: SpanMaker, e: ParseError) -> SpannedError { 36 | match e { 37 | ParseError::InvalidToken { location } => { 38 | SpannedError::new1("SyntaxError: Invalid token", sm.span(location, location)) 39 | } 40 | ParseError::UnrecognizedEof { location, expected } => SpannedError::new1( 41 | format!( 42 | "SyntaxError: Unexpected end of input.\nNote: expected tokens: [{}]\nParse error occurred here:", 43 | expected.join(", ") 44 | ), 45 | sm.span(location, location), 46 | ), 47 | ParseError::UnrecognizedToken { token, expected } => SpannedError::new1( 48 | format!( 49 | "SyntaxError: Unexpected token {}\nNote: expected tokens: [{}]\nParse error occurred here:", 50 | token.1, 51 | expected.join(", ") 52 | ), 53 | sm.span(token.0, token.2), 54 | ), 55 | ParseError::ExtraToken { token } => { 56 | SpannedError::new1("SyntaxError: Unexpected extra token", sm.span(token.0, token.2)) 57 | } 58 | ParseError::User { error: (msg, span) } => SpannedError::new1(msg, span), 59 | } 60 | } 61 | 62 | #[wasm_bindgen] 63 | pub struct State { 64 | parser: ScriptParser, 65 | spans: SpanManager, 66 | strings: lasso::Rodeo, 67 | 68 | checker: TypeckState, 69 | compiler: ModuleBuilder, 70 | 71 | out: Option, 72 | err: Option, 73 | } 74 | #[wasm_bindgen] 75 | impl State { 76 | pub fn new() -> Self { 77 | let mut strings = Rodeo::new(); 78 | let checker = TypeckState::new(&mut strings); 79 | 80 | State { 81 | parser: ScriptParser::new(), 82 | spans: SpanManager::default(), 83 | strings, 84 | 85 | checker, 86 | compiler: ModuleBuilder::new(), 87 | 88 | out: None, 89 | err: None, 90 | } 91 | } 92 | 93 | fn process_sub(&mut self, source: &str) -> Result { 94 | let span_maker = self.spans.add_source(source.to_owned()); 95 | let mut ctx = ast::ParserContext { 96 | span_maker, 97 | strings: &mut self.strings, 98 | }; 99 | 100 | let ast = self 101 | .parser 102 | .parse(&mut ctx, source) 103 | .map_err(|e| convert_parse_error(ctx.span_maker, e))?; 104 | let _t = self.checker.check_script(&mut self.strings, &ast)?; 105 | 106 | let mut ctx = codegen::Context(&mut self.compiler, &self.strings); 107 | let js_ast = codegen::compile_script(&mut ctx, &ast); 108 | Ok(js_ast.to_source()) 109 | } 110 | 111 | pub fn process(&mut self, source: &str) -> bool { 112 | let res = self.process_sub(source); 113 | match res { 114 | Ok(s) => { 115 | self.out = Some(s); 116 | true 117 | } 118 | Err(e) => { 119 | self.err = Some(e.print(&self.spans)); 120 | false 121 | } 122 | } 123 | } 124 | 125 | pub fn get_output(&mut self) -> Option { 126 | self.out.take() 127 | } 128 | pub fn get_err(&mut self) -> Option { 129 | self.err.take() 130 | } 131 | 132 | pub fn reset(&mut self) { 133 | mem::swap(&mut self.checker, &mut TypeckState::new(&mut self.strings)); 134 | mem::swap(&mut self.compiler, &mut ModuleBuilder::new()); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_imports)] 3 | #![allow(unused_variables)] 4 | 5 | use std::env; 6 | use std::fs; 7 | use std::time::Instant; 8 | 9 | use polysubml_demo::State; 10 | 11 | fn main() { 12 | let mut state = State::new(); 13 | for fname in env::args().skip(1) { 14 | println!("Processing {}", fname); 15 | let data = fs::read_to_string(fname).unwrap(); 16 | // println!(">> {}", data); 17 | 18 | let t0 = Instant::now(); 19 | let res = if state.process(&data) { 20 | state.get_output().unwrap() 21 | } else { 22 | state.get_err().unwrap() 23 | }; 24 | dbg!(t0.elapsed()); 25 | 26 | println!("{}", res); 27 | } 28 | } 29 | 30 | #[cfg(test)] 31 | mod tests { 32 | use super::*; 33 | 34 | #[test] 35 | fn run_tests() { 36 | let mut state = State::new(); 37 | 38 | let data = fs::read_to_string("tests/combined.ml").unwrap(); 39 | for part in data.split("###") { 40 | let s = part.trim(); 41 | if s.is_empty() { 42 | continue; 43 | } 44 | 45 | let (kind, s) = s.split_once('\n').unwrap(); 46 | // println!("{} {}", kind, s); 47 | 48 | if state.process(s) { 49 | if kind != "Good" { 50 | println!("Unexpectedly passed:\n{}", s); 51 | assert_eq!(kind, "Good"); 52 | } 53 | } else { 54 | if kind != "Bad" { 55 | println!("Unexpected error:\n{}", state.get_err().unwrap()); 56 | assert_eq!(kind, "Bad"); 57 | } 58 | }; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/parse_types.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::collections::HashSet; 3 | use std::hash::Hash; 4 | use std::rc::Rc; 5 | 6 | use crate::ast; 7 | use crate::ast::JoinKind; 8 | use crate::ast::PolyKind; 9 | use crate::ast::StringId; 10 | use crate::ast::TypeParam; 11 | use crate::core::*; 12 | use crate::instantiate::InstantionContext; 13 | use crate::instantiate::Substitutions; 14 | use crate::reachability::TypeNodeInd; 15 | use crate::spans::Span; 16 | use crate::spans::SpannedError as SyntaxError; 17 | use crate::spans::SpannedError; 18 | use crate::type_errors::HoleSrc; 19 | use crate::typeck::Bindings; 20 | use crate::typeck::TypeckState; 21 | use crate::unwindmap::UnwindMap; 22 | use crate::unwindmap::sorted; 23 | 24 | use UTypeHead::*; 25 | use VTypeHead::*; 26 | 27 | type Result = std::result::Result; 28 | 29 | // Represent distinct declarations of polymorphic/existential types in the source code 30 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 31 | pub struct SourceLoc(Span); 32 | 33 | enum ParsedTypeHead { 34 | Case(HashMap), 35 | Func(RcParsedType, RcParsedType), 36 | Record(HashMap)>), 37 | 38 | PolyHead(Rc, RcParsedType), 39 | PolyVar(VarSpec), 40 | RecHead(SourceLoc, RcParsedType), 41 | RecVar(SourceLoc), 42 | 43 | VarJoin(JoinKind, HashMap, Option), 44 | 45 | Bot, 46 | Top, 47 | Hole(HoleSrc), 48 | Simple(TypeCtorInd), 49 | } 50 | type ParsedType = (PolyAndRecDeps, Span, ParsedTypeHead); 51 | type RcParsedType = Rc; 52 | 53 | #[derive(Debug, Default, Clone)] 54 | pub struct PolyDeps(HashSet); 55 | impl PolyDeps { 56 | pub fn single(loc: SourceLoc) -> Self { 57 | Self(vec![loc].into_iter().collect()) 58 | } 59 | 60 | fn extend(&mut self, other: &Self) { 61 | self.0.extend(&other.0); 62 | } 63 | 64 | pub fn get(&self, key: SourceLoc) -> bool { 65 | self.0.contains(&key) 66 | } 67 | 68 | pub fn remove(&mut self, key: SourceLoc) { 69 | self.0.remove(&key); 70 | } 71 | } 72 | 73 | #[derive(Default, Clone)] 74 | struct PolyAndRecDeps { 75 | poly: PolyDeps, 76 | rec: PolyDeps, 77 | } 78 | impl PolyAndRecDeps { 79 | fn add(&mut self, child: RcParsedType) -> RcParsedType { 80 | self.poly.extend(&child.0.poly); 81 | self.rec.extend(&child.0.rec); 82 | child 83 | } 84 | } 85 | 86 | pub struct ParsedTypeSig(RcParsedType); 87 | pub struct ParsedLetPattern(RcParsedType, ParsedBindings); 88 | pub struct ParsedFuncSig { 89 | bindings: ParsedBindings, 90 | ret_type: RcParsedType, 91 | func_type: RcParsedType, 92 | } 93 | 94 | pub struct TreeMaterializerState { 95 | cache: HashMap<*const ParsedType, (Value, Use)>, 96 | rec_types: HashMap, 97 | /// ScopeLvl to use when creating holes - note that this will be lower than for abstract types that are added 98 | scopelvl: ScopeLvl, 99 | } 100 | impl TreeMaterializerState { 101 | pub fn new(scopelvl: ScopeLvl) -> Self { 102 | Self { 103 | cache: HashMap::new(), 104 | rec_types: HashMap::new(), 105 | scopelvl, 106 | } 107 | } 108 | 109 | pub fn with<'a>(&'a mut self, core: &'a mut TypeCheckerCore) -> TreeMaterializer<'a> { 110 | TreeMaterializer { 111 | core, 112 | cache: &mut self.cache, 113 | rec_types: &mut self.rec_types, 114 | scopelvl: self.scopelvl, 115 | } 116 | } 117 | } 118 | 119 | pub struct TreeMaterializer<'a> { 120 | core: &'a mut TypeCheckerCore, 121 | cache: &'a mut HashMap<*const ParsedType, (Value, Use)>, 122 | rec_types: &'a mut HashMap, 123 | scopelvl: ScopeLvl, 124 | } 125 | impl<'a> TreeMaterializer<'a> { 126 | fn eval(&self, deps: &PolyAndRecDeps) -> PolyDeps { 127 | let mut res = deps.poly.clone(); 128 | for loc in deps.rec.0.iter().copied() { 129 | res.extend(&self.rec_types.get(&loc).unwrap().1); 130 | } 131 | res 132 | } 133 | 134 | fn materialize_tree_sub_into_ph(&mut self, ty: &ParsedType, ph: (Value, Use)) { 135 | use ParsedTypeHead::*; 136 | 137 | let deps = self.eval(&ty.0); 138 | let (vhead, uhead) = match &ty.2 { 139 | &Case(ref cases) => { 140 | let mut utype_case_arms = HashMap::new(); 141 | let mut vtype_case_arms = Vec::new(); 142 | for (&tag, &(tag_span, ref ty)) in cases { 143 | let (v, u) = self.materialize_tree(ty); 144 | 145 | vtype_case_arms.push(((tag, v), tag_span)); 146 | utype_case_arms.insert(tag, u); 147 | } 148 | 149 | // Grammar ensures that cases is nonempty 150 | let vhead = if cases.len() <= 1 { 151 | VCase { 152 | case: vtype_case_arms[0].0, 153 | } 154 | } else { 155 | VUnion( 156 | vtype_case_arms 157 | .into_iter() 158 | // deps is overly coarse here, but oh well 159 | // This will lead to extra copies of the VCase nodes, but the underlying 160 | // "case" node will still have correct deps, and so won't be copied. 161 | .map(|(case, span)| self.core.new_val(VCase { case }, span, Some(deps.clone()))) 162 | .collect(), 163 | ) 164 | }; 165 | let uhead = UCase { 166 | cases: utype_case_arms, 167 | wildcard: None, 168 | }; 169 | 170 | (vhead, uhead) 171 | } 172 | &Func(ref arg, ref ret) => { 173 | let arg = self.materialize_tree(arg); 174 | let ret = self.materialize_tree(ret); 175 | (VFunc { arg: arg.1, ret: ret.0 }, UFunc { arg: arg.0, ret: ret.1 }) 176 | } 177 | &Record(ref fields) => { 178 | let mut vtype_fields = HashMap::with_capacity(fields.len()); 179 | let mut utype_fields = HashMap::with_capacity(fields.len()); 180 | for (&name, &(span, ref rty, ref wty)) in fields { 181 | let rty = self.materialize_tree(rty); 182 | let wty = wty.as_ref().map(|wty| self.materialize_tree(wty)); 183 | 184 | vtype_fields.insert(name, (rty.0, wty.map(|w| w.1), span)); 185 | utype_fields.insert(name, (rty.1, wty.map(|w| w.0), span)); 186 | } 187 | 188 | (VObj { fields: vtype_fields }, UObj { fields: utype_fields }) 189 | } 190 | &PolyHead(ref data, ref sub) => { 191 | let sub = self.materialize_tree(sub); 192 | (VPolyHead(data.clone(), sub.0, false), UPolyHead(data.clone(), sub.1, false)) 193 | } 194 | &PolyVar(spec) => (VTypeVar(spec), UTypeVar(spec)), 195 | &RecHead(loc, ref sub) => { 196 | self.rec_types.insert(loc, (ph, deps)); 197 | self.materialize_tree_sub_into_ph(sub, ph); 198 | self.rec_types.remove(&loc); 199 | return; 200 | } 201 | 202 | &VarJoin(kind, ref vars, ref sub) => { 203 | let sub = sub.as_deref().map(|ty| self.materialize_tree(ty)); 204 | let var_set = vars.iter().map(|(&vs, &span)| vs).collect(); 205 | 206 | match kind { 207 | JoinKind::Union => { 208 | let mut vals: Vec<_> = vars 209 | .iter() 210 | .map(|(&vs, &span)| { 211 | let deps = PolyDeps::single(vs.loc); 212 | self.core.new_val(VTypeVar(vs), span, Some(deps)) 213 | }) 214 | .collect(); 215 | if let Some(t) = sub { 216 | vals.push(t.0); 217 | } 218 | (VUnion(vals), UDisjointUnion(var_set, sub.map(|t| t.1))) 219 | } 220 | JoinKind::Intersect => { 221 | let mut uses: Vec<_> = vars 222 | .iter() 223 | .map(|(&vs, &span)| { 224 | let deps = PolyDeps::single(vs.loc); 225 | self.core.new_use(UTypeVar(vs), span, Some(deps)) 226 | }) 227 | .collect(); 228 | if let Some(t) = sub { 229 | uses.push(t.1); 230 | } 231 | (VDisjointIntersect(var_set, sub.map(|t| t.0)), UIntersection(uses)) 232 | } 233 | } 234 | } 235 | 236 | RecVar(..) | Simple(..) | Bot | Top | Hole(_) => unreachable!(), 237 | }; 238 | let span = ty.1; 239 | self.core.set_val(ph.0, vhead, span, Some(deps.clone())); 240 | self.core.set_use(ph.1, uhead, span, Some(deps)); 241 | } 242 | 243 | fn materialize_tree_sub(&mut self, ty: &ParsedType) -> (Value, Use) { 244 | use ParsedTypeHead::*; 245 | match &ty.2 { 246 | Case(..) | Func(..) | Record(..) | PolyHead(..) | PolyVar(..) | RecHead(..) | VarJoin(..) => { 247 | let vredirect = self.core.val_placeholder(); 248 | let uredirect = self.core.use_placeholder(); 249 | let ph = (vredirect, uredirect); 250 | self.materialize_tree_sub_into_ph(ty, ph); 251 | ph 252 | } 253 | &RecVar(loc) => self.rec_types.get(&loc).unwrap().0, 254 | &Simple(ty_con) => self.core.custom(ty_con, ty.1), 255 | 256 | &Bot => (self.core.bot(), self.core.new_use(UTypeHead::UBot, ty.1, None)), 257 | &Top => (self.core.new_val(VTypeHead::VTop, ty.1, None), self.core.top_use()), 258 | &Hole(src) => self.core.var(src, self.scopelvl), 259 | } 260 | } 261 | 262 | fn materialize_tree(&mut self, ty: &ParsedType) -> (Value, Use) { 263 | let key = ty as *const _; 264 | if let Some(t) = self.cache.get(&key) { 265 | return *t; 266 | } 267 | 268 | let t = self.materialize_tree_sub(ty); 269 | self.cache.insert(key, t); 270 | t 271 | } 272 | 273 | pub fn add_type(&mut self, parsed: ParsedTypeSig) -> (Value, Use) { 274 | self.materialize_tree(&parsed.0) 275 | } 276 | 277 | fn materialize_and_instantiate_bindings( 278 | &mut self, 279 | parsed: ParsedBindings, 280 | ret_type: RcParsedType, 281 | should_instantiate_ret: bool, 282 | bindings: &mut Bindings, 283 | ) -> Use { 284 | // First materialize all type trees 285 | let mut new_vars: Vec<_> = parsed 286 | .vars 287 | .into_iter() 288 | .map(|(name, (_, ty))| (name, self.materialize_tree(&ty).0)) 289 | .collect(); 290 | let mut ret_type = self.materialize_tree(&ret_type).1; 291 | 292 | if !parsed.types.is_empty() { 293 | bindings.scopelvl.0 += 1; 294 | } 295 | 296 | let mut new_types = HashMap::new(); 297 | // Now see if we have to instantiate type parameters to local abstract types 298 | for spec in parsed.poly_heads { 299 | let subs = spec 300 | .params 301 | .iter() 302 | .copied() 303 | .map(|(name, span)| (name, self.core.add_abstract_type(name, span, bindings.scopelvl))) 304 | .collect(); 305 | 306 | let mut ctx = InstantionContext::new(self.core, Substitutions::Abs(&subs), spec.loc); 307 | for (name, v) in new_vars.iter_mut() { 308 | let old = *v; 309 | *v = ctx.instantiate_val(*v); 310 | // println!("{}: inst {}->{}", name.into_inner(), old.0.0, v.0.0); 311 | } 312 | if should_instantiate_ret { 313 | ret_type = ctx.instantiate_use(ret_type); 314 | } 315 | 316 | // Add the new types to new_types 317 | for (name, tycon) in subs { 318 | new_types.insert((spec.loc, name), tycon); 319 | } 320 | } 321 | 322 | for (name, ty) in new_vars { 323 | // println!("var {}: {}", name.into_inner(), ty.0.0); 324 | bindings.vars.insert(name, ty); 325 | } 326 | for (alias, loc, name) in parsed.types { 327 | // println!("type {}: tycon {}", alias.into_inner(), new_types.get(&(loc, name)).unwrap().0); 328 | bindings.types.insert(alias, *new_types.get(&(loc, name)).unwrap()); 329 | } 330 | ret_type 331 | } 332 | 333 | pub fn add_pattern_bound(&mut self, parsed: &ParsedLetPattern) -> Use { 334 | self.materialize_tree(&parsed.0).1 335 | } 336 | 337 | pub fn add_pattern(&mut self, parsed: ParsedLetPattern, bindings: &mut Bindings) -> Use { 338 | // println!("add pat {} {}", parsed.1.types.len(), parsed.1.poly_heads.len()); 339 | self.materialize_and_instantiate_bindings(parsed.1, parsed.0, false, bindings) 340 | } 341 | 342 | pub fn add_func_type(&mut self, parsed: &ParsedFuncSig) -> Value { 343 | self.materialize_tree(&parsed.func_type).0 344 | } 345 | 346 | pub fn add_func_sig(&mut self, parsed: ParsedFuncSig, bindings: &mut Bindings) -> Use { 347 | self.materialize_and_instantiate_bindings(parsed.bindings, parsed.ret_type, true, bindings) 348 | } 349 | } 350 | 351 | #[derive(Default)] 352 | struct ParsedBindings { 353 | vars: HashMap, 354 | types: Vec<(StringId, SourceLoc, StringId)>, 355 | poly_heads: Vec>, 356 | } 357 | impl ParsedBindings { 358 | fn insert_var(&mut self, name: StringId, span: Span, ty: RcParsedType) -> Result<()> { 359 | if let Some((old_span, _)) = self.vars.insert(name, (span, ty)) { 360 | Err(SyntaxError::new2( 361 | "SyntaxError: Repeated variable binding in pattern", 362 | span, 363 | "Note: Name was already bound here", 364 | old_span, 365 | )) 366 | } else { 367 | Ok(()) 368 | } 369 | } 370 | } 371 | 372 | fn flip(k: &JoinKind) -> JoinKind { 373 | match k { 374 | JoinKind::Union => JoinKind::Intersect, 375 | JoinKind::Intersect => JoinKind::Union, 376 | } 377 | } 378 | 379 | enum TypeVar { 380 | Rec(SourceLoc), 381 | Param(VarSpec), 382 | } 383 | pub struct TypeParser<'a> { 384 | global_types: &'a UnwindMap, 385 | local_types: UnwindMap, 386 | 387 | // If loc isn't allowed in either kind, remove it from the map 388 | // Can use regular hashmap here becuase each loc will only be processed 389 | // at most once in a given tree, so no need for unwinding. 390 | join_allowed: HashMap, 391 | } 392 | impl<'a> TypeParser<'a> { 393 | pub fn new(global_types: &'a UnwindMap) -> Self { 394 | Self { 395 | global_types, 396 | local_types: UnwindMap::new(), 397 | join_allowed: HashMap::new(), 398 | } 399 | } 400 | 401 | fn parse_union_or_intersect_type( 402 | &mut self, 403 | deps: &mut PolyAndRecDeps, 404 | kind: JoinKind, 405 | exprs: &[ast::STypeExpr], 406 | ) -> Result { 407 | let mut vars = HashMap::new(); 408 | let mut default = None; 409 | 410 | use ParsedTypeHead::*; 411 | for expr in exprs.iter() { 412 | let sub = deps.add(self.parse_type_sub(expr)?); 413 | 414 | match sub.2 { 415 | PolyVar(spec) => { 416 | if self.join_allowed.get(&spec.loc) == Some(&kind) { 417 | vars.insert(spec, sub.1); 418 | continue; 419 | } 420 | } 421 | Top | Bot => { 422 | return Err(SyntaxError::new1( 423 | "SyntaxError: Any and never are not allowed in union or intersection types.", 424 | sub.1, 425 | )); 426 | } 427 | 428 | _ => (), 429 | }; 430 | 431 | match default { 432 | None => default = Some(sub), 433 | Some(old) => { 434 | return Err(SyntaxError::new2( 435 | "SyntaxError: Repeated ineligible join type", 436 | sub.1, 437 | "Note: Previous ineligible type here", 438 | old.1, 439 | )); 440 | } 441 | } 442 | } 443 | 444 | Ok(VarJoin(kind, vars, default)) 445 | } 446 | 447 | fn parse_type_sub_contravariant(&mut self, tyexpr: &ast::STypeExpr) -> Result { 448 | for v in self.join_allowed.values_mut() { 449 | *v = flip(v); 450 | } 451 | let res = self.parse_type_sub(tyexpr); 452 | for v in self.join_allowed.values_mut() { 453 | *v = flip(v); 454 | } 455 | 456 | res 457 | } 458 | 459 | fn parse_type_sub_invariant(&mut self, tyexpr: &ast::STypeExpr) -> Result { 460 | let temp = std::mem::take(&mut self.join_allowed); 461 | let res = self.parse_type_sub(tyexpr); 462 | self.join_allowed = temp; 463 | 464 | res 465 | } 466 | 467 | fn parse_type_sub(&mut self, tyexpr: &ast::STypeExpr) -> Result { 468 | use ast::TypeExpr::*; 469 | let mut deps = PolyAndRecDeps::default(); 470 | let span = tyexpr.1; 471 | let head = match &tyexpr.0 { 472 | Bot => ParsedTypeHead::Bot, 473 | Case(cases) => { 474 | let mut m = HashMap::new(); 475 | for &((tag, tag_span), ref wrapped_expr) in cases { 476 | let sub = deps.add(self.parse_type_sub(wrapped_expr)?); 477 | m.insert(tag, (tag_span, sub)); 478 | } 479 | ParsedTypeHead::Case(m) 480 | } 481 | Func(lhs, rhs) => { 482 | let lhs = deps.add(self.parse_type_sub_contravariant(lhs)?); 483 | let rhs = deps.add(self.parse_type_sub(rhs)?); 484 | ParsedTypeHead::Func(lhs, rhs) 485 | } 486 | Record(fields) => { 487 | let mut m = HashMap::new(); 488 | 489 | for &((name, name_span), ref type_decl) in fields { 490 | use ast::FieldTypeDecl::*; 491 | 492 | match type_decl { 493 | Imm(ty) => { 494 | let ty = deps.add(self.parse_type_sub(ty)?); 495 | m.insert(name, (name_span, ty, None)); 496 | } 497 | // Mutable field with read and write types the same 498 | RWSame(ty) => { 499 | let ty = deps.add(self.parse_type_sub_invariant(ty)?); 500 | m.insert(name, (name_span, ty.clone(), Some(ty))); 501 | } 502 | RWPair(ty, ty2) => { 503 | let ty = deps.add(self.parse_type_sub(ty)?); 504 | let ty2 = deps.add(self.parse_type_sub_contravariant(ty2)?); 505 | m.insert(name, (name_span, ty, Some(ty2))); 506 | } 507 | } 508 | } 509 | ParsedTypeHead::Record(m) 510 | } 511 | Hole => ParsedTypeHead::Hole(HoleSrc::Explicit(span)), 512 | Ident(s) => { 513 | if let Some(ty) = self.local_types.get(s) { 514 | match ty { 515 | &TypeVar::Rec(loc) => { 516 | deps.rec.0.insert(loc); 517 | ParsedTypeHead::RecVar(loc) 518 | } 519 | &TypeVar::Param(spec) => { 520 | deps.poly.0.insert(spec.loc); 521 | ParsedTypeHead::PolyVar(spec) 522 | } 523 | } 524 | } else if let Some(&ty) = self.global_types.get(s) { 525 | ParsedTypeHead::Simple(ty) 526 | } else { 527 | return Err(SyntaxError::new1("SyntaxError: Undefined type or type constructor", span)); 528 | } 529 | } 530 | &Poly(ref params, ref def, kind) => { 531 | let loc = SourceLoc(span); 532 | let mark = self.local_types.unwind_point(); 533 | self.join_allowed.insert(loc, match kind { 534 | PolyKind::Universal => JoinKind::Union, 535 | PolyKind::Existential => JoinKind::Intersect, 536 | }); 537 | 538 | let mut parsed_params = HashMap::new(); 539 | for param in params.iter().copied() { 540 | parsed_params.insert(param.name.0, param.name.1); 541 | self.local_types 542 | .insert(param.alias.0, TypeVar::Param(VarSpec { loc, name: param.name.0 })); 543 | } 544 | 545 | let sub = deps.add(self.parse_type_sub(def)?); 546 | 547 | self.join_allowed.remove(&loc); 548 | self.local_types.unwind(mark); 549 | deps.poly.0.remove(&loc); 550 | 551 | let spec = Rc::new(PolyHeadData { 552 | kind, 553 | loc, 554 | params: parsed_params.into_iter().collect(), 555 | }); 556 | ParsedTypeHead::PolyHead(spec, sub) 557 | } 558 | &RecursiveDef(name, ref def) => { 559 | let loc = SourceLoc(span); 560 | 561 | let mark = self.local_types.unwind_point(); 562 | self.local_types.insert(name, TypeVar::Rec(loc)); 563 | let sub = deps.add(self.parse_type_sub(def)?); 564 | self.local_types.unwind(mark); 565 | 566 | use ParsedTypeHead::*; 567 | if !matches!(sub.2, Case(..) | Func(..) | Record(..) | PolyHead(..) | RecHead(..)) { 568 | return Err(SyntaxError::new1( 569 | "SyntaxError: Recursive types must be defined as a function, record, variant, or recursive type.", 570 | sub.1, 571 | )); 572 | } 573 | 574 | deps.rec.0.remove(&loc); 575 | ParsedTypeHead::RecHead(loc, sub) 576 | } 577 | Top => ParsedTypeHead::Top, 578 | &VarJoin(kind, ref children) => self.parse_union_or_intersect_type(&mut deps, kind, children)?, 579 | }; 580 | Ok(Rc::new((deps, span, head))) 581 | } 582 | 583 | fn parse_type_or_hole_sub(&mut self, tyexpr: Option<&ast::STypeExpr>, span_before_hole: Span) -> Result { 584 | tyexpr.map(|tyexpr| self.parse_type_sub(tyexpr)).unwrap_or_else(|| { 585 | Ok(Rc::new(( 586 | PolyAndRecDeps::default(), 587 | span_before_hole, 588 | ParsedTypeHead::Hole(HoleSrc::OptAscribe(span_before_hole)), 589 | ))) 590 | }) 591 | } 592 | 593 | //////////////////////////////////////////////////////////////////////////////////////// 594 | pub fn parse_type(&mut self, tyexpr: &ast::STypeExpr) -> Result { 595 | Ok(ParsedTypeSig(self.parse_type_sub(tyexpr)?)) 596 | } 597 | 598 | pub fn parse_type_or_hole(&mut self, tyexpr: Option<&ast::STypeExpr>, span_before_hole: Span) -> Result { 599 | Ok(ParsedTypeSig(self.parse_type_or_hole_sub(tyexpr, span_before_hole)?)) 600 | } 601 | 602 | fn add_type_params( 603 | &mut self, 604 | loc: SourceLoc, 605 | ty_params: &[TypeParam], 606 | kind: ast::PolyKind, 607 | out: &mut ParsedBindings, 608 | ) -> Option> { 609 | if ty_params.is_empty() { 610 | return None; 611 | } 612 | 613 | let mut parsed_params = HashMap::new(); 614 | for param in ty_params.iter().copied() { 615 | let (name, name_span) = param.name; 616 | let (alias, alias_span) = param.alias; 617 | 618 | parsed_params.insert(name, name_span); 619 | self.local_types.insert(alias, TypeVar::Param(VarSpec { loc, name })); 620 | out.types.push((alias, loc, name)); 621 | } 622 | 623 | let spec = Rc::new(PolyHeadData { 624 | kind, 625 | loc, 626 | params: parsed_params.into_iter().collect(), 627 | }); 628 | out.poly_heads.push(spec.clone()); 629 | Some(spec) 630 | } 631 | 632 | fn parse_let_pattern_sub( 633 | &mut self, 634 | pat: &ast::LetPattern, 635 | out: &mut ParsedBindings, 636 | no_typed_var_allowed: bool, 637 | ) -> Result { 638 | use ast::LetPattern::*; 639 | 640 | Ok(match pat { 641 | &Var((name, span), ref tyexpr) => { 642 | let ty = if let Some(tyexpr) = tyexpr.as_ref() { 643 | self.parse_type_sub(tyexpr)? 644 | } else { 645 | let head = if name.is_some() { 646 | // If pattern does not allow unpathenthesized typed vars, it needs to be 647 | // surrounded in pathenthesis when adding a type annotation. 648 | let src = if no_typed_var_allowed { 649 | HoleSrc::BareVarPattern(span) 650 | } else { 651 | HoleSrc::OptAscribe(span) 652 | }; 653 | ParsedTypeHead::Hole(src) 654 | } else { 655 | ParsedTypeHead::Top 656 | }; 657 | Rc::new((PolyAndRecDeps::default(), span, head)) 658 | }; 659 | if let Some(name) = name { 660 | out.insert_var(name, span, ty.clone())?; 661 | } 662 | ty 663 | } 664 | 665 | &Case((tag, span), ref val_pat) => { 666 | let sub = self.parse_let_pattern_sub(val_pat, out, true)?; 667 | 668 | let deps = sub.0.clone(); 669 | let mut m = HashMap::new(); 670 | m.insert(tag, (span, sub)); 671 | 672 | Rc::new((deps, span, ParsedTypeHead::Case(m))) 673 | } 674 | &Record(((ref ty_params, ref pairs), span)) => { 675 | let loc = SourceLoc(span); 676 | let mark = self.local_types.unwind_point(); 677 | 678 | let poly_spec = self.add_type_params(loc, ty_params, ast::PolyKind::Existential, out); 679 | 680 | let mut field_names = HashMap::with_capacity(pairs.len()); 681 | let mut fields = HashMap::new(); 682 | let mut deps = PolyAndRecDeps::default(); 683 | for &((name, name_span), ref sub_pattern) in pairs { 684 | if let Some(old_span) = field_names.insert(name, name_span) { 685 | return Err(SyntaxError::new2( 686 | "SyntaxError: Repeated field pattern name", 687 | name_span, 688 | "Note: Field was already bound here", 689 | old_span, 690 | )); 691 | } 692 | 693 | let sub = deps.add(self.parse_let_pattern_sub(sub_pattern, out, false)?); 694 | fields.insert(name, (name_span, sub, None)); 695 | } 696 | self.local_types.unwind(mark); 697 | 698 | let mut new_type = Rc::new((deps.clone(), span, ParsedTypeHead::Record(fields))); 699 | if let Some(spec) = poly_spec { 700 | deps.poly.remove(loc); 701 | new_type = Rc::new((deps, span, ParsedTypeHead::PolyHead(spec, new_type))); 702 | } 703 | 704 | new_type 705 | } 706 | }) 707 | } 708 | 709 | pub fn parse_let_pattern(&mut self, pat: &ast::LetPattern, no_typed_var_allowed: bool) -> Result { 710 | let mut out = ParsedBindings::default(); 711 | let ty = self.parse_let_pattern_sub(pat, &mut out, no_typed_var_allowed)?; 712 | Ok(ParsedLetPattern(ty, out)) 713 | } 714 | 715 | pub fn parse_func_sig( 716 | &mut self, 717 | ty_params: &Option>, 718 | arg_pat: &(ast::LetPattern, Span), 719 | ret_type: Option<&ast::STypeExpr>, 720 | span: Span, 721 | ) -> Result { 722 | let (arg_pat, arg_pat_span) = (&arg_pat.0, arg_pat.1); 723 | 724 | let ty_params = ty_params.as_ref().map(|v| &v[..]).unwrap_or_default(); 725 | let loc = SourceLoc(span); 726 | let mut out = ParsedBindings::default(); 727 | 728 | let mark = self.local_types.unwind_point(); 729 | let poly_spec = self.add_type_params(loc, ty_params, ast::PolyKind::Universal, &mut out); 730 | 731 | let mut deps = PolyAndRecDeps::default(); 732 | let arg_bound = deps.add(self.parse_let_pattern_sub(arg_pat, &mut out, true)?); 733 | let ret_type = deps.add(self.parse_type_or_hole_sub(ret_type, arg_pat_span)?); 734 | 735 | self.local_types.unwind(mark); 736 | 737 | let mut func_type = Rc::new((deps, span, ParsedTypeHead::Func(arg_bound, ret_type.clone()))); 738 | 739 | if let Some(spec) = poly_spec { 740 | func_type = Rc::new((PolyAndRecDeps::default(), span, ParsedTypeHead::PolyHead(spec, func_type))); 741 | } 742 | 743 | Ok(ParsedFuncSig { 744 | bindings: out, 745 | ret_type, 746 | func_type, 747 | }) 748 | } 749 | } 750 | -------------------------------------------------------------------------------- /src/reachability.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | #[derive(Debug, Clone)] 4 | struct OrderedMap { 5 | keys: Vec, 6 | m: HashMap, 7 | } 8 | impl OrderedMap { 9 | fn new() -> Self { 10 | Self { 11 | keys: Vec::new(), 12 | m: HashMap::new(), 13 | } 14 | } 15 | 16 | fn insert(&mut self, k: K, v: V) -> Option { 17 | let old = self.m.insert(k.clone(), v); 18 | if old.is_none() { 19 | self.keys.push(k); 20 | } 21 | old 22 | } 23 | 24 | fn iter_keys(&self) -> std::slice::Iter { 25 | self.keys.iter() 26 | } 27 | 28 | fn retain(&mut self, f: impl Fn(&K) -> bool) { 29 | self.m.retain(|k, _| f(k)); 30 | // Also remove any extra keys left from unrelated deletions 31 | // since journal removal may have left extra keys in the list. 32 | self.keys.retain(|k| self.m.contains_key(k)); 33 | } 34 | } 35 | 36 | pub trait ExtNodeDataTrait { 37 | fn truncate(&mut self, i: TypeNodeInd); 38 | } 39 | 40 | pub trait EdgeDataTrait: Clone { 41 | fn update(&mut self, other: &Self) -> bool; 42 | fn expand(self, hole: &ExtNodeData, ind: TypeNodeInd) -> Self; 43 | } 44 | 45 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 46 | pub struct TypeNodeInd(pub usize); 47 | 48 | struct ReachabilityNode { 49 | data: ExtNodeData, 50 | flows_from: OrderedMap, 51 | flows_to: OrderedMap, 52 | } 53 | impl ReachabilityNode { 54 | fn fix_and_truncate(&mut self, i: TypeNodeInd) { 55 | self.data.truncate(i); 56 | self.flows_from.retain(|&k| k < i); 57 | self.flows_to.retain(|&k| k < i); 58 | } 59 | } 60 | 61 | pub struct Reachability { 62 | nodes: Vec>, 63 | 64 | // Nodes past this point may be reverted in case of a type error 65 | // Value of 0 indicates no mark is set (or if a mark is set, there's nothing to do anyway) 66 | rewind_mark: TypeNodeInd, 67 | journal: Vec<(TypeNodeInd, TypeNodeInd, Option)>, 68 | } 69 | impl> Reachability { 70 | pub fn new() -> Self { 71 | Self { 72 | nodes: Vec::new(), 73 | rewind_mark: TypeNodeInd(0), 74 | journal: Vec::new(), 75 | } 76 | } 77 | 78 | pub fn len(&self) -> usize { 79 | self.nodes.len() 80 | } 81 | 82 | pub fn get(&self, i: TypeNodeInd) -> Option<&ExtNodeData> { 83 | self.nodes.get(i.0).map(|rn| &rn.data) 84 | } 85 | pub fn get_mut(&mut self, i: TypeNodeInd) -> Option<&mut ExtNodeData> { 86 | self.nodes.get_mut(i.0).map(|rn| &mut rn.data) 87 | } 88 | pub fn get_edge(&self, lhs: TypeNodeInd, rhs: TypeNodeInd) -> Option<&ExtEdgeData> { 89 | self.nodes.get(lhs.0).and_then(|rn| rn.flows_to.m.get(&rhs)) 90 | } 91 | 92 | pub fn add_node(&mut self, data: ExtNodeData) -> TypeNodeInd { 93 | let i = self.len(); 94 | 95 | let n = ReachabilityNode { 96 | data, 97 | flows_from: OrderedMap::new(), 98 | flows_to: OrderedMap::new(), 99 | }; 100 | self.nodes.push(n); 101 | TypeNodeInd(i) 102 | } 103 | 104 | fn update_edge_value(&mut self, lhs: TypeNodeInd, rhs: TypeNodeInd, val: ExtEdgeData) { 105 | let old = self.nodes[lhs.0].flows_to.insert(rhs, val.clone()); 106 | self.nodes[rhs.0].flows_from.insert(lhs, val); 107 | 108 | // If the nodes are >= rewind_mark, they'll be removed during rewind anyway 109 | // so we only have to journal edge values when both are below the mark. 110 | if lhs < self.rewind_mark && rhs < self.rewind_mark { 111 | self.journal.push((lhs, rhs, old)); 112 | } 113 | } 114 | 115 | pub fn add_edge( 116 | &mut self, 117 | lhs: TypeNodeInd, 118 | rhs: TypeNodeInd, 119 | edge_val: ExtEdgeData, 120 | out: &mut Vec<(TypeNodeInd, TypeNodeInd, ExtEdgeData)>, 121 | ) { 122 | // println!("add_edge {}->{}", lhs.0, rhs.0); 123 | let mut work = vec![(lhs, rhs, edge_val)]; 124 | 125 | while let Some((lhs, rhs, mut edge_val)) = work.pop() { 126 | // println!(" add_edge_sub {}->{}", lhs.0, rhs.0); 127 | let old_edge = self.nodes[lhs.0].flows_to.m.get_mut(&rhs); 128 | match old_edge { 129 | Some(old) => { 130 | let mut old = old.clone(); 131 | if old.update(&edge_val) { 132 | // println!("reevaluating {} {}", lhs.0, rhs.0); 133 | edge_val = old; // updated value will be inserted into map below 134 | } else { 135 | // New edge value did not cause an update compared to existing edge value. 136 | continue; 137 | } 138 | } 139 | None => {} 140 | }; 141 | self.update_edge_value(lhs, rhs, edge_val.clone()); 142 | 143 | let temp = edge_val.clone().expand(&self.nodes[lhs.0].data, lhs); 144 | for &lhs2 in self.nodes[lhs.0].flows_from.iter_keys() { 145 | work.push((lhs2, rhs, temp.clone())); 146 | } 147 | 148 | let temp = edge_val.clone().expand(&self.nodes[rhs.0].data, rhs); 149 | for &rhs2 in self.nodes[rhs.0].flows_to.iter_keys() { 150 | work.push((lhs, rhs2, temp.clone())); 151 | } 152 | 153 | // Inform the caller that a new edge was added 154 | out.push((lhs, rhs, edge_val)); 155 | } 156 | } 157 | 158 | pub fn save(&mut self) { 159 | assert!(self.rewind_mark.0 == 0); 160 | self.rewind_mark = TypeNodeInd(self.nodes.len()); 161 | } 162 | 163 | pub fn revert(&mut self) { 164 | let i = self.rewind_mark; 165 | self.rewind_mark = TypeNodeInd(0); 166 | self.nodes.truncate(i.0); 167 | 168 | while let Some((lhs, rhs, val)) = self.journal.pop() { 169 | if let Some(val) = val { 170 | *self.nodes[lhs.0].flows_to.m.get_mut(&rhs).unwrap() = val.clone(); 171 | *self.nodes[rhs.0].flows_from.m.get_mut(&lhs).unwrap() = val; 172 | } else { 173 | self.nodes[lhs.0].flows_to.m.remove(&rhs); 174 | self.nodes[rhs.0].flows_from.m.remove(&lhs); 175 | } 176 | } 177 | 178 | // If we removed edges above, the edge maps will have extra keys 179 | // fix_and_truncate will fix that in addition to truncating edges >= i 180 | for n in self.nodes.iter_mut() { 181 | n.fix_and_truncate(i); 182 | } 183 | } 184 | 185 | pub fn make_permanent(&mut self) { 186 | self.rewind_mark = TypeNodeInd(0); 187 | self.journal.clear(); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/spans.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::collections::HashSet; 3 | use std::error; 4 | use std::fmt; 5 | 6 | #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] 7 | pub struct Span(usize); 8 | 9 | pub type Spanned = (T, Span); 10 | 11 | #[derive(Debug)] 12 | struct Source { 13 | s: String, 14 | line_offsets: Box<[usize]>, 15 | } 16 | impl Source { 17 | fn new(mut s: String) -> Self { 18 | // Ensure trailing space so lines will display nicely 19 | if !s.ends_with('\n') { 20 | s.push('\n'); 21 | } 22 | 23 | // Precalculate the offsets of each line in the string 24 | let line_offsets = s 25 | .lines() 26 | .map(|line| line.as_ptr() as usize - s.as_ptr() as usize) 27 | .chain(std::iter::once(s.len())) 28 | .collect(); 29 | Self { s, line_offsets } 30 | } 31 | 32 | fn get_lineno(&self, off: usize) -> usize { 33 | match self.line_offsets.binary_search(&off) { 34 | Ok(ind) => ind, 35 | Err(ind) => ind - 1, 36 | } 37 | } 38 | 39 | fn get_pos(&self, off: usize) -> (usize, usize) { 40 | let lineno = self.get_lineno(off); 41 | let start = self.line_offsets[lineno]; 42 | (lineno, off - start) 43 | } 44 | 45 | fn get_line(&self, lineno: usize) -> &str { 46 | if lineno + 1 >= self.line_offsets.len() { 47 | return "\n"; 48 | } 49 | 50 | let off = self.line_offsets[lineno]; 51 | let off2 = self.line_offsets[lineno + 1]; 52 | &self.s[off..off2] 53 | } 54 | 55 | fn print_line_if_nonempty(&self, out: &mut String, lineno: usize) { 56 | let line = self.get_line(lineno); 57 | if !line.trim().is_empty() { 58 | *out += line; 59 | } 60 | } 61 | 62 | fn print_nonempty_lines(&self, out: &mut String, y1: usize, y2: usize) { 63 | for y in y1..y2 { 64 | self.print_line_if_nonempty(out, y); 65 | } 66 | } 67 | 68 | fn print_middle_context(&self, out: &mut String, y1: usize, y2: usize) { 69 | // Make range inclusive of start 70 | let y1 = y1 + 1; 71 | if y2 >= y1 + 2 + CONTEXT_LINES * 2 { 72 | let skipped = y2 - y1 - CONTEXT_LINES * 2; 73 | self.print_nonempty_lines(out, y1, y1 + CONTEXT_LINES); 74 | *out += &format!("... {} lines omitted\n", skipped); 75 | self.print_nonempty_lines(out, y2 - CONTEXT_LINES, y2); 76 | } else { 77 | self.print_nonempty_lines(out, y1, y2); 78 | } 79 | } 80 | } 81 | 82 | const CONTEXT_LINES: usize = 2; 83 | 84 | #[derive(Debug, Default)] 85 | pub struct SpanManager { 86 | sources: Vec, 87 | spans: Vec<(usize, usize, usize)>, 88 | } 89 | impl SpanManager { 90 | pub fn add_source(&mut self, source: String) -> SpanMaker { 91 | let i = self.sources.len(); 92 | self.sources.push(Source::new(source)); 93 | SpanMaker { 94 | parent: self, 95 | source_ind: i, 96 | pool: Default::default(), 97 | } 98 | } 99 | 100 | fn new_span(&mut self, source_ind: usize, l: usize, r: usize) -> Span { 101 | let i = self.spans.len(); 102 | self.spans.push((source_ind, l, r)); 103 | Span(i) 104 | } 105 | 106 | //////////////////////////////////////////////////////////////////////////////////////////// 107 | /// Printing functions 108 | fn highlight_line(&self, out: &mut String, line: &str, parts: Vec<(&str, usize, usize)>) { 109 | *out += line; 110 | let mut pos = 0; 111 | 112 | for (s, mut start, end) in parts { 113 | start = std::cmp::max(start, pos); 114 | if start >= end { 115 | continue; 116 | } 117 | 118 | *out += &" ".repeat(start - pos); 119 | *out += &s.repeat(end - start); 120 | pos = end; 121 | } 122 | 123 | *out += &" ".repeat(line.len().saturating_sub(pos)); 124 | *out += "\n"; 125 | } 126 | 127 | fn print(&self, out: &mut String, span: Span) { 128 | let (source_ind, l, r) = self.spans[span.0]; 129 | let source = &self.sources[source_ind]; 130 | 131 | // println!("source offsets {} {} {:?}", source.s.len(), source.line_offsets.len(), source.line_offsets); 132 | let (y1, x1) = source.get_pos(l); 133 | let (y2, x2) = source.get_pos(r); 134 | 135 | // Extra leading line of context 136 | source.print_nonempty_lines(out, y1.saturating_sub(CONTEXT_LINES), y1); 137 | 138 | let line = source.get_line(y1); 139 | let start = x1; 140 | let end = if y1 == y2 { x2 } else { line.len() }; 141 | self.highlight_line(out, line, vec![("^", start, start + 1), ("~", start + 1, end)]); 142 | 143 | source.print_middle_context(out, y1, y2); 144 | if y2 > y1 { 145 | let line = source.get_line(y2); 146 | self.highlight_line(out, line, vec![("~", 0, x2)]); 147 | } 148 | 149 | // Extra trailing line of context 150 | source.print_nonempty_lines(out, y2 + 1, y2 + 1 + CONTEXT_LINES); 151 | } 152 | 153 | fn print_insertion(&self, out: &mut String, before: &str, span: Span, after: &str) { 154 | let (source_ind, l, r) = self.spans[span.0]; 155 | let source = &self.sources[source_ind]; 156 | 157 | let insertions = [(before, source.get_pos(l)), (after, source.get_pos(r))]; 158 | // Skip when insertion string is empty 159 | let insertions = insertions.iter().copied().filter(|t| !t.0.is_empty()); 160 | // Group by line 161 | use itertools::Itertools; 162 | let insertions = insertions.chunk_by(|&(s, (y, x))| y); 163 | 164 | let mut prev = None; 165 | for (y, chunk) in insertions.into_iter() { 166 | if let Some(prev) = prev { 167 | source.print_middle_context(out, prev, y); 168 | } else { 169 | source.print_nonempty_lines(out, y.saturating_sub(CONTEXT_LINES), y); 170 | } 171 | prev = Some(y); 172 | 173 | let mut line = source.get_line(y).to_string(); 174 | let mut inserted = 0; 175 | let mut highlights = Vec::new(); 176 | 177 | for (s, (_, mut x)) in chunk { 178 | x += inserted; 179 | line.insert_str(x, s); 180 | inserted += s.len(); 181 | highlights.push(("+", x, x + s.len())); 182 | } 183 | 184 | self.highlight_line(out, &line, highlights); 185 | } 186 | 187 | if let Some(y) = prev { 188 | source.print_nonempty_lines(out, y + 1, y + 1 + CONTEXT_LINES); 189 | } 190 | } 191 | } 192 | 193 | #[derive(Debug)] 194 | pub struct SpanMaker<'a> { 195 | parent: &'a mut SpanManager, 196 | source_ind: usize, 197 | pool: HashMap<(usize, usize), Span>, 198 | } 199 | impl<'a> SpanMaker<'a> { 200 | pub fn span(&mut self, l: usize, r: usize) -> Span { 201 | // Make the borrow checker happy 202 | let source_ind = self.source_ind; 203 | let parent = &mut self.parent; 204 | 205 | *self.pool.entry((l, r)).or_insert_with(|| parent.new_span(source_ind, l, r)) 206 | } 207 | } 208 | 209 | #[derive(Debug)] 210 | enum Item { 211 | Str(String), 212 | Span(Span), 213 | Insert(String, Span, String), 214 | } 215 | 216 | #[derive(Debug)] 217 | pub struct SpannedError { 218 | // pairs: Vec<(String, Span)>, 219 | items: Vec, 220 | } 221 | 222 | impl SpannedError { 223 | pub fn new() -> Self { 224 | Self { items: Vec::new() } 225 | } 226 | 227 | pub fn push_str(&mut self, s: impl Into) { 228 | self.items.push(Item::Str(s.into())); 229 | } 230 | 231 | pub fn push_span(&mut self, span: Span) { 232 | self.items.push(Item::Span(span)); 233 | } 234 | 235 | pub fn push_insert(&mut self, before: impl Into, span: Span, after: impl Into) { 236 | self.items.push(Item::Insert(before.into(), span, after.into())); 237 | } 238 | 239 | pub fn new1(s1: impl Into, s2: Span) -> Self { 240 | let mut new = Self::new(); 241 | new.push_str(s1); 242 | new.push_span(s2); 243 | new 244 | } 245 | 246 | pub fn new2(s1: impl Into, s2: Span, s3: impl Into, s4: Span) -> Self { 247 | let mut new = Self::new(); 248 | new.push_str(s1); 249 | new.push_span(s2); 250 | new.push_str(s3); 251 | new.push_span(s4); 252 | new 253 | } 254 | 255 | pub fn print(&self, sm: &SpanManager) -> String { 256 | let mut out = String::new(); 257 | for item in self.items.iter() { 258 | match item { 259 | &Item::Str(ref s) => { 260 | out += s; 261 | out += "\n"; 262 | } 263 | &Item::Span(span) => sm.print(&mut out, span), 264 | &Item::Insert(ref before, span, ref after) => sm.print_insertion(&mut out, before, span, after), 265 | } 266 | } 267 | out 268 | } 269 | } 270 | impl fmt::Display for SpannedError { 271 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 272 | Ok(()) 273 | } 274 | } 275 | impl error::Error for SpannedError {} 276 | -------------------------------------------------------------------------------- /src/type_errors.rs: -------------------------------------------------------------------------------- 1 | // immutable field 2 | // missing field 3 | // unhandled case 4 | // abs escape 5 | // general mismatch 6 | 7 | use std::collections::HashSet; 8 | use std::u32; 9 | 10 | use crate::ast; 11 | use crate::ast::InstantiateSourceKind; 12 | use crate::ast::StringId; 13 | use crate::core::*; 14 | use crate::spans::Span; 15 | use crate::spans::SpannedError; 16 | 17 | #[derive(Debug, Clone, Copy)] 18 | pub enum HoleSrc { 19 | /// An explicit _ in a type annotation in the source code. 20 | Explicit(Span), 21 | /// Insert :_ after span (pattern, return type, or mut field with missing optional annotation) 22 | OptAscribe(Span), 23 | /// span -> span before name after (used for polymorphic instantiation parameters) 24 | Instantiation((Span, InstantiateSourceKind), StringId), 25 | /// Wrap the given expr in an explicit type annotation 26 | CheckedExpr(Span), 27 | BareVarPattern(Span), // Same as CheckedExpr but with higher priority 28 | } 29 | impl HoleSrc { 30 | fn priority(&self) -> usize { 31 | use HoleSrc::*; 32 | match self { 33 | Explicit(..) => 100, 34 | OptAscribe(..) => 81, 35 | BareVarPattern(..) => 81, 36 | Instantiation(..) => 64, 37 | CheckedExpr(..) => 0, 38 | } 39 | } 40 | } 41 | 42 | pub struct PartialTypeError(SpannedError, ScopeLvl, Vec); 43 | impl PartialTypeError { 44 | fn new() -> Self { 45 | Self(SpannedError::new(), ScopeLvl(u32::MAX), Vec::new()) 46 | } 47 | 48 | fn push(&mut self, msg: String, span: Span) { 49 | self.0.push_str(msg); 50 | self.0.push_span(span); 51 | // Separately track added spans so we can check whether a root matches an already reported span 52 | self.2.push(span); 53 | } 54 | 55 | pub fn into(self) -> Result { 56 | Err(self.0) 57 | } 58 | 59 | pub fn add_hole_int(&mut self, core: &TypeCheckerCore, strings: &mut lasso::Rodeo, pair: (Value, Use)) { 60 | // First follow the FlowReasons backwards to get a list of holes (inference variables) and 61 | // roots involved in the detected type contradiction. 62 | let mut seen = HashSet::new(); 63 | let mut holes = Vec::new(); 64 | let mut roots = Vec::new(); 65 | backtrack_hole_list_sub(core, &mut seen, &mut holes, &mut roots, pair); 66 | 67 | // For type escape errors, only consider holes in outer scopes 68 | holes.retain(|v| v.scopelvl <= self.1); 69 | // println!("{:?} found {} holes {:?}", pair, holes.len(), holes); 70 | 71 | let n = holes.len(); 72 | let best = holes 73 | .into_iter() 74 | .enumerate() 75 | .max_by_key(|&(i, v)| v.src.priority() + i * (n - i)); 76 | 77 | if let Some(hole) = best { 78 | self.0.push_str( 79 | "Hint: To narrow down the cause of the type mismatch, consider adding an explicit type annotation here:", 80 | ); 81 | 82 | use HoleSrc::*; 83 | match hole.1.src { 84 | Explicit(span) => self.0.push_span(span), 85 | OptAscribe(span) => self.0.push_insert("", span, ": _"), 86 | Instantiation((span, kind), name) => { 87 | let name = strings.resolve(&name); 88 | 89 | use InstantiateSourceKind::*; 90 | let s = match kind { 91 | ImplicitCall => format!("[{}=_]", name), 92 | ImplicitRecord => format!(" type {}=_; ", name), 93 | ExplicitParams(is_empty) => format!("{}{}=_", if is_empty { "" } else { "; " }, name), 94 | }; 95 | 96 | self.0.push_insert("", span, s); 97 | } 98 | CheckedExpr(span) | BareVarPattern(span) => self.0.push_insert("(", span, ": _)"), 99 | } 100 | } else { 101 | // If there were no type inference variables we could hint for, try flow roots instead 102 | // println!("roots {:?}", roots); 103 | // println!("spans {:?}", self.2); 104 | 105 | for span in roots { 106 | if !self.2.contains(&span) { 107 | self.0 108 | .push_str("Note: Type mismatch was detected starting from this expression:"); 109 | self.0.push_span(span); 110 | break; 111 | } 112 | } 113 | } 114 | } 115 | } 116 | 117 | enum TMsg { 118 | BeA(String), 119 | HaveTy(String, Option), 120 | } 121 | impl TMsg { 122 | fn print(&self, show_ctors: bool) -> String { 123 | match self { 124 | TMsg::BeA(s) => format!("be a {}", s), 125 | TMsg::HaveTy(s, c) => { 126 | if show_ctors && c.is_none() { 127 | format!("have builtin type {}", s) 128 | } else { 129 | format!("have type {}", s) 130 | } 131 | } 132 | } 133 | } 134 | } 135 | 136 | fn be_a(s: &str) -> TMsg { 137 | TMsg::BeA(s.to_owned()) 138 | } 139 | 140 | pub fn type_mismatch_err( 141 | strings: &mut lasso::Rodeo, 142 | type_ctors: &[TypeCtor], 143 | lhs: &VTypeNode, 144 | rhs: &UTypeNode, 145 | ) -> PartialTypeError { 146 | use TMsg::*; 147 | use UTypeHead::*; 148 | use VTypeHead::*; 149 | 150 | let found = match lhs.0 { 151 | VUnion(_) => unreachable!(), 152 | VInstantiateExist { .. } => unreachable!(), 153 | VPolyHead(..) => unreachable!(), 154 | VTop => HaveTy("any".to_owned(), None), 155 | VFunc { .. } => be_a("function"), 156 | VObj { .. } => be_a("record"), 157 | VCase { .. } => be_a("variant"), 158 | VAbstract { ty, .. } => { 159 | let tycon = &type_ctors[ty.0]; 160 | let name = strings.resolve(&tycon.name); 161 | HaveTy(name.to_owned(), tycon.span) 162 | } 163 | // VAbstract { ty, .. } => &type_ctors[ty.0].debug, 164 | VTypeVar(tv) => BeA(format!("type parameter {}", strings.resolve(&tv.name))), 165 | VDisjointIntersect(..) => be_a("intersection"), 166 | }; 167 | 168 | let expected = match rhs.0 { 169 | UIntersection(_) => unreachable!(), 170 | UInstantiateUni { .. } => unreachable!(), 171 | UPolyHead(..) => unreachable!(), 172 | UBot => HaveTy("never".to_owned(), None), 173 | UFunc { .. } => be_a("function"), 174 | UObj { .. } => be_a("record"), 175 | UCase { .. } => be_a("variant"), 176 | UAbstract { ty, .. } => { 177 | let tycon = &type_ctors[ty.0]; 178 | let name = strings.resolve(&tycon.name); 179 | HaveTy(name.to_owned(), tycon.span) 180 | } 181 | // VAbstract { ty, .. } => &type_ctors[ty.0].debug, 182 | UTypeVar(tv) => BeA(format!("type parameter {}", strings.resolve(&tv.name))), 183 | UDisjointUnion(..) => be_a("union"), 184 | }; 185 | 186 | let show_ctors = match (&found, &expected) { 187 | (HaveTy(s, _), HaveTy(s2, _)) if s == s2 => true, 188 | _ => false, 189 | }; 190 | 191 | let mut parts = PartialTypeError::new(); 192 | parts.push( 193 | format!("TypeError: Value is required to {} here:", expected.print(show_ctors)), 194 | rhs.1, 195 | ); 196 | match expected { 197 | HaveTy(s, Some(span)) if show_ctors => { 198 | parts.push(format!("Where {} is the abstract type defined here:", s), span); 199 | } 200 | _ => {} 201 | } 202 | 203 | parts.push( 204 | format!("However, that value may {} originating here:", found.print(show_ctors)), 205 | lhs.1, 206 | ); 207 | match found { 208 | HaveTy(s, Some(span)) if show_ctors => { 209 | parts.push(format!("Where {} is the abstract type defined here:", s), span); 210 | } 211 | _ => {} 212 | } 213 | 214 | parts 215 | } 216 | 217 | pub fn unhandled_variant_err(lhs: &VTypeNode, rhs: &UTypeNode, name: &str) -> PartialTypeError { 218 | let mut parts = PartialTypeError::new(); 219 | parts.push( 220 | format!("TypeError: Unhandled variant {}\nNote: Value originates here:", name), 221 | lhs.1, 222 | ); 223 | parts.push(format!("But it is not handled here:"), rhs.1); 224 | parts 225 | } 226 | 227 | pub fn missing_field_err(lhs_span: Span, rhs_span: Span, name: &str) -> PartialTypeError { 228 | let mut parts = PartialTypeError::new(); 229 | parts.push( 230 | format!("TypeError: Missing field {}\nNote: Field {} is accessed here:", name, name), 231 | rhs_span, 232 | ); 233 | parts.push(format!("But the record is defined without that field here:"), lhs_span); 234 | parts 235 | } 236 | 237 | pub fn immutable_field_err(lhs_span: Span, rhs_span: Span, name: &str) -> PartialTypeError { 238 | let mut parts = PartialTypeError::new(); 239 | parts.push( 240 | format!( 241 | "TypeError: Can't set immutable field {}.\nNote: Field is required to be mutable here:", 242 | name 243 | ), 244 | rhs_span, 245 | ); 246 | parts.push(format!("But the record is defined with that field immutable here:"), lhs_span); 247 | parts 248 | } 249 | 250 | pub fn type_escape_error( 251 | strings: &mut lasso::Rodeo, 252 | ty_ctor: &TypeCtor, 253 | lhs: &VTypeNode, 254 | rhs: &UTypeNode, 255 | scopelvl: ScopeLvl, 256 | ) -> PartialTypeError { 257 | let mut parts = PartialTypeError::new(); 258 | parts.1 = scopelvl; 259 | 260 | parts.push( 261 | format!( 262 | "TypeError: Type {} defined here escapes its scope", 263 | strings.resolve(&ty_ctor.name), 264 | ), 265 | ty_ctor.span.unwrap(), 266 | ); 267 | parts.push(format!("Note: A value of this type originates here:"), lhs.1); 268 | parts.push(format!("and is consumed here after escaping the defining scope:"), rhs.1); 269 | parts 270 | } 271 | 272 | pub fn poisoned_poly_err(span: Span) -> PartialTypeError { 273 | let mut parts = PartialTypeError::new(); 274 | parts 275 | .0 276 | .push_str("TypeError: Repeated instantiation of nested polymorphic type requires intervening type annotation:"); 277 | parts.0.push_insert("(", span, ": )"); 278 | parts 279 | } 280 | 281 | fn backtrack_hole_list_sub( 282 | core: &TypeCheckerCore, 283 | seen: &mut HashSet<(Value, Use)>, 284 | holes_list: &mut Vec, 285 | roots_list: &mut Vec, 286 | mut pair: (Value, Use), 287 | ) { 288 | while !seen.contains(&pair) { 289 | // println!("checking {} {}", pair.0.0.0, pair.1.0.0); 290 | seen.insert(pair); 291 | let reason = core.r.get_edge(pair.0.0, pair.1.0).unwrap().reason; 292 | // println!("reason {:?}", reason); 293 | match reason { 294 | FlowReason::Root(span) => { 295 | roots_list.push(span); 296 | break; 297 | } 298 | FlowReason::Transitivity(h) => { 299 | backtrack_hole_list_sub(core, seen, holes_list, roots_list, (pair.0, Use(h))); 300 | 301 | match core.r.get(h) { 302 | Some(&TypeNode::Var(data)) => holes_list.push(data), 303 | _ => unreachable!(), 304 | } 305 | 306 | pair = (Value(h), pair.1); 307 | } 308 | FlowReason::Check(v, u) => pair = (v, u), 309 | } 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /src/typeck.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::cell::RefCell; 3 | use std::collections::HashMap; 4 | use std::collections::HashSet; 5 | use std::error; 6 | use std::fmt; 7 | use std::hash::Hash; 8 | use std::rc::Rc; 9 | 10 | use crate::ast; 11 | use crate::ast::StringId; 12 | use crate::core::*; 13 | use crate::parse_types::TreeMaterializer; 14 | use crate::parse_types::TreeMaterializerState; 15 | use crate::parse_types::TypeParser; 16 | use crate::spans::Span; 17 | use crate::spans::SpannedError as SyntaxError; 18 | use crate::type_errors::HoleSrc; 19 | use crate::unwindmap::UnwindMap; 20 | use crate::unwindmap::UnwindPoint; 21 | 22 | use UTypeHead::*; 23 | use VTypeHead::*; 24 | 25 | type Result = std::result::Result; 26 | 27 | type BindingsUnwindPoint = (UnwindPoint, UnwindPoint, ScopeLvl); 28 | pub struct Bindings { 29 | pub vars: UnwindMap, 30 | pub types: UnwindMap, 31 | pub scopelvl: ScopeLvl, 32 | } 33 | impl Bindings { 34 | fn new() -> Self { 35 | Self { 36 | vars: UnwindMap::new(), 37 | types: UnwindMap::new(), 38 | scopelvl: ScopeLvl(0), 39 | } 40 | } 41 | 42 | fn unwind_point(&mut self) -> BindingsUnwindPoint { 43 | (self.vars.unwind_point(), self.types.unwind_point(), self.scopelvl) 44 | } 45 | 46 | fn unwind(&mut self, n: BindingsUnwindPoint) { 47 | self.vars.unwind(n.0); 48 | self.types.unwind(n.1); 49 | self.scopelvl = n.2; 50 | } 51 | 52 | fn make_permanent(&mut self, n: BindingsUnwindPoint) { 53 | self.vars.make_permanent(n.0); 54 | self.types.make_permanent(n.1); 55 | } 56 | } 57 | 58 | #[allow(non_snake_case)] 59 | pub struct TypeckState { 60 | core: TypeCheckerCore, 61 | bindings: Bindings, 62 | 63 | TY_BOOL: TypeCtorInd, 64 | TY_FLOAT: TypeCtorInd, 65 | TY_INT: TypeCtorInd, 66 | TY_STR: TypeCtorInd, 67 | } 68 | impl TypeckState { 69 | #[allow(non_snake_case)] 70 | pub fn new(strings: &mut lasso::Rodeo) -> Self { 71 | let mut core = TypeCheckerCore::new(); 72 | let TY_BOOL = core.add_builtin_type(strings.get_or_intern_static("bool")); 73 | let TY_FLOAT = core.add_builtin_type(strings.get_or_intern_static("float")); 74 | let TY_INT = core.add_builtin_type(strings.get_or_intern_static("int")); 75 | let TY_STR = core.add_builtin_type(strings.get_or_intern_static("str")); 76 | 77 | let mut new = Self { 78 | core, 79 | bindings: Bindings::new(), 80 | 81 | TY_BOOL, 82 | TY_FLOAT, 83 | TY_INT, 84 | TY_STR, 85 | }; 86 | 87 | let n = new.bindings.unwind_point(); 88 | for (i, ty) in new.core.type_ctors.iter().enumerate() { 89 | new.bindings.types.insert(ty.name, TypeCtorInd(i)); 90 | } 91 | new.bindings.make_permanent(n); 92 | 93 | new 94 | } 95 | 96 | fn parse_type_signature(&mut self, tyexpr: &ast::STypeExpr) -> Result<(Value, Use)> { 97 | let temp = TypeParser::new(&self.bindings.types).parse_type(tyexpr)?; 98 | let mut mat = TreeMaterializerState::new(self.bindings.scopelvl); 99 | Ok(mat.with(&mut self.core).add_type(temp)) 100 | } 101 | 102 | fn process_let_pattern(&mut self, pat: &ast::LetPattern, no_typed_var_allowed: bool) -> Result { 103 | let temp = TypeParser::new(&self.bindings.types).parse_let_pattern(pat, no_typed_var_allowed)?; 104 | let mut mat = TreeMaterializerState::new(self.bindings.scopelvl); 105 | Ok(mat.with(&mut self.core).add_pattern(temp, &mut self.bindings)) 106 | } 107 | 108 | fn check_expr(&mut self, strings: &mut lasso::Rodeo, expr: &ast::Expr, bound: Use) -> Result<()> { 109 | use ast::Expr::*; 110 | match expr { 111 | Block(statements, rest_expr) => { 112 | assert!(statements.len() >= 1); 113 | let mark = self.bindings.unwind_point(); 114 | 115 | for stmt in statements.iter() { 116 | self.check_statement(strings, stmt, false)?; 117 | } 118 | 119 | self.check_expr(strings, rest_expr, bound)?; 120 | self.bindings.unwind(mark); 121 | } 122 | Call(func_expr, arg_expr, span) => { 123 | let arg_type = self.infer_expr(strings, arg_expr)?; 124 | 125 | let bound = self.core.new_use( 126 | UFunc { 127 | arg: arg_type, 128 | ret: bound, 129 | }, 130 | *span, 131 | None, 132 | ); 133 | self.check_expr(strings, func_expr, bound)?; 134 | } 135 | &FieldAccess(ref lhs_expr, (name, field_span), full_span) => { 136 | let bound = self.core.obj_use(vec![(name, (bound, None, field_span))], field_span); 137 | self.check_expr(strings, lhs_expr, bound)?; 138 | } 139 | &FieldSet(ref lhs_expr, (name, name_span), ref rhs_expr, full_span) => { 140 | let rhs_type = self.infer_expr(strings, rhs_expr)?; 141 | let bound = self.core.obj_use(vec![(name, (bound, Some(rhs_type), name_span))], name_span); 142 | self.check_expr(strings, lhs_expr, bound)?; 143 | } 144 | If((cond_expr, span), then_expr, else_expr) => { 145 | let bool_use = self.core.simple_use(self.TY_BOOL, *span); 146 | self.check_expr(strings, cond_expr, bool_use)?; 147 | self.check_expr(strings, then_expr, bound)?; 148 | self.check_expr(strings, else_expr, bound)?; 149 | } 150 | &InstantiateUni((ref expr, lhs_span), (ref sigs, sigs_span), src_kind, full_span) => { 151 | let mut params = HashMap::new(); 152 | for &(name, ref sig) in sigs { 153 | params.insert(name, self.parse_type_signature(sig)?); 154 | } 155 | let bound = self.core.new_use( 156 | UInstantiateUni { 157 | params: Rc::new(RefCell::new(params)), 158 | target: bound, 159 | src_template: (sigs_span, src_kind), 160 | }, 161 | lhs_span, 162 | None, 163 | ); 164 | self.check_expr(strings, expr, bound)?; 165 | } 166 | &Loop(ref expr, full_span) => { 167 | let bound = self.core.case_use( 168 | vec![ 169 | (strings.get_or_intern_static("Break"), bound), 170 | (strings.get_or_intern_static("Continue"), self.core.top_use()), 171 | ], 172 | None, 173 | full_span, 174 | ); 175 | self.check_expr(strings, expr, bound)?; 176 | } 177 | &Match((ref match_expr, arg_span), ref cases, full_span) => { 178 | // Bounds from the match arms 179 | let mut case_type_pairs = Vec::with_capacity(cases.len()); 180 | let mut wildcard_type = None; 181 | 182 | // Pattern reachability checking 183 | let mut case_names = HashMap::with_capacity(cases.len()); 184 | let mut wildcard = None; 185 | 186 | for ((pattern, pattern_span), rhs_expr) in cases { 187 | use ast::LetPattern::*; 188 | match pattern { 189 | Case((tag, _), val_pat) => { 190 | if let Some(old_span) = case_names.insert(&*tag, *pattern_span) { 191 | return Err(SyntaxError::new2( 192 | "SyntaxError: Duplicate match pattern", 193 | *pattern_span, 194 | "Note: Variant already matched here:", 195 | old_span, 196 | )); 197 | } 198 | 199 | let mark = self.bindings.unwind_point(); 200 | let pattern_bound = self.process_let_pattern(val_pat, true)?; 201 | // Note: bound is bound for the result types, not the pattern 202 | self.check_expr(strings, rhs_expr, bound)?; 203 | case_type_pairs.push((*tag, pattern_bound)); 204 | self.bindings.unwind(mark); 205 | } 206 | Record(..) => { 207 | return Err(SyntaxError::new1( 208 | "SyntaxError: Invalid wildcard match pattern", 209 | *pattern_span, 210 | )); 211 | } 212 | // Wildcard case - only Var patterns will actually work here. 213 | // Any other pattern will result in a type error. 214 | Var(..) => { 215 | if let Some(old_span) = wildcard { 216 | return Err(SyntaxError::new2( 217 | "SyntaxError: Duplicate match pattern", 218 | *pattern_span, 219 | "Note: Wildcard already matched here:", 220 | old_span, 221 | )); 222 | } 223 | 224 | wildcard = Some(*pattern_span); 225 | 226 | let mark = self.bindings.unwind_point(); 227 | let pattern_bound = self.process_let_pattern(pattern, true)?; 228 | // Note: bound is bound for the result types, not the pattern 229 | self.check_expr(strings, rhs_expr, bound)?; 230 | wildcard_type = Some(pattern_bound); 231 | self.bindings.unwind(mark); 232 | } 233 | } 234 | } 235 | 236 | let bound = self.core.case_use(case_type_pairs, wildcard_type, arg_span); 237 | self.check_expr(strings, match_expr, bound)?; 238 | } 239 | 240 | // Cases that should be inferred instead 241 | BinOp(_, _, _, _, _, _, span) 242 | | Case((_, span), _) 243 | | FuncDef((_, span)) 244 | | Literal(_, (_, span)) 245 | | InstantiateExist(_, _, _, span) 246 | | Record(_, span) 247 | | Typed(_, (_, span)) 248 | | Variable((_, span)) => { 249 | // Span is just an arbitrary span (usually that of the current expression) used 250 | // to help users diagnose cause of a type error that doesn't go through any holes. 251 | let t = self.infer_expr(strings, expr)?; 252 | self.core.flow(strings, t, bound, *span, self.bindings.scopelvl)?; 253 | } 254 | }; 255 | Ok(()) 256 | } 257 | 258 | fn infer_expr(&mut self, strings: &mut lasso::Rodeo, expr: &ast::Expr) -> Result { 259 | use ast::Expr::*; 260 | 261 | match expr { 262 | BinOp(lhs_expr, lhs_span, rhs_expr, rhs_span, op_type, op, full_span) => { 263 | use ast::Literal::*; 264 | let (arg_class, ret_class) = op_type; 265 | let (lhs_bound, rhs_bound) = match arg_class { 266 | Some(arg_class) => { 267 | let cls = match arg_class { 268 | Bool => self.TY_BOOL, 269 | Float => self.TY_FLOAT, 270 | Int => self.TY_INT, 271 | Str => self.TY_STR, 272 | }; 273 | 274 | (self.core.simple_use(cls, *lhs_span), self.core.simple_use(cls, *rhs_span)) 275 | } 276 | None => (self.core.top_use(), self.core.top_use()), 277 | }; 278 | self.check_expr(strings, lhs_expr, lhs_bound)?; 279 | self.check_expr(strings, rhs_expr, rhs_bound)?; 280 | 281 | let cls = match ret_class { 282 | Bool => self.TY_BOOL, 283 | Float => self.TY_FLOAT, 284 | Int => self.TY_INT, 285 | Str => self.TY_STR, 286 | }; 287 | Ok(self.core.simple_val(cls, *full_span)) 288 | } 289 | // Allow block expressions to be inferred as well as checked 290 | // TODO - deduplicate this code 291 | Block(statements, rest_expr) => { 292 | assert!(statements.len() >= 1); 293 | let mark = self.bindings.unwind_point(); 294 | 295 | for stmt in statements.iter() { 296 | self.check_statement(strings, stmt, false)?; 297 | } 298 | 299 | let res = self.infer_expr(strings, rest_expr)?; 300 | self.bindings.unwind(mark); 301 | Ok(res) 302 | } 303 | Case((tag, span), val_expr) => { 304 | let val_type = self.infer_expr(strings, val_expr)?; 305 | Ok(self.core.new_val(VCase { case: (*tag, val_type) }, *span, None)) 306 | } 307 | FuncDef(((ty_params, arg_pattern, ret_tyexpr, body_expr), span)) => { 308 | let parsed = TypeParser::new(&self.bindings.types).parse_func_sig( 309 | ty_params, 310 | arg_pattern, 311 | ret_tyexpr.as_ref(), 312 | *span, 313 | )?; 314 | 315 | let mark = self.bindings.unwind_point(); 316 | let mut mat = TreeMaterializerState::new(self.bindings.scopelvl); 317 | let mut mat = mat.with(&mut self.core); 318 | let func_type = mat.add_func_type(&parsed); 319 | let ret_bound = mat.add_func_sig(parsed, &mut self.bindings); 320 | 321 | self.check_expr(strings, body_expr, ret_bound)?; 322 | 323 | self.bindings.unwind(mark); 324 | Ok(func_type) 325 | } 326 | // Allow if expressions to be inferred as well as checked 327 | // TODO - deduplicate this code 328 | If((cond_expr, span), then_expr, else_expr) => { 329 | let bool_use = self.core.simple_use(self.TY_BOOL, *span); 330 | self.check_expr(strings, cond_expr, bool_use)?; 331 | let res1 = self.infer_expr(strings, then_expr)?; 332 | let res2 = self.infer_expr(strings, else_expr)?; 333 | if res1 == res2 { 334 | Ok(res1) 335 | } else { 336 | // spans for Union nodes don't matter, so just use whatever is handy 337 | Ok(self.core.new_val(VUnion(vec![res1, res2]), *span, None)) 338 | } 339 | } 340 | &InstantiateExist(ref expr, (ref sigs, sigs_span), src_kind, full_span) => { 341 | let mut params = HashMap::new(); 342 | for &(name, ref sig) in sigs { 343 | params.insert(name, self.parse_type_signature(sig)?); 344 | } 345 | 346 | let target = self.infer_expr(strings, expr)?; 347 | Ok(self.core.new_val( 348 | VInstantiateExist { 349 | params: Rc::new(RefCell::new(params)), 350 | target, 351 | src_template: (sigs_span, src_kind), 352 | }, 353 | full_span, 354 | None, 355 | )) 356 | } 357 | Literal(type_, (code, span)) => { 358 | use ast::Literal::*; 359 | let span = *span; 360 | 361 | let ty = match type_ { 362 | Bool => self.TY_BOOL, 363 | Float => self.TY_FLOAT, 364 | Int => self.TY_INT, 365 | Str => self.TY_STR, 366 | }; 367 | Ok(self.core.simple_val(ty, span)) 368 | } 369 | Record(fields, span) => { 370 | let mut field_names = HashMap::with_capacity(fields.len()); 371 | let mut field_type_pairs = Vec::with_capacity(fields.len()); 372 | for ((name, name_span), expr, mutable, type_annot) in fields { 373 | if let Some(old_span) = field_names.insert(&*name, *name_span) { 374 | return Err(SyntaxError::new2( 375 | "SyntaxError: Repeated field name", 376 | *name_span, 377 | "Note: Field was already defined here", 378 | old_span, 379 | )); 380 | } 381 | 382 | if *mutable { 383 | let temp = 384 | TypeParser::new(&self.bindings.types).parse_type_or_hole(type_annot.as_ref(), *name_span)?; 385 | let mut mat = TreeMaterializerState::new(self.bindings.scopelvl); 386 | let (v, u) = mat.with(&mut self.core).add_type(temp); 387 | 388 | self.check_expr(strings, expr, u)?; 389 | field_type_pairs.push((*name, (v, Some(u), *name_span))); 390 | } else { 391 | // For immutable fields, use the type annotation if one was supplied 392 | // but do not create a hole (inference variable) if there wasn't, 393 | let t = if let Some(ty) = type_annot { 394 | let (v, u) = self.parse_type_signature(ty)?; 395 | self.check_expr(strings, expr, u)?; 396 | v 397 | } else { 398 | self.infer_expr(strings, expr)? 399 | }; 400 | 401 | field_type_pairs.push((*name, (t, None, *name_span))); 402 | } 403 | } 404 | let fields = field_type_pairs.into_iter().collect(); 405 | Ok(self.core.new_val(VTypeHead::VObj { fields }, *span, None)) 406 | } 407 | Typed(expr, sig) => { 408 | let sig_type = self.parse_type_signature(sig)?; 409 | self.check_expr(strings, expr, sig_type.1)?; 410 | Ok(sig_type.0) 411 | } 412 | Variable((name, span)) => { 413 | if let Some(v) = self.bindings.vars.get(name) { 414 | Ok(*v) 415 | } else { 416 | Err(SyntaxError::new1(format!("SyntaxError: Undefined variable"), *span)) 417 | } 418 | } 419 | 420 | // Cases that have to be checked instead 421 | Call(_, _, span) 422 | | FieldAccess(_, _, span) 423 | | FieldSet(_, _, _, span) 424 | | Loop(_, span) 425 | | InstantiateUni(_, _, _, span) 426 | | Match(_, _, span) => { 427 | let (v, u) = self.core.var(HoleSrc::CheckedExpr(*span), self.bindings.scopelvl); 428 | self.check_expr(strings, expr, u)?; 429 | Ok(v) 430 | } 431 | } 432 | } 433 | 434 | fn check_let_def(&mut self, strings: &mut lasso::Rodeo, lhs: &ast::LetPattern, expr: &ast::Expr) -> Result<()> { 435 | // Check if left hand side is a simple assignment with no type annotation 436 | if let &ast::LetPattern::Var((Some(name), _), None) = lhs { 437 | // If lefthand side is a simple assignment, avoid adding an inference var 438 | // (and hence the possibility of prompting the user to add a type annotation) 439 | // when the type is "obvious" or redundant from the right hand side. 440 | // For FuncDef, type annotations should be added on the function definition, 441 | // so don't prompt for redundant annotations on the assignment. 442 | use ast::Expr::*; 443 | match expr { 444 | FuncDef(..) | Literal(..) | Typed(..) | Variable(..) => { 445 | let ty = self.infer_expr(strings, expr)?; 446 | self.bindings.vars.insert(name, ty); 447 | return Ok(()); 448 | } 449 | _ => {} 450 | }; 451 | } 452 | 453 | let parsed = TypeParser::new(&self.bindings.types).parse_let_pattern(lhs, false)?; 454 | let mut mat = TreeMaterializerState::new(self.bindings.scopelvl); 455 | 456 | // Important: The RHS of a let needs to be evaluated *before* we add the bindings from the LHS 457 | // However, we need to compute the bound (use type) of the lhs pattern so that we can check 458 | // the rhs against it. Therefore, materializing the pattern is split into two calls. 459 | // The first merely returns the bound while the second below actually adds the pattern bindings. 460 | let bound = mat.with(&mut self.core).add_pattern_bound(&parsed); 461 | self.check_expr(strings, expr, bound)?; 462 | 463 | // Now add the pattern bindings 464 | mat.with(&mut self.core).add_pattern(parsed, &mut self.bindings); 465 | Ok(()) 466 | } 467 | 468 | fn check_let_rec_defs(&mut self, strings: &mut lasso::Rodeo, defs: &Vec) -> Result<()> { 469 | // Important: Must use the same materializer state when materializing the outer and inner function types 470 | let mut mat = TreeMaterializerState::new(self.bindings.scopelvl); 471 | 472 | let mut temp = Vec::new(); 473 | // Parse the function signatures 474 | // Materialize the outer function types and assign to bindings 475 | for &(name, (ref expr, span)) in defs.iter() { 476 | match expr { 477 | ast::Expr::FuncDef(((ty_params, arg_pattern, ret_tyexpr, body_expr), span)) => { 478 | let parsed = TypeParser::new(&self.bindings.types).parse_func_sig( 479 | ty_params, 480 | arg_pattern, 481 | ret_tyexpr.as_ref(), 482 | *span, 483 | )?; 484 | 485 | self.bindings 486 | .vars 487 | .insert(name, mat.with(&mut self.core).add_func_type(&parsed)); 488 | temp.push((parsed, body_expr)); 489 | } 490 | _ => { 491 | return Err(SyntaxError::new1( 492 | format!("SyntaxError: Let rec can only assign function definitions."), 493 | span, 494 | )); 495 | } 496 | } 497 | } 498 | 499 | // Now process the body of each function definition one by one 500 | for (parsed, body) in temp { 501 | let mark = self.bindings.unwind_point(); 502 | 503 | let ret_bound = mat.with(&mut self.core).add_func_sig(parsed, &mut self.bindings); 504 | self.check_expr(strings, body, ret_bound)?; 505 | 506 | self.bindings.unwind(mark); 507 | } 508 | 509 | Ok(()) 510 | } 511 | 512 | fn check_statement( 513 | &mut self, 514 | strings: &mut lasso::Rodeo, 515 | def: &ast::Statement, 516 | allow_useless_exprs: bool, 517 | ) -> Result<()> { 518 | use ast::Statement::*; 519 | match def { 520 | Empty => {} 521 | Expr(expr) => { 522 | if !allow_useless_exprs { 523 | use ast::Expr::*; 524 | match expr { 525 | BinOp(_, _, _, _, _, _, span) 526 | | Case((_, span), _) 527 | | FieldAccess(_, _, span) 528 | | FuncDef((_, span)) 529 | | InstantiateExist(_, _, _, span) 530 | | InstantiateUni(_, _, _, span) 531 | | Literal(_, (_, span)) 532 | | Record(_, span) 533 | | Variable((_, span)) => { 534 | return Err(SyntaxError::new1( 535 | format!( 536 | "SyntaxError: Only block, call, field set, if, loop, match, and typed expressions can appear in a sequence. The value of this expression will be ignored, which is likely unintentional. If you did intend to ignore the value of this expression, do so explicitly via let _ = ..." 537 | ), 538 | *span, 539 | )); 540 | } 541 | _ => {} 542 | }; 543 | } 544 | 545 | self.check_expr(strings, expr, self.core.top_use())?; 546 | } 547 | LetDef((pattern, var_expr)) => { 548 | self.check_let_def(strings, pattern, var_expr)?; 549 | } 550 | LetRecDef(defs) => { 551 | self.check_let_rec_defs(strings, defs)?; 552 | } 553 | Println(exprs) => { 554 | for expr in exprs { 555 | self.check_expr(strings, expr, self.core.top_use())?; 556 | } 557 | } 558 | }; 559 | Ok(()) 560 | } 561 | 562 | pub fn check_script(&mut self, strings: &mut lasso::Rodeo, parsed: &[ast::Statement]) -> Result<()> { 563 | // Tell type checker to start keeping track of changes to the type state so we can roll 564 | // back all the changes if the script contains an error. 565 | self.core.save(); 566 | let mark = self.bindings.unwind_point(); 567 | 568 | let len = parsed.len(); 569 | for (i, item) in parsed.iter().enumerate() { 570 | let is_last = i == len - 1; 571 | if let Err(e) = self.check_statement(strings, item, is_last) { 572 | // println!("num type nodes {}", self.core.num_type_nodes()); 573 | 574 | // Roll back changes to the type state and bindings 575 | self.core.revert(); 576 | self.bindings.unwind(mark); 577 | return Err(e); 578 | } 579 | } 580 | 581 | // Now that script type-checked successfully, make the global definitions permanent 582 | // by removing them from the changes rollback list 583 | self.core.make_permanent(); 584 | self.bindings.make_permanent(mark); 585 | // println!("num type nodes {}", self.core.num_type_nodes()); 586 | // println!("{} vars {} flows", self.core.varcount, self.core.flowcount); 587 | Ok(()) 588 | } 589 | } 590 | -------------------------------------------------------------------------------- /src/unwindmap.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::hash::Hash; 3 | 4 | // utility functions 5 | pub fn sorted(it: impl IntoIterator) -> Vec { 6 | let mut v = it.into_iter().collect::>(); 7 | v.sort_unstable(); 8 | v 9 | } 10 | 11 | pub struct UnwindPoint(usize); 12 | pub struct UnwindMap { 13 | m: HashMap, 14 | changes: Vec<(K, Option)>, 15 | } 16 | impl UnwindMap { 17 | pub fn new() -> Self { 18 | Self { 19 | m: HashMap::new(), 20 | changes: Vec::new(), 21 | } 22 | } 23 | 24 | pub fn get(&self, k: &K) -> Option<&V> { 25 | self.m.get(k) 26 | } 27 | 28 | pub fn insert(&mut self, k: K, v: V) { 29 | let old = self.m.insert(k.clone(), v); 30 | self.changes.push((k, old)); 31 | } 32 | 33 | pub fn unwind_point(&mut self) -> UnwindPoint { 34 | UnwindPoint(self.changes.len()) 35 | } 36 | 37 | pub fn unwind(&mut self, n: UnwindPoint) { 38 | let n = n.0; 39 | assert!(n <= self.changes.len()); 40 | while self.changes.len() > n { 41 | let (k, old) = self.changes.pop().unwrap(); 42 | match old { 43 | Some(v) => self.m.insert(k, v), 44 | None => self.m.remove(&k), 45 | }; 46 | } 47 | } 48 | 49 | pub fn make_permanent(&mut self, n: UnwindPoint) { 50 | assert!(n.0 == 0); 51 | self.changes.clear(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | pub fn set_panic_hook() { 3 | // When the `console_error_panic_hook` feature is enabled, we can call the 4 | // `set_panic_hook` function at least once during initialization, and then 5 | // we will get better error messages if our code ever panics. 6 | // 7 | // For more details see 8 | // https://github.com/rustwasm/console_error_panic_hook#readme 9 | #[cfg(feature = "console_error_panic_hook")] 10 | console_error_panic_hook::set_once(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/combined.ml: -------------------------------------------------------------------------------- 1 | ### Good 2 | let f = fun x -> x[]; 3 | 4 | // Test recovery from bad universal instantiation 5 | ### Bad 6 | let _ = 1 + (f (fun (type t) (x: t) : t -> x)) 1; 7 | let _ = x + 4; 8 | 9 | ### Good 10 | let _ = 1.0 +. (f (fun (type t) (x: t) : t -> x)) 1.0; 11 | 12 | let r = {a=1; b=fun x->x+1; c=1.2; d=fun x->x+.2.1}; 13 | 14 | // Test recovery from bad existential instantiation 15 | ### Bad 16 | let {type t; a: t; b: t->t} = r; 17 | let _ = 4 +. 3; 18 | 19 | ### Good 20 | 1+2 21 | 22 | ### Good 23 | let {type t; c: t; d: t->t} = r; 24 | 25 | let _ = fun (type t) (x: t, f: type u. t * u->int * u) : int * t -> ( 26 | let (a, b) = f (x, 23); 27 | let (c, d) = f (x, {x=a+b}); 28 | let _ = c + d.x; 29 | 30 | f (x, x) 31 | ); 32 | 33 | let rec fizzbuzz = fun i -> ( 34 | print (if i % 3 == 0 then 35 | if i % 5 == 0 then 36 | "FizzBuzz" 37 | else 38 | "Fizz" 39 | else 40 | if i % 5 == 0 then 41 | "Buzz" 42 | else 43 | i 44 | ); 45 | if i < 50 then 46 | fizzbuzz (i+1) 47 | else 48 | 0 49 | ); 50 | fizzbuzz 0; 51 | 52 | let vars = {mut i=0}; 53 | loop if vars.i >= 50 then `Break 0 else ( 54 | let i = vars.i <- vars.i + 1; 55 | print (if i % 3 == 0 then 56 | if i % 5 == 0 then 57 | "FizzBuzz" 58 | else 59 | "Fizz" 60 | else 61 | if i % 5 == 0 then 62 | "Buzz" 63 | else 64 | i 65 | ); 66 | `Continue 0 67 | ); 68 | 69 | let x = ( 70 | let a = 4; 71 | let b = a + 3; 72 | let c = a * b; 73 | let d = c / b; 74 | d + a 75 | ); 76 | 77 | let x = ( 78 | print "test", 1+2, 3 * 9, 2.0 /. 0.0; 79 | 42 80 | ); 81 | 82 | let (a: int, b: str, c, d: float) = 1, "", "", 3.2; 83 | 84 | let {a; b: int; c=x: int; d={e: float}} = {a=1; b=2; c=3; d={e=4.4}}; 85 | 86 | match `Foo {x=32} with 87 | | `Foo {x: int} -> x; 88 | 89 | let add = fun {a: int; b: int} -> a + b; 90 | match `Foo 32 with 91 | | `Foo (x: int) -> x; 92 | 93 | let add_one = fun (x: int) -> x + 1; 94 | let a = 42; 95 | let b = -9.7; 96 | 97 | let r = { 98 | a: int; 99 | x: int = a; 100 | mut b: float; 101 | mut y: float = b 102 | }; 103 | 104 | let add_curried = fun (a: int) : (int -> int) -> 105 | fun (b: int) : int -> a + b; 106 | 107 | print (add_curried 4) 22; // 26; 108 | 109 | (`Foo 3: [`Foo of int | `Bar float]); 110 | 111 | 112 | let {type t; a: t; b: t->t} = {a=3; b=fun x->x+1}; 113 | let ref = loop `Break {mut v=`None 0}; 114 | 115 | ref.v <- `Some a; 116 | match ref.v with 117 | | `Some (a: t) -> b a 118 | | `None _ -> 0 119 | ; 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | let x = {mut v=`None 0; mut t=`None 0}; 131 | x.v <- `Some (x.v, {a=0; b=fun x->x+1}); 132 | x.v <- `Some (x.v, {a=0.2; b=fun x->x+.9.1}); 133 | x.v <- `Some (x.v, {a={q=1}; b=fun {q}->{q}}); 134 | 135 | loop match x.v with 136 | | `None _ -> `Break 0 137 | | `Some (t, h) -> ( 138 | x.v <- t; 139 | 140 | let {type t; a: t; b: t->t} = h; 141 | print (match x.t <- `Some a with 142 | | `None _ -> "missing" 143 | | `Some t -> t 144 | ); 145 | 146 | `Continue 0 147 | ); 148 | 149 | 150 | 151 | ### Bad 152 | let f = fun (type t) (x: t) : t -> x; 153 | let _ = 1 + f 3.2; 154 | 155 | ### Bad 156 | // Test for types escaping loops 157 | let x = {mut v=`None 0; mut t=`None 0}; 158 | x.v <- `Some (x.v, {a=0; b=fun x->x+1}); 159 | x.v <- `Some (x.v, {a=0.2; b=fun x->x+.9.1}); 160 | x.v <- `Some (x.v, {a={q=1}; b=fun {q}->{q}}); 161 | 162 | loop match x.v with 163 | | `None _ -> `Break 0 164 | | `Some (t, h) -> ( 165 | x.v <- t; 166 | 167 | let {type t; a: t; b: t->t} = h; 168 | print (match x.t <- `Some a with 169 | | `None _ -> "missing" 170 | | `Some t -> b t 171 | ); 172 | 173 | `Continue 0 174 | ); 175 | 176 | 177 | 178 | ### Bad 179 | let a = 3; 180 | let _ = {a: any}.a + 1; 181 | 182 | 183 | ### Bad 184 | // Error on unused <= expressions: 185 | let x = {a=4; mut b=6; c=9}; 186 | print x; // {a=4; b=6; c=9} 187 | x.b <= x.b + 11; 188 | print x; // {a=4; b=17; c=9} 189 | 190 | ### Bad 191 | let ref = {mut v=`None 0}; 192 | let {type t; a: t; b: t->t} = {a=3; b=fun x->x+1}; 193 | 194 | ref.v <- `Some a; 195 | match ref.v with 196 | | `Some a -> b a 197 | | `None _ -> 0 198 | ; 199 | 200 | ### Bad 201 | let {type t; a: t; b: t->t; ref: _} = {a=3; b=fun x->x+1; ref={mut v=`None 0}}; 202 | 203 | ref.v <- `Some a; 204 | match ref.v with 205 | | `Some a -> b a 206 | | `None _ -> 0 207 | ; 208 | 209 | 210 | ### Bad 211 | let ref = loop `Break {mut v=`None 0}; 212 | 213 | let {type t; a: t; b: t->t} = {a=3; b=fun x->x+1}; 214 | 215 | ref.v <- `Some a; 216 | match ref.v with 217 | | `Some (a: t) -> b a 218 | | `None _ -> 0 219 | ; 220 | --------------------------------------------------------------------------------