├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── comparison.md ├── examples ├── complex │ ├── conv.sis │ ├── dft2.sis │ └── smooth.sis ├── fibs.sis └── matmult.sis ├── overview.md ├── package.json ├── src ├── ast │ ├── composite.ts │ ├── definition.ts │ ├── expression.ts │ ├── function.ts │ ├── if.ts │ ├── index.ts │ ├── let.ts │ ├── literal.ts │ ├── loop.ts │ ├── node.ts │ ├── operand.ts │ └── types │ │ ├── array.ts │ │ ├── function.ts │ │ ├── index.ts │ │ ├── primitive.ts │ │ ├── record.ts │ │ ├── stream.ts │ │ └── value.ts ├── grammar │ └── sisal.pegjs ├── graphml │ └── index.ts ├── ir1 │ ├── create.ts │ ├── nodes │ │ ├── array.ts │ │ ├── arrayAccess.ts │ │ ├── binary.ts │ │ ├── function.ts │ │ ├── functionCall.ts │ │ ├── identifier.ts │ │ ├── if.ts │ │ ├── ifBranch.ts │ │ ├── index.ts │ │ ├── let.ts │ │ ├── literal.ts │ │ ├── loop.ts │ │ ├── node.ts │ │ ├── old.ts │ │ ├── ready.ts │ │ ├── record.ts │ │ ├── recordAccess.ts │ │ ├── stream.ts │ │ ├── types │ │ │ ├── array.ts │ │ │ ├── function.ts │ │ │ ├── index.ts │ │ │ ├── literal.ts │ │ │ ├── record.ts │ │ │ ├── some.ts │ │ │ └── stream.ts │ │ └── unary.ts │ ├── ports │ │ ├── port.ts │ │ ├── single.ts │ │ └── stream.ts │ ├── scopes │ │ ├── flat.ts │ │ ├── loop.ts │ │ └── scope.ts │ ├── types │ │ ├── array.ts │ │ ├── boolean.ts │ │ ├── check.ts │ │ ├── error.ts │ │ ├── float.ts │ │ ├── function.ts │ │ ├── index.ts │ │ ├── integer.ts │ │ ├── ready.ts │ │ ├── record.ts │ │ ├── some.ts │ │ ├── stream.ts │ │ ├── string.ts │ │ └── type.ts │ └── values │ │ ├── array.ts │ │ ├── boolean.ts │ │ ├── error.ts │ │ ├── fetch.ts │ │ ├── float.ts │ │ ├── function.ts │ │ ├── index.ts │ │ ├── integer.ts │ │ ├── ready.ts │ │ ├── record.ts │ │ ├── streamComplete.ts │ │ ├── streamElement.ts │ │ ├── streamEnd.ts │ │ ├── string.ts │ │ └── type.ts ├── parser.ts ├── print.ts ├── program.ts ├── sisal.ts ├── stdlib.ts └── stdlib │ ├── first.ts │ ├── last.ts │ ├── reductions.sis │ └── wrapper.ts ├── test ├── mocha.opts ├── programs │ ├── arith.sis │ ├── arith.sis.result │ ├── array.sis │ ├── array.sis.result │ ├── arrayAccess.sis │ ├── arrayAccess.sis.result │ ├── closure.sis │ ├── closure.sis.result │ ├── function.sis │ ├── function.sis.result │ ├── if.sis │ ├── if.sis.result │ ├── let.sis │ ├── let.sis.result │ ├── logic.sis │ ├── logic.sis.result │ ├── loop.sis │ ├── loop.sis.result │ ├── record.sis │ ├── record.sis.result │ ├── recursion.sis │ ├── recursion.sis.result │ ├── smoke.sis │ ├── smoke.sis.result │ ├── stdlib.sis │ ├── stdlib.sis.result │ ├── stream.sis │ └── stream.sis.result └── simple.ts ├── tsconfig.json └── tslint.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.result text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build folder 2 | build 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # node-waf configuration 27 | .lock-wscript 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directories 33 | node_modules 34 | jspm_packages 35 | 36 | # Optional npm cache directory 37 | .npm 38 | 39 | # Optional REPL history 40 | .node_repl_history 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7.5" 4 | env: 5 | - TASK=selfcheck 6 | 7 | script: 8 | - case "$TASK" in 9 | selfcheck) make setup test;; 10 | *) make -C $TASK check;; 11 | esac 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Renat Idrisov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | setup: 2 | npm install 3 | 4 | compile: 5 | tsc 6 | 7 | lint: 8 | @./node_modules/tslint/bin/tslint -c tslint.json 'src/**/*.ts' 9 | 10 | test: 11 | npm test 12 | 13 | .PHONY: test setup setup-dev compile lint 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Modernized Sisal Interpreter (MSInt) [![Build Status](https://travis-ci.org/parsifal-47/sisal-is.svg?branch=master)](https://travis-ci.org/parsifal-47/sisal-is) 2 | ======== 3 | 4 | Sisal with type inference and indent structuring! 5 | 6 | Current status: `early alpha` (some programs work but test coverage is very low) 7 | 8 | ### How does it look like 9 | 10 | The main difference between ordinary Sisal and this version that is does not require types to be explicitly specified and uses indents instead of *end function* stuff: 11 | 12 | ```python 13 | conv = f(m, a, n, x) 14 | for i in [1..n-m+1] 15 | returns array of for j in [1..m] 16 | returns sum of a[j] * x[i+j-1] 17 | 18 | main = f(m, cycles) 19 | let 20 | a = for i in [1.. m] returns array of i 21 | x = for i in [1.. m * cycles] returns array of i 22 | in 23 | conv( m, a, m * cycles, x ) 24 | ``` 25 | 26 | Please read [language overview](overview.md) if you want to know more. 27 | 28 | A set of test programs with results is available [here](test/programs/). 29 | More complex examples are [here](examples/). 30 | 31 | ### Supported constraints 32 | 33 | - f (function) 34 | - let in 35 | - if elseif else 36 | - for in 37 | - for while 38 | - for repeat 39 | - arithmetics + - * / % 40 | 41 | 42 | ### Standard reductions 43 | 44 | - sum, product, array, stream, last, max, min 45 | 46 | ### Deployment 47 | 48 | to setup: 49 | 50 | ```bash 51 | npm install 52 | ``` 53 | 54 | or 55 | 56 | ```bash 57 | make setup 58 | ``` 59 | 60 | to run tests: 61 | 62 | ```bash 63 | make test 64 | ``` 65 | 66 | to compile: 67 | 68 | ```bash 69 | make compile 70 | ``` 71 | 72 | to run sisal program: 73 | 74 | ```bash 75 | node ./build/sisal.js 76 | ``` 77 | 78 | to output computation graph in graphML format: 79 | 80 | ```bash 81 | node ./build/sisal.js --graph 82 | ``` 83 | 84 | for example: 85 | ```bash 86 | node ./build/sisal.js ./test/programs/if.sis --graph 87 | ``` 88 | 89 | ### Feedback 90 | 91 | Please, feel free to create issue or make contribution to this software! 92 | -------------------------------------------------------------------------------- /comparison.md: -------------------------------------------------------------------------------- 1 | # Syntax comparison 2 | 3 | The example below is straightforward matrix multiplication. 4 | 5 | ## Classic Sisal 6 | 7 | ```python 8 | function Main( A,B: array[array[real]]; M,N,L : integer returns array[array[real]] ) 9 | for i in 1, M cross j in 1, L 10 | returns array of 11 | for k in 1, N repeat 12 | R := A[i,k] * B[k,j] 13 | returns sum of R 14 | end for 15 | end for 16 | end function 17 | ``` 18 | 19 | ## Sisal-is 20 | 21 | ```python 22 | main = f(a,b,m,n,l) 23 | for i,j in [1..m],[1..l] 24 | returns array of for k in [1..n] 25 | r = a[i,k] * b[k,j] 26 | returns sum of r 27 | ``` 28 | -------------------------------------------------------------------------------- /examples/complex/conv.sis: -------------------------------------------------------------------------------- 1 | # CONVOLVER 2 | 3 | # This routine applies a general filter A of M elements to a real sequence 4 | # X of N elements. 5 | # 6 | # Y[i] = SUMMATION(j=1,M) of A[i] * X[i+j-1] for i = 1,2,...N-M+1 7 | # 8 | # Inputs: M, Cycles where N = M * Cycles 9 | # 10 | 11 | Conv = f(M, A, N, X) 12 | for I in [1..N-M+1] 13 | returns array of for J in [1..M] 14 | returns sum of A[J] * X[I+J-1] 15 | 16 | Main = f(M, Cycles) 17 | let 18 | A = for I in [1..M] returns array of double(I) 19 | X = for I in [1 .. M * Cycles] returns array of double(I) 20 | in 21 | Conv( M, A, M * Cycles, X ) 22 | -------------------------------------------------------------------------------- /examples/complex/dft2.sis: -------------------------------------------------------------------------------- 1 | # DISCRETE FOURIER TRANSFORM - FORWARD 2 | # 3 | # Y[K] := SUMMATION(J=1,N) X[J] * etothe( X ) For K = 1,2,...N 4 | # X := (2 * Pi * i (J-1) * (K-1)) / N 5 | # 6 | # e ** ix := cos(x) + i*sin(x) 7 | # 8 | 9 | dft = f(N, X) 10 | let 11 | Pi = 3.1415926535897932 12 | Theta = (2.0 * Pi) / double(N) 13 | in 14 | for K in [1..N] 15 | Rdata, 16 | Idata = for J in [1..N] 17 | EArg = Theta * double(J-1) * double(K-1) 18 | Er = cos(EArg) 19 | Ei = sin(EArg) 20 | Rvalue = (X[J].R * Er) - (X[J].I * Ei) 21 | Ivalue = (X[J].I * Er) + (X[J].R * Ei) 22 | returns sum of Rvalue, Ivalue 23 | returns array of (R = Rdata, I = Idata) 24 | 25 | Main = f(N) 26 | let 27 | Pi = 3.1415926535897932 28 | DRN = double(N) 29 | X = for J in [1..N] 30 | Rvalue = sin( (DRN * Pi) / 8.0 ) 31 | Ivalue = 0.0 32 | returns array of (R = Rvalue, I = Ivalue) 33 | in 34 | dft( N, X ) 35 | -------------------------------------------------------------------------------- /examples/complex/smooth.sis: -------------------------------------------------------------------------------- 1 | # FUNCTION Smooth: Piece of a large aerodynamics code obtained from NASA Ames. 2 | # Uses a 3 point weighted average for simplicity. 3 | # Refer to "Mapping Array Computations for a Dataflow Multiprocessor", by 4 | # J. Dennis. 5 | 6 | Smooth = f(n,Q,S,D) 7 | let 8 | S1 = for e in S at j, k, l 9 | v = if ( j = 1 | k = 1 | l = 1 | j = n | k = n | l = n ) 10 | e 11 | else 12 | e + 0.3 * ( Q[j+1,k,l] * D[j+1,k,l] 13 | - 2.0 * Q[j,k,l] * D[j,k,l] 14 | + Q[j-1,k,l] * D[j-1,k,l] ) / D[j,k,l] 15 | returns array of e 16 | 17 | S2 = for e in S1 at j, k, l 18 | v = if ( j = 1 | k = 1 | l = 1 | j = n | k = n | l = n ) 19 | e 20 | else 21 | e + 0.3 * ( Q[j,k+1,l] * D[j,k+1,l] 22 | - 2.0 * Q[j,k,l] * D[j,k,l] 23 | + Q[j,k-1,l] * D[j,k-1,l] ) / D[j,k,l] 24 | returns array of e 25 | 26 | S3 = for e in S2 at j, k, l 27 | v = if ( j = 1 | k = 1 | l = 1 | j = n | k = n | l = n ) 28 | e 29 | else 30 | e + 0.3 * ( Q[j,k,l+1] * D[j,k,l+1] 31 | - 2.0 * Q[j,k,l] * D[j,k,l] 32 | + Q[j,k,l-1] * D[j,k,l-1] ) / D[j,k,l] 33 | returns array of e 34 | in 35 | S3 36 | 37 | main = f(n) 38 | let 39 | Q = for i,j,k in [1..n],[1..n],[1..n] 40 | returns array of 1.1234 41 | 42 | S = for i,j,k in [1..n],[1..n],[1..n] 43 | returns array of 924.143567 44 | 45 | D = for i,j,k in [1..n],[1..n],[1..n] 46 | returns array of 0.1234 47 | in 48 | Smooth( n, Q, S, D ) 49 | -------------------------------------------------------------------------------- /examples/fibs.sis: -------------------------------------------------------------------------------- 1 | fib = f(m) 2 | if m < 2 3 | m 4 | else 5 | fib(m - 1) + fib(m - 2) 6 | 7 | main = f() 8 | fib(2), fib(5) 9 | -------------------------------------------------------------------------------- /examples/matmult.sis: -------------------------------------------------------------------------------- 1 | main = f(a,b,m,n,l) 2 | for i,j in [1..m],[1..l] 3 | returns array of for k in [1..n] 4 | r = a[i,k] * b[k,j] 5 | returns sum of r 6 | -------------------------------------------------------------------------------- /overview.md: -------------------------------------------------------------------------------- 1 | # Sisal-is 2 | 3 | Strongly-typed programming language with type inference. There are no side effects and no input in a form that most of us familiar with. Evaluation is lazy. 4 | 5 | Comparing to older Sisal versions it has lambdas and indent-based syntax. Check [this](comparison.md) document if you want to see new vs old. 6 | 7 | ## Code blocks 8 | 9 | The main way to express nesting is indentation, but indentation is ignored inside brackets: 10 | 11 | ```python 12 | 1 + ( 2 + 13 | 3 + 4) 14 | ``` 15 | 16 | This code will be interpreted equally to `1 + (2 + 3 + 4)`. Curly brackets could be used to write complex constraints inside brackets. In this case opening curly bracket 17 | will be interpreted as new indentation level start and closing bracket -- indentation end. 18 | 19 | It means that the following function definition: 20 | 21 | ```python 22 | f() 23 | 2 24 | ``` 25 | 26 | Could be equally rewritten as: 27 | 28 | ```python 29 | f() {2} 30 | ``` 31 | 32 | ## Primitive data types 33 | 34 | Three primitive types are currently supported: signed integers, booleans and floats. 35 | Capacity of integer and float is at least 32 bits. Notation is traditional, 36 | hex numbers could be written as "0x...". 37 | 38 | ## Arithmetics 39 | 40 | `*`, `/`, `+` and `-` are supported for float and integer numbers. 41 | 42 | ## Function definition 43 | 44 | `f()` is defining a function. Inside of parenthesis a list of arguments is followed by 45 | `returns` keyword and a list of return types. 46 | 47 | ```python 48 | f(returns integer) 49 | 2 50 | ``` 51 | 52 | This function takes no arguments and returns integer. Type definitions could be omitted: 53 | 54 | ```python 55 | f() 56 | 2 57 | ``` 58 | 59 | The following function takes two arguments and returns their sum. 60 | 61 | ```python 62 | f(a, b) 63 | a + b 64 | ``` 65 | 66 | Derived types for arguments and return value depend on surrounding code. They could be fixed in a following way: 67 | 68 | ```python 69 | f(a: integer, b: integer returns integer) 70 | a + b 71 | ``` 72 | 73 | ## Sisal program 74 | 75 | Program is a sequence of definitions, it is equal to first inner block of `let` operator 76 | with one exception: definition `main` is invoked as a function. 77 | 78 | ```python 79 | main = f() 80 | 2 + 3 81 | ``` 82 | 83 | ```python 84 | dub = f(a) 85 | a * 2 86 | 87 | main = f() 88 | 2 + dub(3) 89 | ``` 90 | 91 | ## Function application 92 | In case of single argument, function can be applied with `of` syntax. 93 | 94 | ```python 95 | dub = f(a) 96 | a * 2 97 | 98 | main = f() 99 | 2 + dub of 3 100 | ``` 101 | 102 | It makes possible to write nested loops without brackets as we'll see below. 103 | 104 | ## Closures and recursion 105 | 106 | Function implicitly accessing its definition context. 107 | 108 | ```python 109 | fib = f(M) 110 | if (M < 2) 111 | M 112 | else 113 | fib(M - 1) + fib(M - 2) 114 | ``` 115 | 116 | ## Polymorphism 117 | 118 | Supported for input arguments as well as for return values. 119 | 120 | ## Composite data types 121 | 122 | ### array 123 | 124 | Contains elements of one type. Type itself is defined as `array[]`. 125 | Array value is defined as a list of its elements: `[2, 3, 4]`. Index is zero-based. 126 | 127 | ### record 128 | 129 | Contains named fields. Datatype is defined as `record[name1: type1, name2: type2]`. Type annotations for fields are not required when could be inferred. Record value is defined as `[name1 = 2, name2 = 3]`. Default values are not supported. 130 | 131 | ### stream 132 | 133 | This type is almost equal to `array`, except it could be infinite. Datatype is defined as `stream[]`. Value of this type could be defined as stream([2, 3, 4]). Where function `stream` converts `array` into `stream`. 134 | 135 | ### function 136 | 137 | Function datatype is defined as `function[, ... [returns , ...]]`. Function value is defined with the following syntax: 138 | 139 | ```python 140 | f() 141 | 2 142 | ``` 143 | 144 | ### datatype 145 | Types could be set and assigned in the same way with other values: 146 | 147 | ```python 148 | a = function[integer returns integer] 149 | ``` 150 | 151 | ## Bindings (let) 152 | 153 | Usually bindings serve better readability, the following statement is supported: 154 | 155 | ```python 156 | main = f(m) 157 | let 158 | a = 1 159 | b = 2 160 | in 161 | a + b + m 162 | ``` 163 | 164 | `let` keyword is followed by definition list and `in` keyword, after which goes the expression for evaluation. 165 | 166 | ## If 167 | 168 | ```python 169 | foo = f(a, b) 170 | if ( a > b ) 171 | a - b 172 | else 173 | b - a 174 | ``` 175 | 176 | ```python 177 | foo = f(a, b) 178 | if ( a > b ) 179 | a - b 180 | elseif ( a < b ) 181 | b - a 182 | else 183 | 0 184 | ``` 185 | 186 | ## Loops 187 | The most distinctive part of Sisal is loop definition. Body of each loop contains list of bindings which is equal to `let` operator, loop ends with reduction, it describes how values from loop iterations are aggregated. 188 | 189 | Loops could contain initialization block and references to previous iterations with `old` keyword. 190 | 191 | ### Reductions 192 | 193 | Built-in reductions are `sum`, `product`, `array`, `stream`, `value`, `max`, `min`. 194 | Any user function which takes single stream argument could be used as a reduction. Recursive reductions are allowed. 195 | 196 | ### Range loops 197 | 198 | All iterations are evaluated for a given array or stream. 199 | 200 | ```python 201 | length = f() 202 | for i in [1 .. 100] 203 | returns array(i) 204 | ``` 205 | 206 | ```python 207 | length = f() 208 | for i in [1 .. 100] 209 | returns array(i), max(i), min(i) 210 | ``` 211 | 212 | ```python 213 | length = f(A: stream) 214 | for i in A 215 | returns array of i 216 | ``` 217 | 218 | Multi-dimensional ranges are supported: 219 | 220 | ```python 221 | main = f(A) 222 | for i,j in [1 .. 100],[1 .. 100] 223 | s = A[i, j] 224 | returns stream of s 225 | ``` 226 | 227 | ### Pre-conditional loops 228 | Condition is evaluated before the first iteration. 229 | 230 | ```python 231 | foo = f(N) 232 | for initial 233 | i = 0 234 | while (i<=N) 235 | i = old i + 1 236 | returns value of i 237 | ``` 238 | 239 | ```python 240 | foo = f(N, T, S) 241 | for initial 242 | t = T 243 | s = S 244 | i = 0 245 | while (i <= 2*N) 246 | i = old i + (t * s) + 2*S 247 | returns value of i 248 | ``` 249 | 250 | ### Post-conditional loops 251 | 252 | ```python 253 | length = f(N) 254 | for initial 255 | L = if (N >= 0) 256 | 0 257 | else 258 | 1 259 | repeat 260 | N = old N / 10 261 | L = old L + 1 262 | while N /= 0 263 | returns value of L 264 | ``` 265 | 266 | ```python 267 | foo = f(N, T) 268 | for initial 269 | i = 0 270 | s = 0 271 | t = T 272 | repeat 273 | s = old s + old i 274 | i = if (t>0) 275 | old i + t 276 | else 277 | old i + 1 278 | while ( (s <= N) & (old i <= 100) ) 279 | returns value of s 280 | ``` 281 | 282 | ## Type casting 283 | Types are converted with the following polymorphic functions: 284 | 285 | - `integer` -- converts other primitive types and string to integer. 286 | - `float` -- converts other primitive types and string to float. 287 | - `string` -- converts primitive types to string. 288 | - `stream` -- converts array to stream. 289 | - `type` -- converts value to its type. 290 | 291 | ## Range generators 292 | 293 | List of natural numbers could be generated with special syntax: `[2..100]`. Left border should be less than right, right might be absent, in this case the stream is infinite: `[1..]` (stream of natural numbers). 294 | 295 | ## Standard library 296 | 297 | `sin`, `cos`, `sqrt`, `pow` -- accepting and returning float values. 298 | 299 | ## Modularity 300 | 301 | Definitions from other files are imported with `import`, it takes a string and returns a record, containing all top-level definitions from the given file. 302 | 303 | ```python 304 | A = import("library.sis") 305 | main = f() 306 | A.main() 307 | ``` 308 | 309 | ## Interaction with external world 310 | 311 | Sisal program is getting values as input arguments, streams should be used in of case interactive application. Please refer to specific interpreter or compiler for the details. 312 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sisal-is", 3 | "version": "0.1.0", 4 | "description": "Modernized dialect of Sisal", 5 | "main": "./build/sisal.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "test": "mocha", 11 | "coverage": "nyc mocha" 12 | }, 13 | "nyc": { 14 | "include": [ 15 | "src/**/*.ts" 16 | ], 17 | "extension": [ 18 | ".ts" 19 | ], 20 | "require": [ 21 | "ts-node/register" 22 | ], 23 | "reporter": [ 24 | "text-summary", 25 | "html" 26 | ], 27 | "sourceMap": true 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/parsifal-47/sisal-is.git" 32 | }, 33 | "keywords": [ 34 | "sisal", 35 | "interpreter", 36 | "streams", 37 | "dataflow", 38 | "functional" 39 | ], 40 | "author": "Renat Idrisov", 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/parsifal-47/sisal-is/issues" 44 | }, 45 | "homepage": "https://github.com/parsifal-47/sisal-is#readme", 46 | "dependencies": { 47 | "@types/node": "^9.4.0", 48 | "@types/pegjs": "^0.10.0", 49 | "indent-adder": "git://github.com/parsifal-47/indent-adder.git", 50 | "pegjs": "^0.10.0", 51 | "pretty-data": "git://github.com/parsifal-47/pretty-data.git" 52 | }, 53 | "devDependencies": { 54 | "@types/chai": "^4.1.2", 55 | "@types/mocha": "^2.2.48", 56 | "chai": "^4.1.2", 57 | "istanbul": "^0.4.5", 58 | "mocha": "^5.0.0", 59 | "nyc": "^11.4.1", 60 | "source-map-support": "^0.5.3", 61 | "ts-node": "^4.1.0", 62 | "tslint": "^5.9.1", 63 | "tsutils": "^2.21.0", 64 | "typescript": "2.7.1" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ast/composite.ts: -------------------------------------------------------------------------------- 1 | import { Definition } from "./definition"; 2 | import { Expression } from "./expression"; 3 | import { FunctionValue } from "./function"; 4 | import { Node } from "./node"; 5 | import { TypeValue } from "./types/value"; 6 | 7 | export type CompositeValue = ArrayValue | RecordValue | 8 | StreamValue | FunctionValue | TypeValue; 9 | 10 | export interface ArrayValue extends Node { 11 | contents: Expression[]; 12 | } 13 | 14 | export function isArrayValue(node: Node): node is ArrayValue { 15 | return node.type === "Array"; 16 | } 17 | 18 | export interface RecordValue extends Node { 19 | contents: Definition[]; 20 | } 21 | 22 | export function isRecordValue(node: Node): node is RecordValue { 23 | return node.type === "Record"; 24 | } 25 | 26 | export interface StreamValue extends Node { 27 | lowerBound: Expression; 28 | upperBound: Expression; 29 | } 30 | 31 | export function isStreamValue(node: Node): node is StreamValue { 32 | return node.type === "Stream"; 33 | } 34 | -------------------------------------------------------------------------------- /src/ast/definition.ts: -------------------------------------------------------------------------------- 1 | import { Expression } from "./expression"; 2 | import { Node } from "./node"; 3 | 4 | export interface Definition extends Node { 5 | left: string[]; 6 | right: Expression[]; 7 | } 8 | 9 | export function isDefinition(node: Node): node is Definition { 10 | return node.type === "Definition"; 11 | } 12 | -------------------------------------------------------------------------------- /src/ast/expression.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "./node"; 2 | import { Operand } from "./operand"; 3 | 4 | export type Expression = BinaryExpression | UnaryExpression | Postfix | Operand; 5 | 6 | export interface BinaryExpression extends Node { 7 | operator: string; 8 | left: Expression; 9 | right: Expression; 10 | } 11 | 12 | export function isBinaryExpression(node: Node): node is BinaryExpression { 13 | return node.type === "Binary"; 14 | } 15 | 16 | export interface UnaryExpression extends Node { 17 | operator: string; 18 | right: Expression; 19 | } 20 | 21 | export function isUnaryExpression(node: Node): node is UnaryExpression { 22 | return node.type === "Unary"; 23 | } 24 | 25 | export interface Postfix extends Node { 26 | base: Operand; 27 | operationList: PostfixOperation[]; 28 | } 29 | 30 | export function isPostfix(node: Node): node is Postfix { 31 | return node.type === "Postfix"; 32 | } 33 | 34 | export type PostfixOperation = FunctionCall | RecordAccess | ArrayAccess; 35 | 36 | export interface FunctionCall extends Node { 37 | arguments: Expression[]; 38 | } 39 | 40 | export function isFunctionCall(node: Node): node is FunctionCall { 41 | return node.type === "FunctionCall"; 42 | } 43 | 44 | export interface RecordAccess extends Node { 45 | field: string; 46 | } 47 | 48 | export function isRecordAccess(node: Node): node is RecordAccess { 49 | return node.type === "RecordAccess"; 50 | } 51 | 52 | export interface ArrayAccess extends Node { 53 | index: Expression; 54 | } 55 | 56 | export function isArrayAccess(node: Node): node is ArrayAccess { 57 | return node.type === "ArrayAccess"; 58 | } 59 | -------------------------------------------------------------------------------- /src/ast/function.ts: -------------------------------------------------------------------------------- 1 | import { Definition } from "./definition"; 2 | import { Expression } from "./expression"; 3 | import { Node } from "./node"; 4 | 5 | export interface FunctionValue extends Node { 6 | params: IdWithOptionalType[]; 7 | body: Expression[]; 8 | returns: Expression[]; 9 | } 10 | 11 | export function isFunctionValue(node: Node): node is FunctionValue { 12 | return node.type === "Lambda"; 13 | } 14 | 15 | export interface IdWithOptionalType extends Node { 16 | name: string; 17 | dataType?: Expression; 18 | } 19 | 20 | export function isIdWithOptionalType(node: Node): node is IdWithOptionalType { 21 | return node.type === "WeaklyTypedIdentifier"; 22 | } 23 | -------------------------------------------------------------------------------- /src/ast/if.ts: -------------------------------------------------------------------------------- 1 | import { Expression } from "./expression"; 2 | import { Node } from "./node"; 3 | 4 | export interface IfExpression extends Node { 5 | condition: Expression; 6 | thenBranch: Expression[]; 7 | elseBranch: Expression[]; 8 | elseIfs: ElseIfBranch[]; 9 | } 10 | 11 | export function isIfExpression(node: Node): node is IfExpression { 12 | return node.type === "If"; 13 | } 14 | 15 | export interface ElseIfBranch extends Node { 16 | condition: Expression; 17 | branch: Expression[]; 18 | } 19 | 20 | export function isElseIfBranch(node: Node): node is ElseIfBranch { 21 | return node.type === "ElseIf"; 22 | } 23 | -------------------------------------------------------------------------------- /src/ast/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./composite"; 2 | export * from "./definition"; 3 | export * from "./expression"; 4 | export * from "./function"; 5 | export * from "./if"; 6 | export * from "./let"; 7 | export * from "./literal"; 8 | export * from "./loop"; 9 | export * from "./node"; 10 | export * from "./operand"; 11 | -------------------------------------------------------------------------------- /src/ast/let.ts: -------------------------------------------------------------------------------- 1 | import { Definition } from "./definition"; 2 | import { Expression } from "./expression"; 3 | import { Node } from "./node"; 4 | 5 | export interface LetExpression extends Node { 6 | definitions: Definition[]; 7 | expressions: Expression[]; 8 | } 9 | 10 | export function isLetExpression(node: Node): node is LetExpression { 11 | return node.type === "Let"; 12 | } 13 | -------------------------------------------------------------------------------- /src/ast/literal.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "./node"; 2 | 3 | export interface IntegerLiteral extends Node { 4 | value: number; 5 | } 6 | 7 | export function isIntegerLiteral(node: Node): node is IntegerLiteral { 8 | return node.type === "IntegerLiteral"; 9 | } 10 | 11 | export interface FloatLiteral extends Node { 12 | value: number; 13 | } 14 | 15 | export function isFloatLiteral(node: Node): node is FloatLiteral { 16 | return node.type === "FloatLiteral"; 17 | } 18 | 19 | export interface BooleanLiteral extends Node { 20 | value: boolean; 21 | } 22 | 23 | export function isBooleanLiteral(node: Node): node is BooleanLiteral { 24 | return node.type === "BooleanLiteral"; 25 | } 26 | 27 | export interface StringLiteral extends Node { 28 | value: string; 29 | } 30 | 31 | export function isStringLiteral(node: Node): node is StringLiteral { 32 | return node.type === "StringLiteral"; 33 | } 34 | 35 | export type Literal = IntegerLiteral | FloatLiteral | StringLiteral | BooleanLiteral; 36 | 37 | export function isLiteral(node: Node): node is Literal { 38 | return isIntegerLiteral(node) || isFloatLiteral(node) || 39 | isStringLiteral(node) || isBooleanLiteral(node); 40 | } 41 | -------------------------------------------------------------------------------- /src/ast/loop.ts: -------------------------------------------------------------------------------- 1 | import { Definition } from "./definition"; 2 | import { Expression } from "./expression"; 3 | import { Node } from "./node"; 4 | 5 | export interface LoopExpression extends Node { 6 | range?: RangeList; 7 | init: Definition[]; 8 | preCondition?: Expression; 9 | postCondition?: Expression; 10 | body: Definition[]; 11 | returns: Expression[]; 12 | } 13 | 14 | export function isLoopExpression(node: Node): node is LoopExpression { 15 | return node.type === "Loop"; 16 | } 17 | 18 | export interface RangeList extends Node { 19 | names: string[]; 20 | ranges: Expression[]; 21 | } 22 | 23 | export function isRangeList(node: Node): node is RangeList { 24 | return node.type === "RangeList"; 25 | } 26 | -------------------------------------------------------------------------------- /src/ast/node.ts: -------------------------------------------------------------------------------- 1 | export interface Position { 2 | offset: number; 3 | line: number; 4 | column: number; 5 | } 6 | 7 | export interface Location { 8 | start: Position; 9 | end: Position; 10 | } 11 | 12 | export interface Node { 13 | type: string; 14 | location: Location; 15 | } 16 | 17 | export function isNode(node: any): node is Node { 18 | return node.type !== undefined; 19 | } 20 | -------------------------------------------------------------------------------- /src/ast/operand.ts: -------------------------------------------------------------------------------- 1 | import { CompositeValue } from "./composite"; 2 | import { IfExpression } from "./if"; 3 | import { LetExpression } from "./let"; 4 | import { Literal } from "./literal"; 5 | import { LoopExpression } from "./loop"; 6 | import { Node } from "./node"; 7 | 8 | export type Operand = CompositeExpression | OldValue | CompositeValue | Literal | string; 9 | export type CompositeExpression = LetExpression | LoopExpression | IfExpression; 10 | 11 | export interface OldValue extends Node { 12 | id: string; 13 | } 14 | 15 | export function isOldValue(node: Node): node is OldValue { 16 | return node.type === "Old"; 17 | } 18 | -------------------------------------------------------------------------------- /src/ast/types/array.ts: -------------------------------------------------------------------------------- 1 | import { Expression } from "../expression"; 2 | import { TypeValue } from "./value"; 3 | 4 | export interface ArrayType extends TypeValue { 5 | elementType: Expression; 6 | } 7 | 8 | export function isArrayType(node: TypeValue): node is ArrayType { 9 | return node.name === "Array"; 10 | } 11 | -------------------------------------------------------------------------------- /src/ast/types/function.ts: -------------------------------------------------------------------------------- 1 | import { Expression } from "../expression"; 2 | import { TypeValue } from "./value"; 3 | 4 | export interface FunctionType extends TypeValue { 5 | params: Expression[]; 6 | returns: Expression[]; 7 | } 8 | 9 | export function isFunctionType(node: TypeValue): node is FunctionType { 10 | return node.name === "Function"; 11 | } 12 | -------------------------------------------------------------------------------- /src/ast/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./array"; 2 | export * from "./function"; 3 | export * from "./primitive"; 4 | export * from "./record"; 5 | export * from "./stream"; 6 | export * from "./value"; 7 | -------------------------------------------------------------------------------- /src/ast/types/primitive.ts: -------------------------------------------------------------------------------- 1 | import { TypeValue } from "./value"; 2 | 3 | export type PrimitiveType = IntegerType | BooleanType | FloatType | StringType; 4 | 5 | export type IntegerType = TypeValue; 6 | 7 | export function isIntegerType(node: TypeValue): node is IntegerType { 8 | return node.name === "integer"; 9 | } 10 | 11 | export type BooleanType = TypeValue; 12 | 13 | export function isBooleanType(node: TypeValue): node is BooleanType { 14 | return node.name === "boolean"; 15 | } 16 | 17 | export type FloatType = TypeValue; 18 | 19 | export function isFloatType(node: TypeValue): node is FloatType { 20 | return node.name === "float"; 21 | } 22 | 23 | export type StringType = TypeValue; 24 | 25 | export function isStringType(node: TypeValue): node is StringType { 26 | return node.name === "string"; 27 | } 28 | -------------------------------------------------------------------------------- /src/ast/types/record.ts: -------------------------------------------------------------------------------- 1 | import { IdWithOptionalType } from "../function"; 2 | import { TypeValue } from "./value"; 3 | 4 | export interface RecordType extends TypeValue { 5 | fields: IdWithOptionalType[]; 6 | } 7 | 8 | export function isRecordType(node: TypeValue): node is RecordType { 9 | return node.name === "Record"; 10 | } 11 | -------------------------------------------------------------------------------- /src/ast/types/stream.ts: -------------------------------------------------------------------------------- 1 | import { Expression } from "../expression"; 2 | import { TypeValue } from "./value"; 3 | 4 | export interface StreamType extends TypeValue { 5 | elementType: Expression; 6 | } 7 | 8 | export function isStreamType(node: TypeValue): node is StreamType { 9 | return node.name === "Stream"; 10 | } 11 | -------------------------------------------------------------------------------- /src/ast/types/value.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "../node"; 2 | 3 | export interface TypeValue extends Node { 4 | name: string; 5 | } 6 | 7 | export function isTypeValue(node: Node): node is TypeValue { 8 | return node.type === "Type"; 9 | } 10 | -------------------------------------------------------------------------------- /src/grammar/sisal.pegjs: -------------------------------------------------------------------------------- 1 | { 2 | function makeList(initial, tail, num) { 3 | for (var i = 0; i < tail.length; i++) { 4 | initial.push(tail[i][num]); 5 | } 6 | return initial; 7 | } 8 | function makeBinary(head, tail) { 9 | var result = head; 10 | for (var i = 0; i < tail.length; i++) { 11 | result = { 12 | type: "Binary", 13 | location: location(), 14 | operator: tail[i][1], 15 | left: result, 16 | right: tail[i][3] 17 | }; 18 | } 19 | return result; 20 | } 21 | } 22 | 23 | start 24 | = __ program:DefinitionList __ { return program; } 25 | 26 | SourceCharacter 27 | = . 28 | 29 | /* Whitespaces and comments */ 30 | 31 | WhiteSpace "whitespace" 32 | = [\t\v\f ] 33 | 34 | LineTerminator 35 | = [\n\r\u2028\u2029] 36 | 37 | LineTerminatorSequence "end of line" 38 | = "\n" 39 | / "\r\n" 40 | / "\r" 41 | / "\u2028" // line separator 42 | / "\u2029" // paragraph separator 43 | 44 | Comment "comment" 45 | = SingleLineComment 46 | 47 | SingleLineComment 48 | = "#" (!LineTerminator SourceCharacter)* 49 | 50 | /* Identifiers */ 51 | 52 | Identifier "identifier" 53 | = !ReservedWord name:IdentifierName { return name; } 54 | 55 | IdentifierName "identifier" 56 | = start:IdentifierStart parts:IdentifierPart* { 57 | return start + parts.join(""); 58 | } 59 | 60 | IdentifierStart 61 | = [a-zA-Z] 62 | / "$" 63 | / "_" 64 | / "\\" sequence:UnicodeEscapeSequence { return sequence; } 65 | 66 | IdentifierPart 67 | = IdentifierStart 68 | / [0-9] 69 | 70 | ReservedWord 71 | = Keyword 72 | / BooleanLiteral 73 | 74 | Keyword 75 | = ( "f" 76 | / "record" 77 | / "returns" 78 | / "repeat" 79 | / "for" 80 | / "while" 81 | / "if" 82 | / "of" 83 | / "let" 84 | / "in" 85 | ) 86 | !IdentifierPart 87 | 88 | /* Literals */ 89 | 90 | Literal 91 | = BooleanLiteral 92 | / NumericLiteral 93 | / value:StringLiteral { 94 | return { 95 | type: "StringLiteral", 96 | location: location(), 97 | value: value 98 | }; 99 | } 100 | 101 | BooleanLiteral 102 | = TrueToken { return { type: "BooleanLiteral", location: location(), value: true }; } 103 | / FalseToken { return { type: "BooleanLiteral", location: location(), value: false }; } 104 | 105 | NumericLiteral "number" 106 | = literal:(HexIntegerLiteral / DecimalLiteral) !IdentifierStart { 107 | return literal; 108 | } 109 | 110 | DecimalLiteral 111 | = before:DecimalIntegerLiteral 112 | after:("." DecimalDigits)? 113 | exponent:ExponentPart? { 114 | if (after !== null || exponent !== null) { 115 | return { 116 | type: "FloatLiteral", 117 | location: location(), 118 | value: parseFloat(before + "." + (after !== null ? after[1] : "") + 119 | (exponent !== null ? exponent : "")) 120 | }; 121 | } 122 | return { 123 | type: "IntegerLiteral", 124 | location: location(), 125 | value: parseInt(before) 126 | }; 127 | } 128 | / "." after:DecimalDigits exponent:ExponentPart? { 129 | return { 130 | type: "FloatLiteral", 131 | location: location(), 132 | value: parseFloat("." + after + exponent) 133 | }; 134 | } 135 | / before:DecimalIntegerLiteral exponent:ExponentPart? { 136 | if (exponent !== null) { 137 | return { 138 | type: "FloatLiteral", 139 | location: location(), 140 | value: parseFloat(before + exponent) 141 | }; 142 | } 143 | return { 144 | type: "IntegerLiteral", 145 | location: location(), 146 | value: parseInt(before) 147 | }; 148 | } 149 | 150 | DecimalIntegerLiteral 151 | = "0" / digit:NonZeroDigit digits:DecimalDigits? { 152 | return digit + (digits === null ? "" : digits); 153 | } 154 | 155 | DecimalDigits 156 | = digits:DecimalDigit+ { return digits.join(""); } 157 | 158 | DecimalDigit 159 | = [0-9] 160 | 161 | NonZeroDigit 162 | = [1-9] 163 | 164 | ExponentPart 165 | = indicator:ExponentIndicator integer:SignedInteger { 166 | return indicator + integer; 167 | } 168 | 169 | ExponentIndicator 170 | = [eE] 171 | 172 | SignedInteger 173 | = sign:[-+]? digits:DecimalDigits { return sign + digits; } 174 | 175 | HexIntegerLiteral 176 | = "0" [xX] digits:HexDigit+ { return parseInt("0x" + digits.join("")); } 177 | 178 | HexDigit 179 | = [0-9a-fA-F] 180 | 181 | StringLiteral "string" 182 | = parts:('"' DoubleStringCharacters? '"' / "'" SingleStringCharacters? "'") { 183 | return parts[1]; 184 | } 185 | 186 | DoubleStringCharacters 187 | = chars:DoubleStringCharacter+ { return chars.join(""); } 188 | 189 | SingleStringCharacters 190 | = chars:SingleStringCharacter+ { return chars.join(""); } 191 | 192 | DoubleStringCharacter 193 | = !('"' / "\\" / LineTerminator) char_:SourceCharacter { return char_; } 194 | / "\\" sequence:EscapeSequence { return sequence; } 195 | / LineContinuation 196 | 197 | SingleStringCharacter 198 | = !("'" / "\\" / LineTerminator) char_:SourceCharacter { return char_; } 199 | / "\\" sequence:EscapeSequence { return sequence; } 200 | / LineContinuation 201 | 202 | LineContinuation 203 | = "\\" sequence:LineTerminatorSequence { return sequence; } 204 | 205 | EscapeSequence 206 | = CharacterEscapeSequence 207 | / "0" !DecimalDigit { return "\0"; } 208 | / HexEscapeSequence 209 | / UnicodeEscapeSequence 210 | 211 | CharacterEscapeSequence 212 | = SingleEscapeCharacter 213 | / NonEscapeCharacter 214 | 215 | SingleEscapeCharacter 216 | = char_:['"\\bfnrtv] { 217 | return char_ 218 | .replace("b", "\b") 219 | .replace("f", "\f") 220 | .replace("n", "\n") 221 | .replace("r", "\r") 222 | .replace("t", "\t") 223 | .replace("v", "\x0B") // IE does not recognize "\v". 224 | } 225 | 226 | NonEscapeCharacter 227 | = (!EscapeCharacter / LineTerminator) char_:SourceCharacter { return char_; } 228 | 229 | EscapeCharacter 230 | = SingleEscapeCharacter 231 | / DecimalDigit 232 | / "x" 233 | / "u" 234 | 235 | HexEscapeSequence 236 | = "x" h1:HexDigit h2:HexDigit { 237 | return String.fromCharCode(parseInt("0x" + h1 + h2)); 238 | } 239 | 240 | UnicodeEscapeSequence 241 | = "u" h1:HexDigit h2:HexDigit h3:HexDigit h4:HexDigit { 242 | return String.fromCharCode(parseInt("0x" + h1 + h2 + h3 + h4)); 243 | } 244 | 245 | /* Tokens */ 246 | 247 | DoToken = "do" !IdentifierPart 248 | ElseToken = "else" !IdentifierPart 249 | FalseToken = "false" !IdentifierPart 250 | LoopToken = "for" !IdentifierPart 251 | FunctionToken = "f" !IdentifierPart 252 | OldToken = "old" !IdentifierPart 253 | LetToken = "let" !IdentifierPart 254 | IfToken = "if" !IdentifierPart 255 | ElseIfToken = "elseif" !IdentifierPart 256 | ErrorToken = "error" !IdentifierPart 257 | InToken = "in" !IdentifierPart 258 | ReturnsToken = "returns" !IdentifierPart 259 | TrueToken = "true" !IdentifierPart 260 | WhileToken = "while" !IdentifierPart 261 | RepeatToken = "repeat" !IdentifierPart 262 | InitialToken = "initial" !IdentifierPart 263 | OfToken = "of" !IdentifierPart 264 | 265 | IntegerToken = "integer" 266 | FloatToken = "float" 267 | StringToken = "string" 268 | BooleanToken = "boolean" 269 | 270 | StreamToken = "stream" 271 | ArrayToken = "array" 272 | RecordToken = "record" 273 | FunctionTypeToken = "function" 274 | 275 | IndentToken = "{" 276 | DedentToken = "}" 277 | 278 | EOS 279 | = __ ";" 280 | / _ LineTerminatorSequence 281 | / _ &"}" 282 | / __ EOF 283 | 284 | EOF 285 | = !. 286 | 287 | /* Underscore */ 288 | 289 | _ 290 | = (WhiteSpace / SingleLineComment)* 291 | 292 | __ 293 | = (WhiteSpace / LineTerminatorSequence / Comment)* 294 | 295 | /* Composite Type Values */ 296 | 297 | CompositeValue 298 | = RecordValue 299 | / ArrayValue 300 | / StreamValue 301 | / FunctionValue 302 | / TypeValue 303 | 304 | ArrayValue 305 | = "[" __ contents:ExpressionList __ "]" { 306 | return {type: "Array", location: location(), contents: contents}; 307 | } 308 | 309 | RecordValue 310 | = "[" __ contents:DefinitionList __ "]" { 311 | return {type: "Record", location: location(), contents: contents}; 312 | } 313 | 314 | StreamValue 315 | = "[" __ lowerBound:Expression __ ".." __ upperBound:Expression? __ "]" { 316 | return {type: "Stream", location: location(), lowerBound: lowerBound, upperBound: upperBound }; 317 | } 318 | 319 | FunctionValue 320 | = FunctionToken __ 321 | "(" __ params:IdsWithOptionalTypes? __ 322 | returns:FunctionReturns? ")" __ body:FunctionBody { 323 | return { 324 | type: "Lambda", 325 | location: location(), 326 | params: params ? params : [], 327 | returns: returns ? returns : [], 328 | body: body 329 | }; 330 | } 331 | 332 | FunctionBody 333 | = IndentToken __ expressions:ExpressionList __ DedentToken { 334 | return expressions; 335 | } 336 | 337 | FunctionReturns 338 | = ReturnsToken __ returns:ExpressionList __ { 339 | return returns; 340 | } 341 | 342 | TypeValue 343 | = PrimitiveType 344 | / StreamType 345 | / ArrayType 346 | / RecordType 347 | / FunctionType 348 | 349 | PrimitiveType 350 | = name:(IntegerToken / BooleanToken / FloatToken / StringToken) { 351 | return {type: "Type", location: location(), name: name }; 352 | } 353 | 354 | StreamType 355 | = StreamToken __ "[" __ type:Expression __ "]" { 356 | return {type: "Type", location: location(), name: "Stream", elementType: type }; 357 | } 358 | 359 | ArrayType 360 | = ArrayToken __ "[" __ type:Expression __ "]" { 361 | return {type: "Type", location: location(), name: "Array", elementType: type }; 362 | } 363 | 364 | RecordType 365 | = RecordToken __ "[" __ fields:IdsWithOptionalTypes __ "]" { 366 | return {type: "Type", location: location(), name: "Record", fields: fields }; 367 | } 368 | 369 | FunctionType 370 | = FunctionTypeToken __ 371 | "[" __ params: ExpressionList __ returns:FunctionReturns? "]" { 372 | return {type: "Type", location: location(), name: "Function", params: params, returns: returns }; 373 | } 374 | 375 | IdsWithOptionalTypes 376 | = head:IdWithOptionalType 377 | tail:(__ "," __ IdWithOptionalType)* { 378 | return makeList([head], tail, 3); 379 | } 380 | 381 | IdWithOptionalType 382 | = id:Identifier __ type:(":" __ Expression __)? { 383 | return {type: "WeaklyTypedIdentifier", location: location(), name: id, dataType: (type ? type[2]: null) }; 384 | } 385 | 386 | /* Expressions */ 387 | 388 | Expression 389 | = LogicalOperation 390 | 391 | LogicalOperator 392 | = "||" { return "|"; } 393 | / "|" !"|" { return "|"; } 394 | / "^" 395 | / "&" 396 | 397 | LogicalOperation 398 | = head:Compare 399 | tail:(__ LogicalOperator __ Compare)* { 400 | return makeBinary(head, tail); 401 | } 402 | 403 | CompareOperator 404 | = "=" 405 | / "/=" 406 | / "<" !"=" { return "<"; } 407 | / ">" !"=" { return ">"; } 408 | / "<=" 409 | / ">=" 410 | 411 | Compare 412 | = head:LowPriorityOperation 413 | tail:(__ CompareOperator __ LowPriorityOperation)* { 414 | return makeBinary(head, tail); 415 | } 416 | 417 | LowPriorityOperator 418 | = "+" 419 | / "-" 420 | 421 | LowPriorityOperation 422 | = head:HighPriorityOperation 423 | tail:(__ LowPriorityOperator __ HighPriorityOperation)* { 424 | return makeBinary(head, tail); 425 | } 426 | 427 | HighPriorityOperator 428 | = "*" 429 | / "/" 430 | / "%" 431 | 432 | HighPriorityOperation 433 | = head:UnaryOperation 434 | tail:(__ HighPriorityOperator __ UnaryOperation)* { 435 | return makeBinary(head, tail); 436 | } 437 | 438 | UnaryOperator 439 | = "-" 440 | / "+" 441 | / "!" 442 | 443 | UnaryOperation 444 | = operation:UnaryOperator __ operand:UnaryOperation { 445 | return {type: "Unary", location: location(), operator: operation, right: operand}; 446 | } 447 | / PostfixOperation 448 | 449 | PostfixOperation 450 | = base:Operand 451 | args:( 452 | __ "(" __ expressions:ExpressionList? __ ")" { 453 | return {type: "FunctionCall", location: location(), arguments: expressions }; 454 | } 455 | / __ "." __ name:Identifier { 456 | return {type: "RecordAccess", location: location(), field: name }; 457 | } 458 | / __ "[" __ expression:Expression __ "]" { 459 | return {type: "ArrayAccess", location: location(), index: expression}; 460 | } 461 | / __ "of" __ expression:Expression { 462 | return {type: "FunctionCall", location: location(), arguments: [expression]}; 463 | } 464 | )* { 465 | if (args.length==0) return base; 466 | 467 | var result = { 468 | type: "Postfix", 469 | location: location(), 470 | base: base, 471 | operationList: [] 472 | }; 473 | for (var i = 0; i < args.length; i++) { 474 | result.operationList.push(args[i]); 475 | } 476 | return result; 477 | } 478 | / "(" __ exp:Expression __ ")" { 479 | return exp; 480 | } 481 | / Operand 482 | 483 | ExpressionList 484 | = head:Expression 485 | tail:(__ "," __ Expression)* { 486 | return makeList([head], tail, 3); 487 | } 488 | 489 | /* Operands */ 490 | 491 | Operand 492 | = LetExpression 493 | / LoopExpression 494 | / IfExpression 495 | / OldToken __ name:Identifier {return {type: "Old", location: location(), id: name}} 496 | / CompositeValue 497 | / Identifier 498 | / Literal 499 | 500 | /* Compound expressions */ 501 | 502 | LetExpression 503 | = LetToken __ definitions:WrappedDefintions InToken __ expressions:WrappedExpressions { 504 | return {type: "Let", location: location(), definitions: definitions, expressions: expressions }; 505 | } 506 | 507 | WrappedDefintions 508 | = IndentToken __ definitions:DefinitionList __ DedentToken __ { 509 | return definitions; 510 | } 511 | 512 | WrappedExpressions 513 | = IndentToken __ expressions:ExpressionList __ DedentToken __ { 514 | return expressions; 515 | } 516 | 517 | DefinitionList 518 | = head:Definition 519 | tail:(__ Definition)* { 520 | return makeList([head], tail, 1); 521 | } 522 | 523 | Definition 524 | = left:LValue __ "=" __ right:ExpressionList { 525 | return {type: "Definition", location: location(), left: left, right: right}; 526 | } 527 | 528 | LValue 529 | = head:Identifier 530 | tail:(__ "," __ Identifier)* { 531 | return makeList([head], tail, 3); 532 | } 533 | 534 | IfExpression 535 | = IfToken __ condition:Expression __ thenBranch:WrappedExpressions 536 | elseIfs:(ElseIfs)? __ ElseToken __ elseBranch:WrappedExpressions { 537 | return { 538 | type: "If", 539 | location: location(), 540 | condition: condition, 541 | thenBranch: thenBranch, 542 | elseIfs: elseIfs ? elseIfs : [], 543 | elseBranch: elseBranch 544 | }; 545 | } 546 | 547 | ElseIfs 548 | = head:ElseIf 549 | tail:(__ ElseIf)* { 550 | return makeList([head], tail, 1); 551 | } 552 | 553 | ElseIf 554 | = ElseIfToken __ condition:Expression __ branch:WrappedExpressions { 555 | return { 556 | type: "ElseIf", 557 | location: location(), 558 | condition: condition, 559 | branch: branch 560 | }; 561 | } 562 | 563 | LoopExpression 564 | = LoopToken __ 565 | range:RangeGenerator? 566 | init:LoopInit? 567 | preCondition:LoopCondition? 568 | (RepeatToken __)? 569 | body:WrappedDefintions? 570 | postCondition:LoopCondition? __ 571 | ReturnsToken __ returns:ExpressionList __ { 572 | return { 573 | type: "Loop", 574 | location: location(), 575 | range: range, 576 | init: init ? init : [], 577 | preCondition: preCondition, 578 | postCondition: postCondition, 579 | body: body ? body : [], 580 | returns: returns 581 | }; 582 | } 583 | 584 | LoopCondition 585 | = WhileToken __ expression:Expression __ { 586 | return expression; 587 | } 588 | 589 | LoopInit 590 | = InitialToken __ definitions:WrappedDefintions { 591 | return definitions; 592 | } 593 | 594 | RangeGenerator 595 | = names:LValue __ InToken __ ranges:ExpressionList __ { 596 | return {type: "RangeList", location: location(), names: names, ranges: ranges }; 597 | } 598 | -------------------------------------------------------------------------------- /src/graphml/index.ts: -------------------------------------------------------------------------------- 1 | import { Port } from "../ir1/ports/port"; 2 | import { printPortData } from "../print"; 3 | 4 | export function makeNode(id: string, name: string, location: string, inPorts: number, 5 | outPorts: number, subGraph: string = ""): string { 6 | const genFunction = (prefix: string, quantity: number) => { 7 | return (Array.from(new Array(quantity), 8 | (val, index) => "").join("")); 9 | }; 10 | 11 | return ` 12 | ${name} 13 | ${location}` + 14 | genFunction("in", inPorts) + 15 | genFunction("out", outPorts) + 16 | subGraph + 17 | ""; 18 | } 19 | 20 | export function makeEdge(idFrom: string, portFrom: number, 21 | idTo: string, portTo: number, port: Port, 22 | inner: boolean = false): string { 23 | let portVal = ""; 24 | printPortData(port, (s: string) => portVal += s); 25 | return ` 27 | ${portVal} 28 | `; 29 | } 30 | 31 | export function makeDocument(contents: string): string { 32 | return ` 33 | 37 | 38 | 39 | ${contents} 40 | `; 41 | } 42 | 43 | export function makeGraph(id: string, contents: string): string { 44 | return `${contents}`; 45 | } 46 | -------------------------------------------------------------------------------- /src/ir1/create.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../ast"; 2 | import * as ASTTypes from "../ast/types"; 3 | import * as Nodes from "./nodes"; 4 | import * as TypeNodes from "./nodes/types"; 5 | import { Scope } from "./scopes/scope"; 6 | 7 | function nodeFromPostfix(postfix: AST.Postfix, scope: Scope): Nodes.Node { 8 | let node = nodeFromExpression(postfix.base, scope); 9 | for (const operation of postfix.operationList) { 10 | if (AST.isFunctionCall(operation)) { 11 | node = new Nodes.FunctionCall(node, operation, scope); 12 | continue; 13 | } 14 | if (AST.isRecordAccess(operation)) { 15 | node = new Nodes.RecordAccess(node, operation, scope); 16 | continue; 17 | } 18 | if (AST.isArrayAccess(operation)) { 19 | node = new Nodes.ArrayAccess(node, operation, scope); 20 | continue; 21 | } 22 | throw new Error("Unexpected postfix operation " + JSON.stringify(operation)); 23 | } 24 | return node; 25 | } 26 | 27 | function nodeFromTypeValue(node: ASTTypes.TypeValue, scope: Scope): Nodes.Node { 28 | if (ASTTypes.isIntegerType(node) || ASTTypes.isFloatType(node) || 29 | ASTTypes.isBooleanType(node) || ASTTypes.isStringType(node)) { 30 | return new TypeNodes.LiteralType(node); 31 | } 32 | if (ASTTypes.isArrayType(node)) { 33 | return new TypeNodes.ArrayType(node, scope); 34 | } 35 | if (ASTTypes.isFunctionType(node)) { 36 | return new TypeNodes.FunctionType(node, scope); 37 | } 38 | if (ASTTypes.isRecordType(node)) { 39 | return new TypeNodes.RecordType(node, scope); 40 | } 41 | if (ASTTypes.isStreamType(node)) { 42 | return new TypeNodes.StreamType(node, scope); 43 | } 44 | throw new Error("Unexpected expression type value " + JSON.stringify(node)); 45 | } 46 | 47 | export function nodeFromExpression(expression: AST.Expression, scope: Scope): Nodes.Node { 48 | if (typeof expression === "string") { 49 | return new Nodes.Identifier(expression, scope); 50 | } 51 | if (AST.isLiteral(expression)) { 52 | return new Nodes.Literal(expression); 53 | } 54 | if (AST.isArrayValue(expression)) { 55 | return new Nodes.ArrayValue(expression, scope); 56 | } 57 | if (AST.isStreamValue(expression)) { 58 | return new Nodes.StreamValue(expression, scope); 59 | } 60 | if (AST.isRecordValue(expression)) { 61 | return new Nodes.RecordValue(expression, scope); 62 | } 63 | if (ASTTypes.isTypeValue(expression)) { 64 | return nodeFromTypeValue(expression, scope); 65 | } 66 | if (AST.isBinaryExpression(expression)) { 67 | return new Nodes.BinaryExpression(expression, scope); 68 | } 69 | if (AST.isUnaryExpression(expression)) { 70 | return new Nodes.UnaryExpression(expression, scope); 71 | } 72 | if (AST.isPostfix(expression)) { 73 | return nodeFromPostfix(expression, scope); 74 | } 75 | if (AST.isOldValue(expression)) { 76 | return new Nodes.OldValue(expression, scope); 77 | } 78 | if (AST.isLetExpression(expression)) { 79 | return new Nodes.LetExpression(expression, scope); 80 | } 81 | if (AST.isLoopExpression(expression)) { 82 | return new Nodes.LoopExpression(expression, scope); 83 | } 84 | if (AST.isIfExpression(expression)) { 85 | return new Nodes.IfExpression(expression, scope); 86 | } 87 | if (AST.isFunctionValue(expression)) { 88 | return new Nodes.FunctionValue(expression, scope); 89 | } 90 | throw new Error("Unexpected expression type in create " + JSON.stringify(expression)); 91 | } 92 | -------------------------------------------------------------------------------- /src/ir1/nodes/array.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast/composite"; 2 | import { nodeFromExpression } from "../create"; 3 | import { SingleValuePort } from "../ports/single"; 4 | import { Scope } from "../scopes/scope"; 5 | import * as Types from "../types"; 6 | import { checkType } from "../types/check"; 7 | import * as Values from "../values"; 8 | import { Node } from "./node"; 9 | 10 | export class ArrayValue extends Node { 11 | private nodes: Node[]; 12 | 13 | constructor(defintion: AST.ArrayValue, scope: Scope) { 14 | super("Array", defintion); 15 | this.nodes = []; 16 | for (const expression of defintion.contents) { 17 | this.nodes.push(nodeFromExpression(expression, scope)); 18 | } 19 | 20 | this.addInPorts(this.nodes); 21 | 22 | this.outPorts = [new SingleValuePort((type: Types.ReadyType) => this.fetchData(type))]; 23 | } 24 | 25 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 26 | if (!checkType(new Types.Array(new Types.Some()), type)) { 27 | return new Values.ErrorValue("Incompartible type, not array"); 28 | } 29 | 30 | const results: Values.ReadyValue[] = []; 31 | let subType: Types.ReadyType; 32 | 33 | if (type instanceof Types.Array) { 34 | subType = type.element; 35 | } else { 36 | subType = new Types.Some(); 37 | } 38 | 39 | for (const port of this.inPorts) { 40 | results.push(Values.fetchComplete(port, subType)); 41 | } 42 | 43 | return new Values.Array(results); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/ir1/nodes/arrayAccess.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast/expression"; 2 | import { nodeFromExpression } from "../create"; 3 | import { SingleValuePort } from "../ports/single"; 4 | import { Scope } from "../scopes/scope"; 5 | import * as Types from "../types"; 6 | import { checkType } from "../types/check"; 7 | import * as Values from "../values"; 8 | import { Node } from "./node"; 9 | 10 | export class ArrayAccess extends Node { 11 | private nodes: Node[]; 12 | 13 | constructor(arraySource: Node, defintion: AST.ArrayAccess, scope: Scope) { 14 | super("ArrayAccess", defintion); 15 | this.nodes = []; 16 | this.nodes.push(arraySource); 17 | this.nodes.push(nodeFromExpression(defintion.index, scope)); 18 | 19 | this.addInPorts(this.nodes); 20 | 21 | this.outPorts = [new SingleValuePort((type: Types.ReadyType) => this.fetchData(type))]; 22 | } 23 | 24 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 25 | const array = this.inPorts[0].getData(new Types.Array(new Types.Some())); 26 | const index = this.inPorts[1].getData(new Types.Integer()); 27 | 28 | if (array instanceof Values.ErrorValue || index instanceof Values.ErrorValue) { 29 | return new Values.ErrorValue("Cannot fetch array element"); 30 | } 31 | const arrayType = array.type as Types.Array; 32 | const subType = arrayType.element; 33 | 34 | if (!checkType(subType, type)) { 35 | return new Values.ErrorValue("Incompartible type: " + 36 | subType.toString() + ", " + type.toString()); 37 | } 38 | 39 | const integerIndex = index as Values.Integer; 40 | const arrayValue = array as Values.Array; 41 | 42 | if (!arrayValue.values[integerIndex.value]) { 43 | return new Values.ErrorValue("Array index is out of bounds: " + integerIndex.value); 44 | } 45 | 46 | return arrayValue.values[integerIndex.value]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/ir1/nodes/binary.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import { nodeFromExpression } from "../create"; 3 | import { StreamPort } from "../ports/stream"; 4 | import { Scope } from "../scopes/scope"; 5 | import * as Types from "../types"; 6 | import * as Values from "../values"; 7 | import { Node } from "./node"; 8 | 9 | export class BinaryExpression extends Node { 10 | private nodes: Node[]; 11 | private operator: string; 12 | 13 | constructor(definition: AST.BinaryExpression, scope: Scope) { 14 | super("Binary", definition); 15 | this.operator = definition.operator; 16 | 17 | this.nodes = []; 18 | this.nodes.push(nodeFromExpression(definition.left, scope)); 19 | this.nodes.push(nodeFromExpression(definition.right, scope)); 20 | this.addInPorts(this.nodes); 21 | 22 | this.outPorts.push(new StreamPort((type: Types.ReadyType, offset: number) => this.fetchData(type, offset))); 23 | } 24 | 25 | private fetchData(dataType: Types.ReadyType, offset: number): Values.ReadyValue { 26 | let type = dataType; 27 | if (["+", "-", "*", "/", "%"].indexOf(this.operator) === -1) { 28 | type = new Types.Some(); 29 | } 30 | const left: Values.ReadyValue = this.inPorts[0].getData(type, offset); 31 | const right: Values.ReadyValue = this.inPorts[1].getData(type, offset); 32 | 33 | if (!Types.checkType(left.type, right.type)) { 34 | return new Values.ErrorValue("Types should be equal for binary operation " + 35 | JSON.stringify(left.type) + " " + JSON.stringify(right.type)); 36 | } 37 | 38 | return this.processPair(left, right); 39 | } 40 | 41 | private processPair(left: Values.ReadyValue, right: Values.ReadyValue): Values.ReadyValue { 42 | if (left.type instanceof Types.Integer) { 43 | return this.processIntPair(left as Values.Integer, right as Values.Integer); 44 | } 45 | 46 | if (left.type instanceof Types.Float) { 47 | return this.processFloatPair(left as Values.Float, right as Values.Float); 48 | } 49 | 50 | if (left.type instanceof Types.Boolean) { 51 | return this.processBooelanPair(left as Values.Boolean, right as Values.Boolean); 52 | } 53 | 54 | if (left instanceof Values.StreamEnd) { 55 | return left; 56 | } 57 | 58 | if ((left instanceof Values.StreamElement) && 59 | (right instanceof Values.StreamElement)) { 60 | return new Values.StreamElement(this.processPair(left.value, right.value)); 61 | } 62 | 63 | return new Values.ErrorValue("Unsupported types " + left.type.name); 64 | } 65 | 66 | private processBooelanPair(left: Values.Boolean, right: Values.Boolean): Values.ReadyValue { 67 | if (this.operator === "|") { 68 | return new Values.Boolean(left.value || right.value); 69 | } 70 | if (this.operator === "&") { 71 | return new Values.Boolean(left.value && right.value); 72 | } 73 | return new Values.ErrorValue("Unknown operation " + this.operator); 74 | } 75 | 76 | private processIntPair(left: Values.Integer, right: Values.Integer): Values.ReadyValue { 77 | if (this.operator === "+") { 78 | return new Values.Integer(left.value + right.value); 79 | } 80 | if (this.operator === "-") { 81 | return new Values.Integer(left.value - right.value); 82 | } 83 | if (this.operator === "*") { 84 | return new Values.Integer(left.value * right.value); 85 | } 86 | if (this.operator === "/") { 87 | return new Values.Integer(Math.trunc(left.value / right.value)); 88 | } 89 | if (this.operator === "%") { 90 | return new Values.Integer(left.value % right.value); 91 | } 92 | if (this.operator === "<") { 93 | return new Values.Boolean(left.value < right.value); 94 | } 95 | if (this.operator === ">") { 96 | return new Values.Boolean(left.value > right.value); 97 | } 98 | if (this.operator === "<=") { 99 | return new Values.Boolean(left.value <= right.value); 100 | } 101 | if (this.operator === ">=") { 102 | return new Values.Boolean(left.value >= right.value); 103 | } 104 | if (this.operator === "=") { 105 | return new Values.Boolean(left.value === right.value); 106 | } 107 | if (this.operator === "/=") { 108 | return new Values.Boolean(left.value !== right.value); 109 | } 110 | return new Values.ErrorValue("Unknown operation " + this.operator); 111 | } 112 | 113 | private processFloatPair(left: Values.Float, right: Values.Float): Values.ReadyValue { 114 | if (this.operator === "+") { 115 | return new Values.Float(left.value + right.value); 116 | } 117 | if (this.operator === "-") { 118 | return new Values.Float(left.value - right.value); 119 | } 120 | if (this.operator === "*") { 121 | return new Values.Float(left.value * right.value); 122 | } 123 | if (this.operator === "/") { 124 | return new Values.Float(left.value / right.value); 125 | } 126 | if (this.operator === "<") { 127 | return new Values.Boolean(left.value < right.value); 128 | } 129 | if (this.operator === ">") { 130 | return new Values.Boolean(left.value > right.value); 131 | } 132 | if (this.operator === "<=") { 133 | return new Values.Boolean(left.value <= right.value); 134 | } 135 | if (this.operator === ">=") { 136 | return new Values.Boolean(left.value >= right.value); 137 | } 138 | if (this.operator === "=") { 139 | return new Values.Boolean(left.value === right.value); 140 | } 141 | if (this.operator === "/=") { 142 | return new Values.Boolean(left.value !== right.value); 143 | } 144 | return new Values.ErrorValue("Unknown operation " + this.operator); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/ir1/nodes/function.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import { nodeFromExpression } from "../create"; 3 | import { SingleValuePort } from "../ports/single"; 4 | import { FlatScope } from "../scopes/flat"; 5 | import { Scope } from "../scopes/scope"; 6 | import * as Types from "../types"; 7 | import * as Values from "../values"; 8 | import { Node } from "./node"; 9 | import { SomeType } from "./types/some"; 10 | 11 | export class FunctionValue extends Node { 12 | private params: Map; 13 | private returns: Node[]; 14 | private bodyFactory: Values.BodyFactory; 15 | private scope: Scope; 16 | 17 | constructor(definition: AST.FunctionValue, s: Scope) { 18 | super("Lambda", definition); 19 | this.scope = new FlatScope(s); 20 | 21 | this.bodyFactory = (scope: Scope) => { 22 | const nodes: Node[] = []; 23 | for (const expression of definition.body) { 24 | nodes.push(nodeFromExpression(expression, scope)); 25 | } 26 | return nodes; 27 | }; 28 | 29 | this.params = new Map(); 30 | for (const field of definition.params) { 31 | const type: Node = field.dataType ? nodeFromExpression(field.dataType, s) 32 | : new SomeType(); 33 | if (type.outPorts.length !== 1) { 34 | throw new Error("Each type of the signature should produce exactly one output"); 35 | } 36 | 37 | this.params.set(field.name, type); 38 | } 39 | 40 | this.returns = []; 41 | for (const expression of definition.returns) { 42 | const node = nodeFromExpression(expression, this.scope); 43 | if (node.outPorts.length !== 1) { 44 | throw new Error("Each type of the signature should produce exactly one output"); 45 | } 46 | this.returns.push(node); 47 | } 48 | while (this.returns.length < definition.body.length) { 49 | this.returns.push(new SomeType()); 50 | } 51 | this.outPorts.push(new SingleValuePort((type: Types.ReadyType) => this.fetchData(type))); 52 | } 53 | 54 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 55 | const params: Types.ReadyType[] = []; 56 | const names: string[] = []; 57 | 58 | this.params.forEach((value: Node, key: string) => { 59 | names.push(key); 60 | params.push((value.outPorts[0].getData(new Types.Type()) as Values.Type).value); 61 | }); 62 | 63 | const returns: Types.ReadyType[] = []; 64 | for (const node of this.returns) { 65 | returns.push( 66 | (node.outPorts[0].getData(new Types.Type()) as Values.Type).value); 67 | } 68 | const readyType = new Types.Function(params, returns); 69 | 70 | if (!Types.checkType(readyType, type)) { 71 | return new Values.ErrorValue("Incompartible type, not type"); 72 | } 73 | 74 | return new Values.Function(readyType, names, this.bodyFactory, this.scope); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/ir1/nodes/functionCall.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast/expression"; 2 | import * as GML from "../../graphml/"; 3 | import { nodeFromExpression } from "../create"; 4 | import { StreamPort } from "../ports/stream"; 5 | import { FlatScope } from "../scopes/flat"; 6 | import { Scope } from "../scopes/scope"; 7 | import * as Types from "../types"; 8 | import { checkType } from "../types/check"; 9 | import * as Values from "../values"; 10 | import { Node, subGraphML } from "./node"; 11 | 12 | export class FunctionCall extends Node { 13 | private nodes: Node[]; 14 | private functionBody: Node[]; 15 | private callScope?: FlatScope; 16 | 17 | constructor(functionSource: Node, defintion: AST.FunctionCall, scope: Scope) { 18 | super("FunctionCall", defintion); 19 | this.nodes = []; 20 | this.nodes.push(functionSource); 21 | 22 | for (const expression of defintion.arguments) { 23 | this.nodes.push(nodeFromExpression(expression, scope)); 24 | } 25 | 26 | this.addInPorts(this.nodes); 27 | this.functionBody = []; 28 | this.outPorts = []; 29 | } 30 | 31 | public requestPorts(portNum: number): number { 32 | for (let i = 0; i < portNum; i++) { 33 | this.outPorts = [new StreamPort((type: Types.ReadyType, offset: number) => this.fetchData(i, type, offset))]; 34 | } 35 | return portNum; 36 | } 37 | 38 | public fetchData(portNum: number, type: Types.ReadyType, offset: number): Values.ReadyValue { 39 | if (this.callScope) { 40 | return this.fetchReadyData(portNum, type, offset); 41 | } 42 | 43 | const args: Types.ReadyType[] = []; 44 | 45 | for (let i = 1; i < this.inPorts.length; i++) { 46 | args.push(this.inPorts[i].getData(new Types.Some()).type); 47 | } 48 | 49 | const returns: Types.ReadyType[] = this.outPorts.map(() => new Types.Some()); 50 | returns[portNum] = type; 51 | 52 | const value: Values.ReadyValue = this.inPorts[0].getData(new Types.Function(args, returns)); 53 | 54 | if (value instanceof Values.ErrorValue) { 55 | return new Values.ErrorValue("Cannot fetch function body"); 56 | } 57 | 58 | const functionValue = value as Values.Function; 59 | this.callScope = new FlatScope(functionValue.scope); 60 | 61 | this.functionBody = functionValue.bodyFactory(this.callScope); 62 | 63 | for (let i = 0; i < functionValue.argumentNames.length; i++) { 64 | this.callScope.inject(functionValue.argumentNames[i], this.inPorts[i + 1]); 65 | } 66 | 67 | return this.fetchReadyData(portNum, type, offset); 68 | } 69 | 70 | public graphML(): string { 71 | return this.graphMLInternal(GML.makeGraph("function", subGraphML(this.functionBody))); 72 | } 73 | 74 | private fetchReadyData(portNum: number, type: Types.ReadyType, offset: number): Values.ReadyValue { 75 | return this.functionBody[portNum].outPorts[0].getData(type, offset); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/ir1/nodes/identifier.ts: -------------------------------------------------------------------------------- 1 | import { StreamPort } from "../ports/stream"; 2 | import { Scope } from "../scopes/scope"; 3 | import { ReadyType } from "../types/ready"; 4 | import { Node } from "./node"; 5 | 6 | export class Identifier extends Node { 7 | public constructor(name: string, scope: Scope) { 8 | super("Identifier"); 9 | this.outPorts = [new StreamPort( 10 | (type: ReadyType, offset: number) => scope.resolve(name, type, offset))]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/ir1/nodes/if.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import { nodeFromExpression } from "../create"; 3 | import { StreamPort } from "../ports/stream"; 4 | import { Scope } from "../scopes/scope"; 5 | import * as Types from "../types"; 6 | import * as Values from "../values"; 7 | import { IfBranch } from "./ifBranch"; 8 | import { Node, subGraphML } from "./node"; 9 | 10 | export class IfExpression extends Node { 11 | private branches: IfBranch[]; 12 | 13 | constructor(definition: AST.IfExpression, scope: Scope) { 14 | super("If", definition); 15 | this.branches = []; 16 | this.branches.push(new IfBranch(definition.condition, definition.thenBranch, scope)); 17 | 18 | for (const branch of definition.elseIfs) { 19 | this.branches.push(new IfBranch(branch.condition, branch.branch, scope)); 20 | } 21 | this.branches.push(new IfBranch({type: "BooleanLiteral", value: true} as AST.Expression, 22 | definition.elseBranch, scope)); 23 | 24 | for (let i = 0; i < definition.thenBranch.length; i++) { 25 | this.outPorts.push(new StreamPort( 26 | (type: Types.ReadyType, offset: number) => this.fetchPort(i, type, offset))); 27 | } 28 | } 29 | 30 | public graphML(): string { 31 | return subGraphML(this.branches); 32 | } 33 | 34 | private fetchPort(portNum: number, dataType: Types.ReadyType, offset: number): Values.ReadyValue { 35 | for (const branch of this.branches) { 36 | const condition = branch.inPorts[0].getData(new Types.Boolean()) as Values.Boolean; 37 | if (condition.value) { 38 | return branch.inPorts[portNum + 1].getData(dataType, offset); 39 | } 40 | } 41 | throw new Error("This should never happen"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ir1/nodes/ifBranch.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import { nodeFromExpression } from "../create"; 3 | import { StreamPort } from "../ports/stream"; 4 | import { Scope } from "../scopes/scope"; 5 | import * as Types from "../types"; 6 | import * as Values from "../values"; 7 | import { Node } from "./node"; 8 | 9 | export class IfBranch extends Node { 10 | private nodes: Node[]; 11 | 12 | constructor(condition: AST.Expression, values: AST.Expression[], scope: Scope) { 13 | super("IfBranch"); 14 | this.nodes = []; 15 | this.nodes.push(nodeFromExpression(condition, scope)); 16 | for (const expression of values) { 17 | this.nodes.push(nodeFromExpression(expression, scope)); 18 | } 19 | this.addInPorts(this.nodes); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ir1/nodes/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./array"; 2 | export * from "./arrayAccess"; 3 | export * from "./binary"; 4 | export * from "./function"; 5 | export * from "./functionCall"; 6 | export * from "./identifier"; 7 | export * from "./if"; 8 | export * from "./ifBranch"; 9 | export * from "./let"; 10 | export * from "./literal"; 11 | export * from "./loop"; 12 | export * from "./node"; 13 | export * from "./old"; 14 | export * from "./ready"; 15 | export * from "./record"; 16 | export * from "./recordAccess"; 17 | export * from "./stream"; 18 | export * from "./unary"; 19 | -------------------------------------------------------------------------------- /src/ir1/nodes/let.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import { nodeFromExpression } from "../create"; 3 | import { StreamPort } from "../ports/stream"; 4 | import { FlatScope } from "../scopes/flat"; 5 | import { Scope } from "../scopes/scope"; 6 | import * as Types from "../types"; 7 | import * as Values from "../values"; 8 | import { Node } from "./node"; 9 | 10 | export class LetExpression extends Node { 11 | private scope: FlatScope; 12 | private nodes: Node[]; 13 | 14 | constructor(definition: AST.LetExpression, scope: Scope) { 15 | super("Let", definition); 16 | this.scope = new FlatScope(scope); 17 | this.scope.addFromAST(definition.definitions); 18 | 19 | this.nodes = []; 20 | for (const expression of definition.expressions) { 21 | this.nodes.push(nodeFromExpression(expression, this.scope)); 22 | } 23 | 24 | for (const node of this.nodes) { 25 | for (const port of node.outPorts) { 26 | this.outPorts.push(port); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ir1/nodes/literal.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import * as ASTTypes from "../../ast/types"; 3 | import { SingleValuePort } from "../ports/single"; 4 | import * as Types from "../types"; 5 | import * as Values from "../values"; 6 | import { Node } from "./node"; 7 | 8 | export class Literal extends Node { 9 | 10 | constructor(defintion: AST.Literal) { 11 | super("Literal", defintion); 12 | 13 | if (!AST.isLiteral(defintion)) { 14 | throw new Error("Trying to construct literal from " + JSON.stringify(defintion)); 15 | } 16 | let value: Values.ReadyValue; 17 | 18 | if (AST.isBooleanLiteral(defintion)) { 19 | value = new Values.Boolean(defintion.value); 20 | } else if (AST.isIntegerLiteral(defintion)) { 21 | value = new Values.Integer(defintion.value); 22 | } else if (AST.isFloatLiteral(defintion)) { 23 | value = new Values.Float(defintion.value); 24 | } else if (AST.isStringLiteral(defintion)) { 25 | value = new Values.String(defintion.value); 26 | } else { 27 | throw new Error("Unexpected literal type " + JSON.stringify(defintion)); 28 | } 29 | 30 | this.outPorts.push(new SingleValuePort((dataType: Types.ReadyType) => value)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ir1/nodes/loop.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import { nodeFromExpression } from "../create"; 3 | import { StreamPort } from "../ports/stream"; 4 | import { LoopScope } from "../scopes/loop"; 5 | import { Scope } from "../scopes/scope"; 6 | import * as Types from "../types"; 7 | import * as Values from "../values"; 8 | import { IfBranch } from "./ifBranch"; 9 | import { Node } from "./node"; 10 | 11 | export class LoopExpression extends Node { 12 | private nodes: Node[]; 13 | private scope: LoopScope; 14 | 15 | constructor(definition: AST.LoopExpression, scope: Scope) { 16 | super("LoopExpression", definition); 17 | this.scope = new LoopScope(scope, definition.body, definition.range, definition.init, 18 | definition.preCondition, definition.postCondition); 19 | 20 | this.nodes = []; 21 | for (const expression of definition.returns) { 22 | this.nodes.push(nodeFromExpression(expression, this.scope)); 23 | } 24 | 25 | for (const node of this.nodes) { 26 | node.requestPorts(1); 27 | for (const port of node.outPorts) { 28 | this.outPorts.push(port); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ir1/nodes/node.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import * as GML from "../../graphml/"; 3 | import { Port } from "../ports/port"; 4 | 5 | export class Node { 6 | private static lastId: number = 0; 7 | 8 | public name: string; 9 | public location: string; 10 | public outPorts: Port[]; 11 | public inPorts: Port[]; 12 | public siblings: Node[]; 13 | public id: string; 14 | 15 | constructor(name: string, ast?: AST.Node) { 16 | Node.lastId++; 17 | 18 | this.name = name; 19 | if (ast && ast.location) { 20 | const locString = (position: AST.Position) => { 21 | return String(position.line) + ":" + String(position.column); 22 | }; 23 | this.location = locString(ast.location.start) + "-" + 24 | locString(ast.location.end); 25 | } else { 26 | this.location = "not applicable"; 27 | } 28 | this.outPorts = []; 29 | this.inPorts = []; 30 | this.siblings = []; 31 | this.id = "node" + String(Node.lastId); 32 | } 33 | 34 | public requestPorts(portNum: number): number { 35 | return this.outPorts.length; 36 | } 37 | 38 | public graphML(): string { 39 | return this.graphMLInternal(""); 40 | } 41 | 42 | public getInEdges(): Array<[Node, number]> { 43 | const edges: Array<[Node, number]> = []; 44 | for (const node of this.siblings) { 45 | edges.push([node, 0]); 46 | } 47 | return edges; 48 | } 49 | 50 | protected addInPorts(nodes: Node[]): void { 51 | for (const node of nodes) { 52 | if (node.requestPorts(1) !== 1) { 53 | throw new Error("Each part should produce exactly one output"); 54 | } 55 | this.siblings.push(node); 56 | this.inPorts.push(node.outPorts[0]); 57 | } 58 | } 59 | 60 | protected graphMLInternal(subGraph: string): string { 61 | return GML.makeNode(this.id, this.name, this.location, this.inPorts.length, this.outPorts.length, subGraph); 62 | } 63 | } 64 | 65 | export function subGraphML(nodes: Node[]): string { 66 | const component = new Map(); 67 | const edges = new Array<[string, number, string, number, Port]>(); 68 | 69 | let nextNodes = nodes; 70 | while (nextNodes.length > 0) { 71 | const newNextNodes = new Array(); 72 | for (const node of nextNodes) { 73 | if (component.has(node.id)) { 74 | continue; 75 | } 76 | 77 | component.set(node.id, node); 78 | const nodeEdges = node.getInEdges(); 79 | for (let i = 0; i < nodeEdges.length; i++) { 80 | edges.push([nodeEdges[i][0].id, nodeEdges[i][1], node.id, i, nodeEdges[i][0].outPorts[0]]); 81 | if (!component.has(nodeEdges[i][0].id)) { 82 | newNextNodes.push(nodeEdges[i][0]); 83 | } 84 | } 85 | } 86 | nextNodes = newNextNodes; 87 | } 88 | 89 | return Array.from(component.values(), (c) => c.graphML()).join("") + 90 | edges.map((e) => GML.makeEdge(e[0], e[1], e[2], e[3], e[4])).join(""); 91 | } 92 | -------------------------------------------------------------------------------- /src/ir1/nodes/old.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import { StreamPort } from "../ports/stream"; 3 | import { Scope } from "../scopes/scope"; 4 | import { ReadyType } from "../types/ready"; 5 | import { Node } from "./node"; 6 | 7 | export class OldValue extends Node { 8 | public constructor(definition: AST.OldValue, scope: Scope) { 9 | super("OldValue", definition); 10 | this.outPorts = [new StreamPort( 11 | (type: ReadyType, offset: number) => scope.resolve(definition.id, type, offset - 1))]; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ir1/nodes/ready.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import * as ASTTypes from "../../ast/types"; 3 | import { SingleValuePort } from "../ports/single"; 4 | import * as Types from "../types"; 5 | import * as Values from "../values"; 6 | import { Node } from "./node"; 7 | 8 | export class ReadyLiteral extends Node { 9 | 10 | constructor(value: Values.ReadyValue) { 11 | super("Literal"); 12 | this.outPorts.push(new SingleValuePort((dataType: Types.ReadyType) => value)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ir1/nodes/record.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast/composite"; 2 | import { nodeFromExpression } from "../create"; 3 | import { Port } from "../ports/port"; 4 | import { SingleValuePort } from "../ports/single"; 5 | import { FlatScope } from "../scopes/flat"; 6 | import { Scope } from "../scopes/scope"; 7 | import * as Types from "../types"; 8 | import { ReadyType } from "../types/ready"; 9 | import * as Values from "../values"; 10 | import { ErrorValue } from "../values/error"; 11 | import { ReadyValue } from "../values/ready"; 12 | import { Node } from "./node"; 13 | 14 | export class RecordValue extends Node { 15 | private scope: FlatScope; 16 | 17 | constructor(record: AST.RecordValue, scope: Scope) { 18 | super("Record"); 19 | this.scope = new FlatScope(scope); 20 | this.scope.addFromAST(record.contents); 21 | 22 | this.scope.definitions.forEach((ports: Port[], key: string) => { 23 | if (ports.length !== 1) { 24 | throw new Error("Polymorphism among record fields is not allowed"); 25 | } 26 | }); 27 | 28 | this.outPorts = [new SingleValuePort((dataType: Types.ReadyType) => this.fetchData(dataType))]; 29 | } 30 | 31 | public fetchData(type: ReadyType): ReadyValue { 32 | if (!Types.checkType(new Types.Record(new Map()), type)) { 33 | return new Values.ErrorValue("Incompartible type, not record"); 34 | } 35 | const subTypes: Map = new Map(); 36 | this.scope.definitions.forEach((ports: Port[], key: string) => { 37 | subTypes.set(key, new Types.Some()); 38 | }); 39 | if (type instanceof Types.Record) { 40 | type.elements.forEach((value: ReadyType, key: string) => { 41 | subTypes.set(key, value); 42 | }); 43 | } 44 | const values: Map = new Map(); 45 | this.scope.definitions.forEach((ports: Port[], key: string) => { 46 | values.set(key, Values.fetchComplete(ports[0], subTypes.get(key)!)); 47 | subTypes.set(key, values.get(key)!.type); 48 | }); 49 | return new Values.Record(new Types.Record(subTypes), values); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/ir1/nodes/recordAccess.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast/expression"; 2 | import { nodeFromExpression } from "../create"; 3 | import { SingleValuePort } from "../ports/single"; 4 | import { Scope } from "../scopes/scope"; 5 | import * as Types from "../types"; 6 | import { checkType } from "../types/check"; 7 | import * as Values from "../values"; 8 | import { Node } from "./node"; 9 | 10 | export class RecordAccess extends Node { 11 | private nodes: Node[]; 12 | private field: string; 13 | 14 | constructor(recordSource: Node, defintion: AST.RecordAccess, scope: Scope) { 15 | super("RecordAccess", defintion); 16 | this.nodes = []; 17 | this.nodes.push(recordSource); 18 | this.addInPorts(this.nodes); 19 | this.field = defintion.field; 20 | 21 | this.outPorts = [new SingleValuePort((type: Types.ReadyType) => this.fetchData(type))]; 22 | } 23 | 24 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 25 | const record = this.inPorts[0].getData(new Types.Record(new Map())); 26 | 27 | if (record instanceof Values.ErrorValue) { 28 | return new Values.ErrorValue("Cannot fetch record field, not a record given"); 29 | } 30 | const recordValue = record as Values.Record; 31 | 32 | if (!recordValue.values.has(this.field)) { 33 | return new Values.ErrorValue("Record has no field " + this.field); 34 | } 35 | const fieldValue = recordValue.values.get(this.field)!; 36 | 37 | if (!checkType(fieldValue.type, type)) { 38 | return new Values.ErrorValue("Incompartible type: " + 39 | fieldValue.type.toString() + ", " + type.toString()); 40 | } 41 | 42 | return fieldValue; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ir1/nodes/stream.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast/composite"; 2 | import { nodeFromExpression } from "../create"; 3 | import { StreamPort } from "../ports/stream"; 4 | import { Scope } from "../scopes/scope"; 5 | import * as Types from "../types"; 6 | import * as Values from "../values"; 7 | import { Node } from "./node"; 8 | 9 | export class StreamValue extends Node { 10 | private nodes: Node[]; 11 | private leftBound?: Values.Integer; 12 | private rightBound?: Values.Integer; 13 | 14 | constructor(defintion: AST.StreamValue, scope: Scope) { 15 | super("StreamValue", defintion); 16 | this.nodes = []; 17 | this.nodes.push(nodeFromExpression(defintion.lowerBound, scope)); 18 | 19 | if (defintion.upperBound) { 20 | this.nodes.push(nodeFromExpression(defintion.upperBound, scope)); 21 | } 22 | this.addInPorts(this.nodes); 23 | 24 | this.outPorts = [new StreamPort((type: Types.ReadyType, offset: number) => this.fetchData(type, offset))]; 25 | } 26 | 27 | private fetchData(dataType: Types.ReadyType, offset: number): Values.ReadyValue { 28 | if (!Types.checkType(new Types.Stream(new Types.Some()), dataType)) { 29 | return new Values.ErrorValue("Incompartible type, not stream"); 30 | } 31 | if (!this.leftBound) { 32 | this.leftBound = this.inPorts[0].getData(new Types.Integer()) as Values.Integer; 33 | if (this.inPorts.length > 1) { 34 | this.rightBound = this.inPorts[1].getData(new Types.Integer()) as Values.Integer; 35 | } 36 | } 37 | if (this.rightBound && (this.leftBound.value + offset > this.rightBound.value)) { 38 | return new Values.StreamEnd(); 39 | } 40 | return new Values.StreamElement(new Values.Integer(this.leftBound.value + offset)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/ir1/nodes/types/array.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../../ast"; 2 | import * as ASTTypes from "../../../ast/types"; 3 | import { nodeFromExpression } from "../../create"; 4 | import { SingleValuePort } from "../../ports/single"; 5 | import { Scope } from "../../scopes/scope"; 6 | import * as Types from "../../types"; 7 | import { checkType } from "../../types/check"; 8 | import * as Values from "../../values"; 9 | import { Node } from "../node"; 10 | 11 | export class ArrayType extends Node { 12 | private nodes: Node[]; 13 | 14 | constructor(defintion: ASTTypes.ArrayType, scope: Scope) { 15 | super("ArrayType", defintion); 16 | 17 | this.nodes = []; 18 | this.nodes.push(nodeFromExpression(defintion.elementType, scope)); 19 | 20 | this.addInPorts(this.nodes); 21 | 22 | this.outPorts = [new SingleValuePort(this.fetchData)]; 23 | } 24 | 25 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 26 | if (!checkType(new Types.Type(), type)) { 27 | return new Values.ErrorValue("Incompartible type, not type"); 28 | } 29 | 30 | const result = this.inPorts[0].getData(new Types.Type()) as Values.Type; 31 | return new Values.Type(new Types.Array(result.value)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ir1/nodes/types/function.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../../ast"; 2 | import * as ASTTypes from "../../../ast/types"; 3 | import { nodeFromExpression } from "../../create"; 4 | import { SingleValuePort } from "../../ports/single"; 5 | import { Scope } from "../../scopes/scope"; 6 | import * as Types from "../../types"; 7 | import { checkType } from "../../types/check"; 8 | import * as Values from "../../values"; 9 | import { Node } from "../node"; 10 | 11 | export class FunctionType extends Node { 12 | private params: Node[]; 13 | private returns: Node[]; 14 | 15 | constructor(defintion: ASTTypes.FunctionType, scope: Scope) { 16 | super("FunctionType", defintion); 17 | 18 | this.params = []; 19 | for (const expression of defintion.params) { 20 | this.params.push(nodeFromExpression(expression, scope)); 21 | } 22 | 23 | this.returns = []; 24 | for (const expression of defintion.returns) { 25 | this.params.push(nodeFromExpression(expression, scope)); 26 | } 27 | 28 | this.addInPorts(this.params); 29 | this.addInPorts(this.returns); 30 | 31 | this.outPorts = [new SingleValuePort(this.fetchData)]; 32 | } 33 | 34 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 35 | if (!checkType(new Types.Type(), type)) { 36 | return new Values.ErrorValue("Incompartible type, not type"); 37 | } 38 | 39 | const params: Types.Type[] = []; 40 | const returns: Types.Type[] = []; 41 | 42 | for (let i = 0; i < this.params.length; i++) { 43 | params.push((this.inPorts[i].getData(new Types.Type()) as Values.Type).value); 44 | } 45 | for (let i = 0; i < this.returns.length; i++) { 46 | returns.push( 47 | (this.inPorts[i + this.params.length].getData(new Types.Type()) as Values.Type).value); 48 | } 49 | 50 | return new Values.Type(new Types.Function(params, returns)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/ir1/nodes/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./array"; 2 | export * from "./function"; 3 | export * from "./literal"; 4 | export * from "./record"; 5 | export * from "./some"; 6 | export * from "./stream"; 7 | -------------------------------------------------------------------------------- /src/ir1/nodes/types/literal.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../../ast"; 2 | import * as ASTTypes from "../../../ast/types"; 3 | import { SingleValuePort } from "../../ports/single"; 4 | import * as Types from "../../types"; 5 | import * as Values from "../../values"; 6 | import { Node } from "../node"; 7 | 8 | export class LiteralType extends Node { 9 | 10 | constructor(defintion: ASTTypes.PrimitiveType) { 11 | super("LiteralType", defintion); 12 | 13 | let value: Values.ReadyValue; 14 | 15 | if (ASTTypes.isBooleanType(defintion)) { 16 | value = new Values.Type(new Types.Boolean()); 17 | } else if (ASTTypes.isIntegerType(defintion)) { 18 | value = new Values.Type(new Types.Integer()); 19 | } else if (ASTTypes.isFloatType(defintion)) { 20 | value = new Values.Type(new Types.Float()); 21 | } else if (ASTTypes.isStringType(defintion)) { 22 | value = new Values.Type(new Types.String()); 23 | } else { 24 | throw new Error("Unexpected literal type type " + JSON.stringify(defintion)); 25 | } 26 | 27 | this.outPorts.push(new SingleValuePort((dataType: Types.ReadyType) => value)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ir1/nodes/types/record.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../../ast"; 2 | import * as ASTTypes from "../../../ast/types"; 3 | import { nodeFromExpression } from "../../create"; 4 | import { SingleValuePort } from "../../ports/single"; 5 | import { Scope } from "../../scopes/scope"; 6 | import * as Types from "../../types"; 7 | import { checkType } from "../../types/check"; 8 | import * as Values from "../../values"; 9 | import { Node } from "../node"; 10 | import { SomeType } from "./some"; 11 | 12 | export class RecordType extends Node { 13 | private nodes: Node[]; 14 | private names: string[]; 15 | 16 | constructor(defintion: ASTTypes.RecordType, scope: Scope) { 17 | super("RecordType", defintion); 18 | this.nodes = []; 19 | this.names = []; 20 | 21 | for (const field of defintion.fields) { 22 | this.names.push(field.name); 23 | if (field.dataType) { 24 | this.nodes.push(nodeFromExpression(field.dataType, scope)); 25 | } else { 26 | this.nodes.push(new SomeType()); 27 | } 28 | } 29 | this.addInPorts(this.nodes); 30 | 31 | this.outPorts = [new SingleValuePort(this.fetchData)]; 32 | } 33 | 34 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 35 | if (!checkType(new Types.Type(), type)) { 36 | return new Values.ErrorValue("Incompartible type, not type"); 37 | } 38 | 39 | const params = new Map(); 40 | 41 | for (let i = 0; i < this.names.length; i++) { 42 | params.set(this.names[i], 43 | (this.inPorts[i].getData(new Types.Type()) as Values.Type).value); 44 | } 45 | 46 | return new Values.Type(new Types.Record(params)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/ir1/nodes/types/some.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../../ast"; 2 | import * as ASTTypes from "../../../ast/types"; 3 | import { SingleValuePort } from "../../ports/single"; 4 | import * as Types from "../../types"; 5 | import * as Values from "../../values"; 6 | import { Node } from "../node"; 7 | 8 | export class SomeType extends Node { 9 | 10 | constructor() { 11 | super("SomeType"); 12 | 13 | this.outPorts.push( 14 | new SingleValuePort((dataType: Types.ReadyType) => new Values.Type(new Types.Some()))); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/ir1/nodes/types/stream.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../../ast"; 2 | import * as ASTTypes from "../../../ast/types"; 3 | import { nodeFromExpression } from "../../create"; 4 | import { SingleValuePort } from "../../ports/single"; 5 | import { Scope } from "../../scopes/scope"; 6 | import * as Types from "../../types"; 7 | import { checkType } from "../../types/check"; 8 | import * as Values from "../../values"; 9 | import { Node } from "../node"; 10 | 11 | export class StreamType extends Node { 12 | private nodes: Node[]; 13 | 14 | constructor(defintion: ASTTypes.StreamType, scope: Scope) { 15 | super("StreamType", defintion); 16 | 17 | this.nodes = []; 18 | this.nodes.push(nodeFromExpression(defintion.elementType, scope)); 19 | 20 | this.addInPorts(this.nodes); 21 | 22 | this.outPorts = [new SingleValuePort(this.fetchData)]; 23 | } 24 | 25 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 26 | if (!checkType(new Types.Type(), type)) { 27 | return new Values.ErrorValue("Incompartible type, not type"); 28 | } 29 | 30 | const result = this.inPorts[0].getData(new Types.Type()) as Values.Type; 31 | return new Values.Type(new Types.Stream(result.value)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ir1/nodes/unary.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import { nodeFromExpression } from "../create"; 3 | import { StreamPort } from "../ports/stream"; 4 | import { Scope } from "../scopes/scope"; 5 | import * as Types from "../types"; 6 | import * as Values from "../values"; 7 | import { Node } from "./node"; 8 | 9 | export class UnaryExpression extends Node { 10 | private nodes: Node[]; 11 | private operator: string; 12 | 13 | constructor(definition: AST.UnaryExpression, scope: Scope) { 14 | super("Unary", definition); 15 | this.operator = definition.operator; 16 | 17 | this.nodes = []; 18 | this.nodes.push(nodeFromExpression(definition.right, scope)); 19 | 20 | this.addInPorts(this.nodes); 21 | 22 | this.outPorts.push(new StreamPort((type: Types.ReadyType, offset: number) => this.fetchData(type, offset))); 23 | } 24 | 25 | private fetchData(dataType: Types.ReadyType, offset: number): Values.ReadyValue { 26 | const right = this.inPorts[0].getData(dataType, offset); 27 | 28 | return this.processOperation(right); 29 | } 30 | 31 | private processOperation(value: Values.ReadyValue): Values.ReadyValue { 32 | if (value instanceof Values.Integer) { 33 | if (this.operator === "-") { 34 | return new Values.Integer(-value.value); 35 | } 36 | if (this.operator === "+") { 37 | return value; 38 | } 39 | } 40 | 41 | if (value instanceof Values.Float) { 42 | if (this.operator === "-") { 43 | return new Values.Float(-value.value); 44 | } 45 | if (this.operator === "+") { 46 | return value; 47 | } 48 | } 49 | 50 | if (value instanceof Values.Boolean) { 51 | if (this.operator === "!") { 52 | return new Values.Boolean(!value.value); 53 | } 54 | } 55 | 56 | if (value instanceof Values.StreamEnd) { 57 | return value; 58 | } 59 | 60 | if (value instanceof Values.StreamElement) { 61 | return new Values.StreamElement(this.processOperation(value.value)); 62 | } 63 | 64 | return new Values.ErrorValue("Unsupported types " + value.type.name); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ir1/ports/port.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "../types/ready"; 2 | import { ReadyValue } from "../values/ready"; 3 | 4 | export interface Port { 5 | getData: (dataType: ReadyType, offset?: number) => ReadyValue; 6 | } 7 | -------------------------------------------------------------------------------- /src/ir1/ports/single.ts: -------------------------------------------------------------------------------- 1 | import { checkType } from "../types/check"; 2 | import { ReadyType } from "../types/ready"; 3 | import { ReadyValue } from "../values/ready"; 4 | import { Port } from "./port"; 5 | 6 | type dataFetchFunction = (dataType: ReadyType) => ReadyValue; 7 | 8 | export class SingleValuePort implements Port { 9 | private data?: ReadyValue; 10 | private fetchData: dataFetchFunction; 11 | 12 | public constructor(fetchData: dataFetchFunction) { 13 | this.fetchData = fetchData; 14 | } 15 | 16 | public getData(dataType: ReadyType, offset?: number): ReadyValue { 17 | if (offset && offset !== 0) { 18 | throw new Error("Requesting non-zero offset for a single value"); 19 | } 20 | if (!this.data) { 21 | this.data = this.fetchData(dataType); 22 | } 23 | checkType(this.data.type, dataType); 24 | return this.data; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ir1/ports/stream.ts: -------------------------------------------------------------------------------- 1 | import { checkType } from "../types/check"; 2 | import { ReadyType } from "../types/ready"; 3 | import { ReadyValue } from "../values/ready"; 4 | import { Port } from "./port"; 5 | 6 | type dataFetchFunction = (dataType: ReadyType, offset: number) => ReadyValue; 7 | 8 | export class StreamPort implements Port { 9 | private data: ReadyValue[]; 10 | private fetchData: dataFetchFunction; 11 | 12 | public constructor(fetchData: dataFetchFunction) { 13 | this.fetchData = fetchData; 14 | this.data = []; 15 | } 16 | 17 | public getData(dataType: ReadyType, offset?: number): ReadyValue { 18 | if (!offset) { 19 | offset = 0; 20 | } 21 | if (!this.data[offset]) { 22 | this.data[offset] = this.fetchData(dataType, offset); 23 | } 24 | checkType(this.data[offset].type, dataType); 25 | return this.data[offset]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ir1/scopes/flat.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import { nodeFromExpression } from "../create"; 3 | import { Node } from "../nodes/node"; 4 | import { Port } from "../ports/port"; 5 | import * as Types from "../types"; 6 | import { ReadyType } from "../types/ready"; 7 | import * as Values from "../values"; 8 | import { Scope } from "./scope"; 9 | 10 | export class FlatScope implements Scope { 11 | public parentScope?: Scope; 12 | public oldScope?: Scope; 13 | public definitions: Map; 14 | 15 | public constructor(parent: Scope | undefined) { 16 | this.parentScope = parent; 17 | this.definitions = new Map(); 18 | } 19 | 20 | public resolve(name: string, type: Types.ReadyType, offset: number): Values.ReadyValue { 21 | if (offset === -1 && this.oldScope) { 22 | return this.oldScope.resolve(name, type, 0); 23 | } 24 | 25 | if (this.definitions.has(name)) { 26 | const candidates = this.definitions.get(name); 27 | for (const port of candidates!) { 28 | const value = port.getData(new Types.Some(), offset); 29 | if (Types.checkType(value.type, type)) { 30 | return value; 31 | } 32 | } 33 | } 34 | 35 | if (!this.parentScope) { 36 | return new Values.ErrorValue("Cannot resolve name " + name); 37 | } 38 | return this.parentScope.resolve(name, type, offset); 39 | } 40 | 41 | public addFromAST(definitions: AST.Definition[]): void { 42 | for (const d of definitions) { 43 | if (d.left.length !== d.right.length && d.right.length !== 1) { 44 | throw new Error("Definition arity doesn't match: " + 45 | String(d.left.length) + ":" + String(d.right.length)); 46 | } 47 | const expressions = d.right.map((e) => nodeFromExpression(e, this)); 48 | if (expressions.length === 1) { 49 | expressions[0].requestPorts(d.left.length); 50 | } 51 | for (let i = 0; i < d.left.length; i++) { 52 | let currentDefinitions: Port[] = []; 53 | if (this.definitions.has(d.left[i])) { 54 | currentDefinitions = this.definitions.get(d.left[i])!; 55 | } 56 | if (expressions.length === 1) { 57 | currentDefinitions.push(expressions[0].outPorts[i]); 58 | } else { 59 | currentDefinitions.push(expressions[i].outPorts[0]); 60 | } 61 | this.definitions.set(d.left[i], currentDefinitions); 62 | } 63 | } 64 | } 65 | 66 | public inject(name: string, port: Port) { 67 | let currentDefinitions: Port[] = []; 68 | if (this.definitions.has(name)) { 69 | currentDefinitions = this.definitions.get(name)!; 70 | } 71 | currentDefinitions.push(port); 72 | this.definitions.set(name, currentDefinitions); 73 | } 74 | } 75 | 76 | export function createFromMap(parent: Scope, definitions: Map): Scope { 77 | const scope = new FlatScope(parent); 78 | definitions.forEach((nodes: Node[], key: string) => { 79 | scope.definitions.set(key, nodes.map((n) => n.outPorts[0])); 80 | }); 81 | return scope; 82 | } 83 | -------------------------------------------------------------------------------- /src/ir1/scopes/loop.ts: -------------------------------------------------------------------------------- 1 | import * as AST from "../../ast"; 2 | import { nodeFromExpression } from "../create"; 3 | import { Node } from "../nodes/node"; 4 | import { ReadyLiteral } from "../nodes/ready"; 5 | import { Port } from "../ports/port"; 6 | import * as Types from "../types"; 7 | import { ReadyType } from "../types/ready"; 8 | import * as Values from "../values"; 9 | import { FlatScope } from "./flat"; 10 | import { Scope } from "./scope"; 11 | 12 | export class LoopScope implements Scope { 13 | public parentScope: Scope; 14 | private initScope: FlatScope; 15 | private iterationScopes: FlatScope[]; 16 | private ranges: Array<[string, Port]>; 17 | private nodes: Node[]; 18 | private localNames: Set; 19 | private maxOffset: number = -1; 20 | private rangeNodes: Node[][]; 21 | 22 | private preCondition?: AST.Expression; 23 | private postCondition?: AST.Expression; 24 | private body: AST.Definition[]; 25 | 26 | public constructor(parent: Scope, body: AST.Definition[], 27 | range?: AST.RangeList, init?: AST.Definition[], 28 | preCondition?: AST.Expression, postCondition?: AST.Expression) { 29 | this.parentScope = parent; 30 | this.iterationScopes = []; 31 | this.rangeNodes = []; 32 | 33 | this.initScope = new FlatScope(this.parentScope); 34 | this.localNames = new Set(); 35 | if (init) { 36 | this.initScope.addFromAST(init); 37 | for (const definition of init) { 38 | for (const name of definition.left) { 39 | this.localNames.add(name); 40 | } 41 | } 42 | } 43 | 44 | for (const definition of body) { 45 | for (const name of definition.left) { 46 | this.localNames.add(name); 47 | } 48 | } 49 | 50 | this.ranges = []; 51 | this.nodes = []; 52 | let currentName = 0; 53 | if (range) { 54 | for (const expression of range.ranges) { 55 | const node = nodeFromExpression(expression, this.parentScope); 56 | this.nodes.push(node); 57 | for (const port of node.outPorts) { 58 | if (currentName >= range.names.length) { 59 | throw new Error("Range should produce equal number of outPorts"); 60 | } 61 | this.ranges.push([range.names[currentName], port]); 62 | this.localNames.add(range.names[currentName]); 63 | currentName++; 64 | } 65 | } 66 | } 67 | 68 | this.body = body; 69 | this.preCondition = preCondition; 70 | this.postCondition = postCondition; 71 | } 72 | 73 | public resolve(name: string, type: Types.ReadyType, offset: number): Values.ReadyValue { 74 | if (this.localNames.has(name)) { 75 | if (!Types.checkType(new Types.Stream(new Types.Some()), type)) { 76 | return new Values.ErrorValue("All names are streams inside loop"); 77 | } 78 | if (this.ranges.length > 0 && this.maxOffset === -1) { 79 | this.maxOffset = 1; 80 | for (const pair of this.ranges) { 81 | const stream = Values.fetchComplete(pair[1], new Types.Some()) as Values.CompleteStream; 82 | this.maxOffset *= stream.values.length; 83 | 84 | const rangeNodes: Node[] = []; 85 | for (const value of stream.values) { 86 | rangeNodes.push(new ReadyLiteral(value)); 87 | } 88 | this.rangeNodes.push(rangeNodes); 89 | } 90 | } 91 | 92 | if (offset >= this.maxOffset) { 93 | return new Values.StreamEnd(); 94 | } 95 | 96 | for (let i = 0; i <= offset; i++) { 97 | if (!this.iterationScopes[i]) { 98 | const prevScope = this.iterationScopes[i - 1] ? 99 | this.iterationScopes[i - 1] : this.initScope; 100 | 101 | if (this.preCondition && !this.checkCondition(this.preCondition, prevScope)) { 102 | break; 103 | } 104 | 105 | this.iterationScopes[i] = new FlatScope(this.parentScope); 106 | this.iterationScopes[i].oldScope = prevScope; 107 | this.iterationScopes[i].addFromAST(this.body); 108 | 109 | let iterationOffset = i; 110 | for (let j = 0; j < this.rangeNodes.length; j++) { 111 | this.iterationScopes[i].inject(this.ranges[j][0], 112 | this.rangeNodes[j][iterationOffset % this.rangeNodes[j].length].outPorts[0]); 113 | iterationOffset /= this.rangeNodes[j].length; 114 | } 115 | 116 | if (this.postCondition && !this.checkCondition(this.postCondition, this.iterationScopes[i])) { 117 | break; 118 | } 119 | } 120 | } 121 | 122 | if (!this.iterationScopes[offset]) { 123 | return new Values.StreamEnd(); 124 | } 125 | 126 | return new Values.StreamElement(this.iterationScopes[offset].resolve(name, type, 0)); 127 | } 128 | 129 | return this.parentScope.resolve(name, type, offset); 130 | } 131 | 132 | private checkCondition(preCondition: AST.Expression, scope: Scope): boolean { 133 | const node = nodeFromExpression(preCondition, scope); 134 | const condition = node.outPorts[0].getData(new Types.Boolean()) as Values.Boolean; 135 | return condition.value; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/ir1/scopes/scope.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import * as Values from "../values"; 3 | 4 | export interface Scope { 5 | resolve: (name: string, type: Types.ReadyType, offset: number) => Values.ReadyValue; 6 | } 7 | -------------------------------------------------------------------------------- /src/ir1/types/array.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class Array extends ReadyType { 4 | public element: ReadyType; 5 | 6 | constructor(element: ReadyType) { 7 | super("Array"); 8 | this.element = element; 9 | } 10 | public toString() { 11 | return "Array[" + this.element.toString() + "]"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ir1/types/boolean.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class Boolean extends ReadyType { 4 | public constructor() { 5 | super("Boolean"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ir1/types/check.ts: -------------------------------------------------------------------------------- 1 | import { Array } from "./array"; 2 | import { ReadyType } from "./ready"; 3 | import { Some } from "./some"; 4 | import { Stream } from "./stream"; 5 | 6 | export function checkType(checked: ReadyType, requested: ReadyType): boolean { 7 | if (requested instanceof Some) { 8 | return true; 9 | } 10 | if ((requested instanceof Stream) && (checked instanceof Stream)) { 11 | return checkType(checked.element, requested.element); 12 | } 13 | if ((requested instanceof Array) && (checked instanceof Array)) { 14 | return checkType(checked.element, requested.element); 15 | } 16 | return requested.name === checked.name; 17 | } 18 | -------------------------------------------------------------------------------- /src/ir1/types/error.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class Error extends ReadyType { 4 | public constructor() { 5 | super("Error"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ir1/types/float.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class Float extends ReadyType { 4 | public constructor() { 5 | super("Float"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ir1/types/function.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class Function extends ReadyType { 4 | public params: ReadyType[]; 5 | public returns: ReadyType[]; 6 | 7 | constructor(params: ReadyType[], returns: ReadyType[]) { 8 | super("Function"); 9 | this.params = params; 10 | this.returns = returns; 11 | } 12 | public toString() { 13 | const getNames = (subtypes: ReadyType[]) => subtypes.map((e) => e.toString()).join(", "); 14 | return "Function[" + getNames(this.params) + " returns " + getNames(this.returns) + "]"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/ir1/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./array"; 2 | export * from "./boolean"; 3 | export * from "./check"; 4 | export * from "./error"; 5 | export * from "./float"; 6 | export * from "./function"; 7 | export * from "./integer"; 8 | export * from "./ready"; 9 | export * from "./record"; 10 | export * from "./some"; 11 | export * from "./stream"; 12 | export * from "./string"; 13 | export * from "./type"; 14 | -------------------------------------------------------------------------------- /src/ir1/types/integer.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class Integer extends ReadyType { 4 | public constructor() { 5 | super("Integer"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ir1/types/ready.ts: -------------------------------------------------------------------------------- 1 | export class ReadyType { 2 | public name: string; 3 | 4 | public constructor(name: string) { 5 | this.name = name; 6 | } 7 | public toString() { 8 | return this.name; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ir1/types/record.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class Record extends ReadyType { 4 | public elements: Map; 5 | 6 | constructor(elements: Map) { 7 | super("Record"); 8 | this.elements = elements; 9 | } 10 | public toString() { 11 | const names: string[] = []; 12 | this.elements.forEach((value: ReadyType, key: string) => { 13 | names.push(key + ": " + value.toString()); 14 | }); 15 | return "Record[" + names.join(", ") + "]"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/ir1/types/some.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class Some extends ReadyType { 4 | public constructor() { 5 | super("Some"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ir1/types/stream.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class Stream extends ReadyType { 4 | public element: ReadyType; 5 | 6 | constructor(element: ReadyType) { 7 | super("Stream"); 8 | this.element = element; 9 | } 10 | public toString() { 11 | return "Stream[" + this.element.toString() + "]"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ir1/types/string.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class String extends ReadyType { 4 | public constructor() { 5 | super("String"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ir1/types/type.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "./ready"; 2 | 3 | export class Type extends ReadyType { 4 | public constructor() { 5 | super("Type"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ir1/values/array.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class Array extends ReadyValue { 5 | public values: ReadyValue[]; 6 | public constructor(values: ReadyValue[]) { 7 | super(new Types.Array(values[0].type)); 8 | this.values = values; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ir1/values/boolean.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class Boolean extends ReadyValue { 5 | public value: boolean; 6 | public constructor(value: boolean) { 7 | super(new Types.Boolean()); 8 | this.value = value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ir1/values/error.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class ErrorValue extends ReadyValue { 5 | public value: string; 6 | public constructor(value: string) { 7 | super(new Types.Error()); 8 | this.value = value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ir1/values/fetch.ts: -------------------------------------------------------------------------------- 1 | import { Port } from "../ports/port"; 2 | import * as Types from "../types"; 3 | import { ReadyValue } from "./ready"; 4 | import { CompleteStream } from "./streamComplete"; 5 | import { StreamElement } from "./streamElement"; 6 | import { StreamEnd } from "./streamEnd"; 7 | 8 | export function fetchComplete(port: Port, type: Types.ReadyType): ReadyValue { 9 | const value = port.getData(type); 10 | if (value instanceof StreamElement) { 11 | const values: ReadyValue[] = []; 12 | let lastValue: ReadyValue = value; 13 | let index = 0; 14 | while (lastValue instanceof StreamElement) { 15 | index += 1; 16 | values.push(lastValue.value); 17 | lastValue = port.getData(type, index); 18 | } 19 | return new CompleteStream(values); 20 | } 21 | return value; 22 | } 23 | -------------------------------------------------------------------------------- /src/ir1/values/float.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class Float extends ReadyValue { 5 | public value: number; 6 | public constructor(value: number) { 7 | super(new Types.Float()); 8 | this.value = value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ir1/values/function.ts: -------------------------------------------------------------------------------- 1 | import { Expression } from "../../ast/expression"; 2 | import { Node } from "../nodes/node"; 3 | import { Scope } from "../scopes/scope"; 4 | import * as Types from "../types"; 5 | import { ReadyValue } from "./ready"; 6 | 7 | export type BodyFactory = (scope: Scope) => Node[]; 8 | 9 | export class Function extends ReadyValue { 10 | public bodyFactory: BodyFactory; 11 | public scope: Scope; 12 | public argumentNames: string[]; 13 | 14 | public constructor(type: Types.Function, argumentNames: string[], 15 | bodyFactory: BodyFactory, scope: Scope) { 16 | super(type); 17 | this.argumentNames = argumentNames; 18 | this.bodyFactory = bodyFactory; 19 | this.scope = scope; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ir1/values/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./array"; 2 | export * from "./boolean"; 3 | export * from "./error"; 4 | export * from "./fetch"; 5 | export * from "./float"; 6 | export * from "./function"; 7 | export * from "./integer"; 8 | export * from "./ready"; 9 | export * from "./record"; 10 | export * from "./streamComplete"; 11 | export * from "./streamElement"; 12 | export * from "./streamEnd"; 13 | export * from "./string"; 14 | export * from "./type"; 15 | -------------------------------------------------------------------------------- /src/ir1/values/integer.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class Integer extends ReadyValue { 5 | public value: number; 6 | public constructor(value: number) { 7 | super(new Types.Integer()); 8 | this.value = value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ir1/values/ready.ts: -------------------------------------------------------------------------------- 1 | import { ReadyType } from "../types/ready"; 2 | 3 | export class ReadyValue { 4 | public type: ReadyType; 5 | constructor(type: ReadyType) { 6 | this.type = type; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/ir1/values/record.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class Record extends ReadyValue { 5 | public values: Map; 6 | public constructor(type: Types.ReadyType, values: Map) { 7 | super(type); 8 | this.values = values; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ir1/values/streamComplete.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class CompleteStream extends ReadyValue { 5 | public values: ReadyValue[]; 6 | public constructor(values: ReadyValue[]) { 7 | super(new Types.Stream(values[0].type)); 8 | this.values = values; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ir1/values/streamElement.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class StreamElement extends ReadyValue { 5 | public value: ReadyValue; 6 | public constructor(value: ReadyValue) { 7 | super(new Types.Stream(value.type)); 8 | this.value = value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ir1/values/streamEnd.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class StreamEnd extends ReadyValue { 5 | public constructor() { 6 | super(new Types.Stream(new Types.Some())); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/ir1/values/string.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class String extends ReadyValue { 5 | public value: string; 6 | public constructor(value: string) { 7 | super(new Types.String()); 8 | this.value = value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ir1/values/type.ts: -------------------------------------------------------------------------------- 1 | import * as Types from "../types"; 2 | import { ReadyValue } from "./ready"; 3 | 4 | export class Type extends ReadyValue { 5 | public value: Types.ReadyType; 6 | public constructor(value: Types.ReadyType) { 7 | super(new Types.Type()); 8 | this.value = value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/parser.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import { add_indents } from "indent-adder"; 3 | import * as PEG from "pegjs"; 4 | import * as AST from "./ast"; 5 | 6 | export class Parser { 7 | private parser: PEG.Parser; 8 | public constructor() { 9 | const grammar = fs.readFileSync("src/grammar/sisal.pegjs", "utf8"); 10 | this.parser = PEG.generate(grammar); 11 | } 12 | public parse(program: string): AST.Definition[] { 13 | return this.parser.parse(add_indents(program, "{", "}", "#", "'\"", "([", ")]")); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/print.ts: -------------------------------------------------------------------------------- 1 | import { Port } from "./ir1/ports/port"; 2 | import * as Types from "./ir1/types"; 3 | import * as Values from "./ir1/values"; 4 | 5 | const maxLength = 10; 6 | 7 | function printValue(value: Values.ReadyValue, print: (s: string) => void) { 8 | if (value instanceof Values.Integer || value instanceof Values.Float || 9 | value instanceof Values.Boolean || value instanceof Values.ErrorValue) { 10 | print(String(value.value)); 11 | return; 12 | } 13 | if (value instanceof Values.StreamElement) { 14 | printValue(value.value, print); 15 | return; 16 | } 17 | if (value instanceof Values.Function) { 18 | print("{function_body}"); 19 | return; 20 | } 21 | if (value instanceof Values.Array || value instanceof Values.CompleteStream) { 22 | trimmedPrint(value.values, print); 23 | return; 24 | } 25 | if (value instanceof Values.Record) { 26 | const values: Values.ReadyValue[] = []; 27 | value.values.forEach((v: Values.ReadyValue, key: string) => { 28 | values.push(v); 29 | }); 30 | trimmedPrint(values, print); 31 | return; 32 | } 33 | } 34 | 35 | function trimmedPrint(values: Values.ReadyValue[], print: (s: string) => void) { 36 | let count = 0; 37 | print("{"); 38 | for (const value of values) { 39 | if (count > 0) { 40 | print(", "); 41 | } 42 | 43 | printValue(value, print); 44 | count++; 45 | 46 | if (count >= maxLength) { 47 | print(", ..."); 48 | break; 49 | } 50 | } 51 | print("}"); 52 | } 53 | 54 | export function printPortData(input: Port, print: (s: string) => void) { 55 | const value = input.getData(new Types.Some()); 56 | print(value.type.toString() + " = "); 57 | 58 | if (value instanceof Values.StreamElement || value instanceof Values.StreamEnd) { 59 | const values = [value]; 60 | let count = 0; 61 | while (!(values[values.length - 1] instanceof Values.StreamEnd) && values.length <= maxLength) { 62 | values.push(input.getData(new Types.Some(), ++count)); 63 | } 64 | trimmedPrint(values, print); 65 | return; 66 | } 67 | printValue(value, print); 68 | } 69 | -------------------------------------------------------------------------------- /src/program.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as AST from "./ast"; 3 | import * as GML from "./graphml/"; 4 | import { nodeFromExpression } from "./ir1/create"; 5 | import { Node, subGraphML } from "./ir1/nodes/node"; 6 | import { Port } from "./ir1/ports/port"; 7 | import { FlatScope } from "./ir1/scopes/flat"; 8 | import { Scope } from "./ir1/scopes/scope"; 9 | import * as Types from "./ir1/types"; 10 | import * as Values from "./ir1/values"; 11 | import { Parser } from "./parser"; 12 | 13 | export class Program { 14 | public outputs: Port[]; 15 | public endNodes: Node[]; 16 | 17 | public constructor(text: string, parser: Parser, stdLib: Scope) { 18 | const scope = new FlatScope(stdLib); 19 | scope.addFromAST(parser.parse(text)); 20 | 21 | const value = scope.resolve("main", new Types.Some(), 0); 22 | 23 | if (value instanceof Values.ErrorValue) { 24 | throw new Error("No main function defined\n"); 25 | } 26 | const main = value as Values.Function; 27 | const type = main.type as Types.Function; 28 | 29 | if (type.params.length > 0) { 30 | throw new Error("Main function arguments are not supported\n"); 31 | } 32 | 33 | this.outputs = []; 34 | 35 | this.endNodes = main.bodyFactory(scope); 36 | for (const node of this.endNodes) { 37 | node.requestPorts(1); 38 | for (const port of node.outPorts) { 39 | this.outputs.push(port); 40 | } 41 | } 42 | } 43 | 44 | public graphML(): string { 45 | return GML.makeDocument(GML.makeGraph("program", subGraphML(this.endNodes))); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/sisal.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import { pd } from "pretty-data"; 3 | import { Parser } from "./parser"; 4 | import { printPortData } from "./print"; 5 | import { Program } from "./program"; 6 | import { buildStdLib } from "./stdlib"; 7 | 8 | const args = process.argv; 9 | let graph = false; 10 | 11 | if (args.indexOf("--graph") !== -1) { 12 | args.splice(args.indexOf("--graph"), 1); 13 | graph = true; 14 | } 15 | 16 | if (args.length < 3) { 17 | process.stdout.write("Please specify Sisal program to run.\n"); 18 | process.exit(1); 19 | } 20 | 21 | const parser = new Parser(); 22 | const stdLib = buildStdLib(parser); 23 | const program = new Program(fs.readFileSync(args[2], "utf8"), parser, stdLib); 24 | 25 | if (graph) { 26 | for (const port of program.outputs) { 27 | printPortData(port, (s: string) => {}); 28 | } 29 | process.stdout.write(pd.xml(program.graphML())); 30 | } else { 31 | let outNumber = 0; 32 | for (const port of program.outputs) { 33 | process.stdout.write("Output #" + (outNumber++) + ": "); 34 | printPortData(port, (s: string) => process.stdout.write(s)); 35 | process.stdout.write("\n"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/stdlib.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import { FlatScope } from "./ir1/scopes/flat"; 3 | import { Scope } from "./ir1/scopes/scope"; 4 | import * as Types from "./ir1/types"; 5 | import { Parser } from "./parser"; 6 | import { firstValueFactory } from "./stdlib/first"; 7 | import { lastValueFactory } from "./stdlib/last"; 8 | import { FunctionWrap } from "./stdlib/wrapper"; 9 | 10 | export function buildStdLib(parser: Parser): Scope { 11 | const scope = new FlatScope(undefined); 12 | const reductions = parser.parse(fs.readFileSync("src/stdlib/reductions.sis", "utf8")); 13 | scope.addFromAST(reductions); 14 | 15 | const lastValue = new FunctionWrap(scope, "last", ["stream"], 16 | new Types.Function([new Types.Stream(new Types.Some())], [new Types.Some()]), 17 | lastValueFactory("stream")); 18 | scope.inject("last", lastValue.outPorts[0]); 19 | 20 | const firstValue = new FunctionWrap(scope, "first", ["stream"], 21 | new Types.Function([new Types.Stream(new Types.Some())], [new Types.Some()]), 22 | firstValueFactory("stream")); 23 | scope.inject("first", firstValue.outPorts[0]); 24 | 25 | return scope; 26 | } 27 | -------------------------------------------------------------------------------- /src/stdlib/first.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "../ir1/nodes/node"; 2 | import { Port } from "../ir1/ports/port"; 3 | import { SingleValuePort } from "../ir1/ports/single"; 4 | import { Scope } from "../ir1/scopes/scope"; 5 | import * as Types from "../ir1/types"; 6 | import * as Values from "../ir1/values"; 7 | 8 | export function firstValueFactory(name: string): Values.BodyFactory { 9 | return (scope: Scope) => { 10 | return [new FirstValue(scope, name)]; 11 | }; 12 | } 13 | 14 | export class FirstValue extends Node { 15 | private scope: Scope; 16 | private argument: string; 17 | 18 | constructor(scope: Scope, argument: string) { 19 | super("LastValue"); 20 | this.scope = scope; 21 | this.argument = argument; 22 | this.outPorts.push(new SingleValuePort((type: Types.ReadyType) => this.fetchData(type))); 23 | } 24 | 25 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 26 | const param = this.scope.resolve(this.argument, new Types.Stream(type), 0); 27 | 28 | if (param instanceof Values.StreamElement) { 29 | return param.value; 30 | } 31 | 32 | return param; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/stdlib/last.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "../ir1/nodes/node"; 2 | import { Port } from "../ir1/ports/port"; 3 | import { SingleValuePort } from "../ir1/ports/single"; 4 | import { Scope } from "../ir1/scopes/scope"; 5 | import * as Types from "../ir1/types"; 6 | import * as Values from "../ir1/values"; 7 | 8 | export function lastValueFactory(name: string): Values.BodyFactory { 9 | return (scope: Scope) => { 10 | return [new LastValue(scope, name)]; 11 | }; 12 | } 13 | 14 | export class LastValue extends Node { 15 | private scope: Scope; 16 | private argument: string; 17 | 18 | constructor(scope: Scope, argument: string) { 19 | super("LastValue"); 20 | this.scope = scope; 21 | this.argument = argument; 22 | this.outPorts.push(new SingleValuePort((type: Types.ReadyType) => this.fetchData(type))); 23 | } 24 | 25 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 26 | let param = this.scope.resolve(this.argument, new Types.Stream(type), 0); 27 | let lastValue = param; 28 | let index = 0; 29 | 30 | while (param instanceof Values.StreamElement) { 31 | lastValue = param; 32 | param = this.scope.resolve(this.argument, new Types.Stream(type), ++index); 33 | } 34 | 35 | if (lastValue instanceof Values.StreamElement) { 36 | return lastValue.value; 37 | } 38 | 39 | return lastValue; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/stdlib/reductions.sis: -------------------------------------------------------------------------------- 1 | max = f(a) 2 | for b in a 3 | initial 4 | m = first of a 5 | repeat 6 | m = if old m > b 7 | old m 8 | else 9 | b 10 | returns last of m 11 | 12 | min = f(a) 13 | for b in a 14 | initial 15 | m = first of a 16 | repeat 17 | m = if old m < b 18 | old m 19 | else 20 | b 21 | returns last of m 22 | 23 | sum = f(a) 24 | for b in a 25 | initial 26 | m = 0 27 | repeat 28 | m = old m + b 29 | returns last of m 30 | 31 | product = f(a) 32 | for b in a 33 | initial 34 | m = 1 35 | repeat 36 | m = old m * b 37 | returns last of m 38 | -------------------------------------------------------------------------------- /src/stdlib/wrapper.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "../ir1/nodes/node"; 2 | import { Port } from "../ir1/ports/port"; 3 | import { SingleValuePort } from "../ir1/ports/single"; 4 | import { Scope } from "../ir1/scopes/scope"; 5 | import * as Types from "../ir1/types"; 6 | import * as Values from "../ir1/values"; 7 | 8 | export class FunctionWrap extends Node { 9 | private scope: Scope; 10 | private bodyFactory: Values.BodyFactory; 11 | private args: string[]; 12 | private signature: Types.Function; 13 | 14 | constructor(scope: Scope, name: string, args: string[], 15 | signature: Types.Function, bodyFactory: Values.BodyFactory) { 16 | super(name); 17 | this.scope = scope; 18 | this.args = args; 19 | this.signature = signature; 20 | this.bodyFactory = bodyFactory; 21 | this.outPorts.push(new SingleValuePort((type: Types.ReadyType) => this.fetchData(type))); 22 | } 23 | 24 | public fetchData(type: Types.ReadyType): Values.ReadyValue { 25 | if (!Types.checkType(this.signature, type)) { 26 | return new Values.ErrorValue("Incompartible type, given: " + 27 | JSON.stringify(this.signature) + ", requested: " + JSON.stringify(type)); 28 | } 29 | 30 | return new Values.Function(this.signature, this.args, this.bodyFactory, this.scope); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require ts-node/register 2 | --require source-map-support/register 3 | --recursive 4 | test/*.ts 5 | -------------------------------------------------------------------------------- /test/programs/arith.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | 7 + 9, 2.3 + 4.5 * 1.5, 3 | (1 + 2) * 3 * (7 + 2), 4 | 16 / 5, 3.33 / 3.0, 11 % 3 5 | -------------------------------------------------------------------------------- /test/programs/arith.sis.result: -------------------------------------------------------------------------------- 1 | Integer = 16 2 | Float = 9.05 3 | Integer = 81 4 | Integer = 3 5 | Float = 1.11 6 | Integer = 2 7 | -------------------------------------------------------------------------------- /test/programs/array.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | [1, 3, 18], 3 | [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 4 | -------------------------------------------------------------------------------- /test/programs/array.sis.result: -------------------------------------------------------------------------------- 1 | Array[Integer] = {1, 3, 18} 2 | Array[Array[Integer]] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} 3 | -------------------------------------------------------------------------------- /test/programs/arrayAccess.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | let 3 | a = [1, 2, 3] 4 | in 5 | a[1] 6 | -------------------------------------------------------------------------------- /test/programs/arrayAccess.sis.result: -------------------------------------------------------------------------------- 1 | Integer = 2 2 | -------------------------------------------------------------------------------- /test/programs/closure.sis: -------------------------------------------------------------------------------- 1 | a = 444 2 | b = 123 3 | main = f() 4 | a + b 5 | -------------------------------------------------------------------------------- /test/programs/closure.sis.result: -------------------------------------------------------------------------------- 1 | Integer = 567 2 | -------------------------------------------------------------------------------- /test/programs/function.sis: -------------------------------------------------------------------------------- 1 | fff = f(a) 2 | a * 2 3 | 4 | main = f() 5 | fff, fff(3), fff of 5 6 | -------------------------------------------------------------------------------- /test/programs/function.sis.result: -------------------------------------------------------------------------------- 1 | Function[Some returns Some] = {function_body} 2 | Integer = 6 3 | Integer = 10 4 | -------------------------------------------------------------------------------- /test/programs/if.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | if 1 > 2 3 | 3 4 | elseif 3 > 2 5 | 2 6 | else 7 | 1 8 | -------------------------------------------------------------------------------- /test/programs/if.sis.result: -------------------------------------------------------------------------------- 1 | Integer = 2 2 | -------------------------------------------------------------------------------- /test/programs/let.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | let 3 | a = 3 4 | b = 5 5 | in 6 | a + b * 4 7 | -------------------------------------------------------------------------------- /test/programs/let.sis.result: -------------------------------------------------------------------------------- 1 | Integer = 23 2 | -------------------------------------------------------------------------------- /test/programs/logic.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | (6 > 7) || (3 > 2) 3 | -------------------------------------------------------------------------------- /test/programs/logic.sis.result: -------------------------------------------------------------------------------- 1 | Boolean = true 2 | -------------------------------------------------------------------------------- /test/programs/loop.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | for i in [1..10] 3 | returns last of i 4 | -------------------------------------------------------------------------------- /test/programs/loop.sis.result: -------------------------------------------------------------------------------- 1 | Integer = 10 2 | -------------------------------------------------------------------------------- /test/programs/record.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | let 3 | rec = [ 4 | west = 1 5 | east = 2 6 | south = 3 7 | north = 4 8 | ] 9 | in 10 | rec, rec.west, rec.south 11 | -------------------------------------------------------------------------------- /test/programs/record.sis.result: -------------------------------------------------------------------------------- 1 | Record[west: Integer, east: Integer, south: Integer, north: Integer] = {1, 2, 3, 4} 2 | Integer = 1 3 | Integer = 3 4 | -------------------------------------------------------------------------------- /test/programs/recursion.sis: -------------------------------------------------------------------------------- 1 | factorial = f(n) 2 | if n < 3 3 | n 4 | else 5 | n * factorial(n - 1) 6 | 7 | main = f() 8 | factorial(2), factorial(8) 9 | -------------------------------------------------------------------------------- /test/programs/recursion.sis.result: -------------------------------------------------------------------------------- 1 | Integer = 2 2 | Integer = 40320 3 | -------------------------------------------------------------------------------- /test/programs/smoke.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | 1 3 | -------------------------------------------------------------------------------- /test/programs/smoke.sis.result: -------------------------------------------------------------------------------- 1 | Integer = 1 2 | -------------------------------------------------------------------------------- /test/programs/stdlib.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | last([1..10]), first([1..10]), 3 | max([1..10]), min([1..10]), 4 | sum([1..10]), product([1..10]) 5 | -------------------------------------------------------------------------------- /test/programs/stdlib.sis.result: -------------------------------------------------------------------------------- 1 | Integer = 10 2 | Integer = 1 3 | Integer = 10 4 | Integer = 1 5 | Integer = 55 6 | Integer = 3628800 7 | -------------------------------------------------------------------------------- /test/programs/stream.sis: -------------------------------------------------------------------------------- 1 | main = f() 2 | [1 .. 18], [4..] 3 | -------------------------------------------------------------------------------- /test/programs/stream.sis.result: -------------------------------------------------------------------------------- 1 | Stream[Integer] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...} 2 | Stream[Integer] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, ...} 3 | -------------------------------------------------------------------------------- /test/simple.ts: -------------------------------------------------------------------------------- 1 | import { Program } from "../src/program"; 2 | import { Parser } from "../src/parser"; 3 | import { buildStdLib } from "../src/stdlib"; 4 | import { printPortData } from "../src/print"; 5 | import { expect } from "chai"; 6 | import "mocha"; 7 | import * as fs from "fs"; 8 | 9 | const testPath = "test/programs/"; 10 | 11 | describe("All tests", () => { 12 | const parser = new Parser(); 13 | const stdLib = buildStdLib(parser); 14 | 15 | it("Should not crash", () => { 16 | const program = new Program(fs.readFileSync(testPath + "smoke.sis", "utf8"), parser, stdLib); 17 | expect(program.outputs.length).to.equal(1); 18 | }); 19 | it("Should not crash 2", () => { 20 | const program = new Program(fs.readFileSync(testPath + "arith.sis", "utf8"), parser, stdLib); 21 | expect(program.outputs.length).to.equal(6); 22 | }); 23 | 24 | const folder = fs.readdirSync(testPath); 25 | for (const fileName of folder) { 26 | if (fs.lstatSync(testPath + fileName).isDirectory() || fileName.endsWith(".result")) { 27 | continue; 28 | } 29 | it("Check " + fileName, () => { 30 | const program = new Program(fs.readFileSync(testPath + fileName, "utf8"), parser, stdLib); 31 | let result = ""; 32 | for (const port of program.outputs) { 33 | printPortData(port, (s: string) => result += s); 34 | result += "\n"; 35 | } 36 | expect(result).to.equal(fs.readFileSync(testPath + fileName + ".result", "utf8")); 37 | }); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./build", 4 | "allowJs": true, 5 | "strict": true, 6 | "moduleResolution": "node", 7 | "target": "es5", 8 | "sourceMap": true, 9 | "lib": ["es6"] 10 | }, 11 | "include": [ 12 | "./src/**/*" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "interface-name": [true, "never-prefix"] 9 | }, 10 | "rulesDirectory": [] 11 | } 12 | --------------------------------------------------------------------------------