├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .tours ├── lexer.tour ├── overview.tour └── parser.tour ├── FUNDING.yml ├── LICENSE ├── README.md ├── baselines └── reference │ ├── basicLex.lex.baseline │ ├── emptyStatement.errors.baseline │ ├── emptyStatement.js.baseline │ ├── emptyStatement.tree.baseline │ ├── firstLex.lex.baseline │ ├── multipleDeclarationsWithDifferentTypes.errors.baseline │ ├── multipleDeclarationsWithDifferentTypes.js.baseline │ ├── multipleDeclarationsWithDifferentTypes.tree.baseline │ ├── newlineLex.lex.baseline │ ├── redeclare.errors.baseline │ ├── redeclare.js.baseline │ ├── redeclare.tree.baseline │ ├── redeclareLetVar.errors.baseline │ ├── redeclareLetVar.js.baseline │ ├── redeclareLetVar.tree.baseline │ ├── semicolonLex.lex.baseline │ ├── singleIdentifier.errors.baseline │ ├── singleIdentifier.js.baseline │ ├── singleIdentifier.tree.baseline │ ├── singleLet.errors.baseline │ ├── singleLet.js.baseline │ ├── singleLet.tree.baseline │ ├── singleTypedVar.errors.baseline │ ├── singleTypedVar.js.baseline │ ├── singleTypedVar.tree.baseline │ ├── singleVar.errors.baseline │ ├── singleVar.js.baseline │ ├── singleVar.tree.baseline │ ├── stringLiteral.errors.baseline │ ├── stringLiteral.js.baseline │ ├── stringLiteral.tree.baseline │ ├── terminator.errors.baseline │ ├── terminator.js.baseline │ ├── terminator.tree.baseline │ ├── twoStatements.errors.baseline │ ├── twoStatements.js.baseline │ ├── twoStatements.tree.baseline │ ├── twoTypedStatements.errors.baseline │ ├── twoTypedStatements.js.baseline │ ├── twoTypedStatements.tree.baseline │ ├── typeAlias.errors.baseline │ ├── typeAlias.js.baseline │ ├── typeAlias.tree.baseline │ ├── typedNumber.lex.baseline │ ├── typedString.lex.baseline │ ├── typedVariableDeclarationList.errors.baseline │ ├── typedVariableDeclarationList.js.baseline │ ├── typedVariableDeclarationList.tree.baseline │ ├── underscoreLex.lex.baseline │ ├── unterminatedString.errors.baseline │ ├── unterminatedString.js.baseline │ ├── unterminatedString.tree.baseline │ ├── usedBeforeDeclaration.errors.baseline │ ├── usedBeforeDeclaration.js.baseline │ ├── usedBeforeDeclaration.tree.baseline │ ├── varLex.lex.baseline │ ├── varWithMultipleDeclarations.errors.baseline │ ├── varWithMultipleDeclarations.js.baseline │ ├── varWithMultipleDeclarations.tree.baseline │ ├── variableDeclarationList.errors.baseline │ ├── variableDeclarationList.js.baseline │ └── variableDeclarationList.tree.baseline ├── draft.md ├── package-lock.json ├── package.json ├── src ├── bind.ts ├── check.ts ├── compile.ts ├── emit.ts ├── error.ts ├── index.ts ├── lex.ts ├── parse.ts ├── test.ts ├── testBinder.ts ├── testFile.ts ├── testParser.ts ├── transform.ts └── types.ts ├── tests ├── emptyStatement.ts ├── multipleDeclarationsWithDifferentTypes.ts ├── redeclare.ts ├── redeclareLetVar.ts ├── singleIdentifier.ts ├── singleLet.ts ├── singleTypedVar.ts ├── singleVar.ts ├── stringLiteral.ts ├── terminator.ts ├── twoStatements.ts ├── twoTypedStatements.ts ├── typeAlias.ts ├── typedVariableDeclarationList.ts ├── unterminatedString.ts ├── usedBeforeDeclaration.ts ├── varWithMultipleDeclarations.ts └── variableDeclarationList.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | /baselines/reference/* eol=lf -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [15.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm ci 24 | - run: npm run build 25 | - run: npm test 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | baselines/local/ 3 | *.js -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.4.0 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /tests/**/* -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /.tours/lexer.tour: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://aka.ms/codetour-schema", 3 | "title": "Lexer", 4 | "steps": [ 5 | { 6 | "title": "Introduction", 7 | "description": "The lexer is the first phase of the compiler. It prepares the program text for the parser by dividing the characters into categories called *tokens*. For example, `=` is a different token from `==` and `+` is a different token from `++` — the lexer takes care of figuring out which is which in a tricky expression like `x+++x`." 8 | }, 9 | { 10 | "file": "src/types.ts", 11 | "description": "Our first stop is the Token type. You can see that there are basically 4 categories of tokens:\n\n1. Keywords like `function`, `var`, `return`.\n2. 'words' like Literal (`'hi'` and `123`), Whitespace (`' ', '\\t', '\\n'`) and Identifier (`x, y, aLongIdentifier`).\n3. Single characters like `=`, `;`. This would include *digraphs* like `==` and `>=` if mini-typescript supported them, but it doesn't.\n4. Abstract characters like Unknown, BOF (Beginning of File) and EOF (End of File).", 12 | "line": 1 13 | }, 14 | { 15 | "file": "src/types.ts", 16 | "description": "The Lexer type exposes two categories of functions:\n\n1. `scan` advances to the next token.\n2. `token`, `pos`, `text` return the respective parts of the lexer's state.\n\nTypically, the parser calls `token` to decide what to do based on the current token. It might collect additional information with `pos` and `text`, then advances to the next token with `scan`.", 17 | "line": 17 18 | }, 19 | { 20 | "file": "src/types.ts", 21 | "description": "`text` returns the text of word-like tokens -- that is, `'hi'` for strings, `x` for identifiers, `1` for numbers. The parser should only call this function when the *current* token is known to be word-like, because the lexer just keeps the text around from the last word-like token.", 22 | "line": 21 23 | }, 24 | { 25 | "file": "src/lex.ts", 26 | "description": "All the complexity of the lexer is in `scan`. `token`, `pos` and `text` just expose the state of the lexer.", 27 | "line": 13 28 | }, 29 | { 30 | "file": "src/lex.ts", 31 | "description": "`scan` first skips all whitespace by calling the `scanForward` utility.\n\nThe real Typescript lexer retains some information about this step to help with ASI, and it also saves the position before scanning forward.", 32 | "line": 19 33 | }, 34 | { 35 | "file": "src/lex.ts", 36 | "description": "`scanForward` is a `while` loop that increments its position as long as the predicate function is true.\n\nI can't remember if the real Typescript lexer works like this. I don't think it does.", 37 | "line": 44 38 | }, 39 | { 40 | "file": "src/lex.ts", 41 | "description": "mini-typescript's lexer first checks whether it's at the end of the source text and sets `token` to `EOF` if so. That's the signal for the parser to stop parsing.\n\nNotice that `scan` doesn't *return*: it sets `token` instead. mini-typescript is simple enough that it can just run to the end of the function, but Typescript is not.", 42 | "line": 21 43 | }, 44 | { 45 | "file": "src/lex.ts", 46 | "description": "Then it checks for numbers -- mini-typescript only supports integers, so the regexes are simple.\nNote that `scan` sets both `text` and `token` for numbers.\nAlso, mini-typescript doesn't support string, boolean or bigint literals, so it just calls the number literal token `NumericLiteral`.", 47 | "line": 24 48 | }, 49 | { 50 | "file": "src/lex.ts", 51 | "description": "Third, the scanner checks for identifiers. This is basically the same as lexing a number.\n\nThe real Typescript lexer uses Unicode classes for identifiers.", 52 | "line": 29 53 | }, 54 | { 55 | "file": "src/lex.ts", 56 | "description": "Identifiers get turned into keywords if they're found as keys in the `keywords` object.", 57 | "line": 32 58 | }, 59 | { 60 | "file": "src/lex.ts", 61 | "description": "Finally, single characters fall through a giant switch statement with all the punctuation tokens.\n\nThe real Typescript lexer handles digraphs like `==` by checking for another `=` in the `=` case. But mini-typescript doesn't support any digraphs.", 62 | "line": 36 63 | }, 64 | { 65 | "file": "src/lex.ts", 66 | "description": "`lexAll` is a convenience function used only for testing: it turns a string into an *array* of tokens instead of an object that lets you iterate a *stream* of tokens. It makes a good, simple example of how to use the lexer.", 67 | "line": 48 68 | }, 69 | { 70 | "file": "src/lex.ts", 71 | "description": "First, create a lexer (plus an array to hold the tokens and a variable to hold the current token).", 72 | "line": 49 73 | }, 74 | { 75 | "file": "src/lex.ts", 76 | "description": "1. Advance to the next token.", 77 | "line": 53 78 | }, 79 | { 80 | "file": "src/lex.ts", 81 | "description": "2. Save the token.", 82 | "line": 54 83 | }, 84 | { 85 | "file": "src/lex.ts", 86 | "description": "3. Quit if the token is `EOF`.", 87 | "line": 56 88 | }, 89 | { 90 | "file": "src/lex.ts", 91 | "description": "4. For word-like tokens, save both the token and its text. Otherwise just save the token.", 92 | "line": 58 93 | } 94 | ] 95 | } -------------------------------------------------------------------------------- /.tours/overview.tour: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://aka.ms/codetour-schema", 3 | "title": "Overview", 4 | "steps": [ 5 | { 6 | "file": "src/index.ts", 7 | "description": "`mini-typescript` is a miniature Typescript compiler intended to teach you how the real Typescript compiler works. It only compiles a tiny slice of the Typescript language, but for that tiny slice, it does almost everything that the real compiler does. (It doesn't cover the language service -- mini-typescript is just a batch compiler.)\n\nThis overview covers all the compiler phases that mini-typescript has. But first, let's look at how the batch compiler `mtsc` works.", 8 | "line": 2 9 | }, 10 | { 11 | "file": "src/index.ts", 12 | "description": "First of all `mtsc` processes arguments. The only allowed syntax is `mtsc file.ts`, so the code is pretty simple.", 13 | "line": 4 14 | }, 15 | { 16 | "file": "src/index.ts", 17 | "description": "Then it reads the file and passes it to `compile`. `compile` returns the parse tree, a list of errors and the output JS file. If mini-typescript had an API, it would return the parse tree, but `mtsc` just ignores it.\n\nLet's look at `compile` next.", 18 | "line": 14 19 | }, 20 | { 21 | "file": "src/compile.ts", 22 | "description": "`mini-typescript` is a pipeline of the following phases:\n\n- Lex\n- Parse\n- Bind\n- Check\n- Transform\n- Emit\n\nThese phases are common to almost all compilers, although they may be combined.\n\nOne important phase that `mini-typescript` omits is module resolution. And both Typescript and `mini-typescript` have relatively simple transform and emit phases, since their target is Javascript, a language that is very close to Typescript.\n\nI'll briefly explain each phase now.", 23 | "line": 10 24 | }, 25 | { 26 | "file": "src/compile.ts", 27 | "description": "The lexer breaks source text down into *tokens*, which represent words and punctuation. Lexing makes it easier for the parser by distinguishing `=` from `==` or `+` from `++`, for example. Some more examples:\n\n* Keywords: `Function`, `Var`, `Let`\n* Punctuation: `Equals`, `EqualsEquals`, `LeftBrace`, `RightBrace`\n* Names: `Identifier`", 28 | "line": 3 29 | }, 30 | { 31 | "file": "src/compile.ts", 32 | "description": "Using the tokens that the lexer produces, the parser builds a *tree* to represent the structure of the program. For example, a function has a name, a list of parameters and a body. The body in turn contains a list of statements. So when a parser sees the Function keyword, it knows the next token should be Identifier, followed by a list of parameters and then a function body. From those 3 parts it produces something like this object:\n\n```ts\n{\n name: \"foo\",\n parameters: [...],\n body: { statements: [...] }\n}\n```\n\nAll the phases after the parser work by visiting each node in the parse tree. That is, for the example function above, there is code to check the function itself, plus code that checks the parameters and the body recursively.", 33 | "line": 4 34 | }, 35 | { 36 | "file": "src/compile.ts", 37 | "description": "The binder produces an *environment*, which is a table that maps names to where they're declared. For example, if you declare `var x = 1`, the binder will record that `x` is declared at `var x = 1`. This lets the checker *resolve* a name to a declaration so it can figure out what its type is.\n\nIn a full language, many constructs have their own environments because they introduce a new *scope*, which is an area where names are valid. For example, functions have parameters and local variables that are only valid inside their body, and classes have properties that behave similarly. In mini-typescript, only modules have an environment because there are no functions or classes.", 38 | "line": 5 39 | }, 40 | { 41 | "file": "src/compile.ts", 42 | "description": "The checker checks each node in the parse tree and issues an error whenever it finds something wrong.\n\nAlthough the checker *mainly* checks types, it can check lots of other things too. Even in mini-typescript, it issues an error for an undeclared identifier. But Typescript's checker has errors for incorrect use of modifiers like `public` and `override`, errors for incorrect imports and even errors for complex incorrect syntax, among many others.", 43 | "line": 6 44 | }, 45 | { 46 | "file": "src/compile.ts", 47 | "description": "The transformer converts the Typescript parse tree into a Javascript parse tree. For mini-typescript, this just means removing type annotations. The same is true for the full Typescript compiler when you target ES Next, but when you target old ES versions like ES2015, the compiler converts new features like object spread into `Object.assign` calls.\n\nNotably, the transformer works with no information from the binder or checker. It just uses the parse tree. In the full Typescript compiler, this is important for fast emit of single files as you edit.", 48 | "line": 7 49 | }, 50 | { 51 | "file": "src/compile.ts", 52 | "description": "The emitter converts a Javascript parse tree into a string. It's basically a giant `toString`.\n\nNotably, the emitter can emit a Typescript parse tree too, since the only difference is that TS has type annotations.", 53 | "line": 8 54 | }, 55 | { 56 | "file": "src/compile.ts", 57 | "description": "That leaves 3 more pieces of the compiler to cover:\n\n1. Types\n2. Errors\n3. Tests", 58 | "line": 2 59 | }, 60 | { 61 | "file": "src/types.ts", 62 | "description": "Typescript puts all of its types into a single file named `types.ts`. They're all here, for every single phase.", 63 | "line": 2 64 | }, 65 | { 66 | "file": "src/error.ts", 67 | "description": "`mini-typescript` maintains a global array of errors that any phase can add to. To avoid dupes and follow-on errors, only the first error at a position is shown. Typescript works similarly, except that it distinguishes between syntax errors (from the parser), semantic errors (from the binder+checker), and suggestions (only shown in the editor).", 68 | "line": 2 69 | }, 70 | { 71 | "file": "src/test.ts", 72 | "description": "`mini-typescript` uses *baselines* for its tests, kind of like Jest snapshots. So does Typescript. Basically, you write a .ts file, and the tests make sure that the compiler's tree, javascript and errors match the ones from the baseline.", 73 | "line": 6 74 | } 75 | ] 76 | } -------------------------------------------------------------------------------- /.tours/parser.tour: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://aka.ms/codetour-schema", 3 | "title": "Parser", 4 | "steps": [ 5 | { 6 | "title": "Introduction", 7 | "description": "The parser builds a *tree* to represent the structure of the program. For example, a function has a name, a list of parameters and a body. The body in turn contains a list of statements. So when a parser sees the Function keyword, it knows the next token should be Identifier, followed by a list of parameters and then a function body. From those 3 parts it produces something like this object:\n\n```ts\n{\n name: \"foo\",\n parameters: [...],\n body: { statements: [...] }\n}\n```\n\nAll the phases after the parser work by visiting each node in the parse tree. For example, to emit a function, the emitter writes \"function\", followed by the function name. Then it recurs on the function's parameters and body." 8 | }, 9 | { 10 | "file": "src/types.ts", 11 | "description": "The `Node` enum is used as the `kind` discriminant for node types. Conveniently, this acts as an index of all the node types that the parser can construct.\n\nYou can see that mini-typescript parses only a few different kinds of nodes.", 12 | "line": 23 13 | }, 14 | { 15 | "file": "src/types.ts", 16 | "description": "`Expression` is a union of all the expression types. For mini-typescript, that's just `Identifier`, `NumericLiteral` and `Assignment`.\n\nThe real Typescript compiler doesn't have unions for all its types -- for many it uses interface inheritance instead. That's mostly for historical reasons, because discriminated unions weren't available until Typescript 2.0.", 17 | "line": 38 18 | }, 19 | { 20 | "file": "src/types.ts", 21 | "description": "Similarly, mini-typescript's node types use intersection to add `Location` for error reporting. Typescript uses interface inheritance.", 22 | "line": 39 23 | }, 24 | { 25 | "file": "src/types.ts", 26 | "description": "`Statement` consists of `ExpressionStatement`, `Var` and `TypeAlias`. `ExpressionStatement` is an expression that is used for its side-effects, like `Assignment`: `x = 1`.", 27 | "line": 52 28 | }, 29 | { 30 | "file": "src/types.ts", 31 | "description": "The parser returns a `Module`, which is really just a file. It ambitiously assumes that everyone will soon be writing nothing but modules in mini-typescript.\n\nThe parser only fills in `statements`; the binder fills in `locals`.", 32 | "line": 74 33 | }, 34 | { 35 | "file": "src/parse.ts", 36 | "description": "A module consists of statements, separated by semicolons. `parseSeparated` alternates calling `parseStatement` and `tryParseToken(Token.Semicolon)` until the latter returns false.\nThe final call to `parseExpected(Token.EOF)` logs an error if there's stray text at the end of the file after the last statement.", 37 | "line": 8 38 | }, 39 | { 40 | "file": "src/parse.ts", 41 | "description": "`tryParseToken` shows off basic lexer usage: first it checks whether the lexer's current token is the one expected by the caller. If it is, it advances to the next token. Otherwise it stays put. This lets the caller inspect the lexer's current token and report errors on it.", 42 | "line": 57 43 | }, 44 | { 45 | "file": "src/parse.ts", 46 | "description": "Both mini-typescript and Typescript use a hand-written recursive descent parser. These parser are collections of functions that recursively call each other. For example `parseStatement` recursively calls `parseIdentifier` and `parseExpression`.", 47 | "line": 40 48 | }, 49 | { 50 | "file": "src/parse.ts", 51 | "description": " Then `parseExpression` recursively calls itself for assignments.", 52 | "line": 16 53 | }, 54 | { 55 | "file": "src/parse.ts", 56 | "description": "The easiest grammar rules to parse have unambiguous start tokens. Here, statements that start with a `var` keyword are `Var`, those that start with `type` are `Type`, and all others are `ExpressionStatements`.\n\nThe original rules are obscured by the code in a recursive descent grammar, but if you work you can see them:\n\n```\nStatement -> Expression | 'var' Identifier [':' Identifier] '=' Expression | 'type' Identifer '=' Identifier\nExpression -> *literal* | Identifier ['=' Expression]\n```", 57 | "line": 42 58 | } 59 | ] 60 | } -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [imteekay] 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nathan Shively-Sanders 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mini-typescript 2 | 3 | This is a fork from [Nathan Shively-Sanders](https://github.com/sandersn)'s [mini-typescript](https://github.com/sandersn/mini-typescript). 4 | 5 | The purpose of this repo is to practice the exercises and learn more about compilers and TypeScript. 6 | 7 | ### To get set up 8 | 9 | ```bash 10 | # Get set up 11 | npm i 12 | npm run build 13 | 14 | # Or have your changes instantly happen 15 | npm run build --watch 16 | 17 | # Run the compiler: 18 | npm run mtsc ./tests/singleVar.ts 19 | ``` 20 | 21 | ## Limitations 22 | 23 | 1. This is an example of the way that Typescript's compiler does things. A compiler textbook will help you learn _compilers_. This project will help you learn _Typescript's code_. 24 | 2. This is only a tiny slice of the language, also unlike a textbook. Often I only put it one instance of a thing, like nodes that introduce a scope, to keep the code size small. 25 | 3. There is no laziness, caching or node reuse, so the checker and transformer code do not teach you those aspects of the design. 26 | 4. There's no surrounding infrastructure, like a language service or a program builder. This is just a model of tsc. 27 | 28 | ## Exercises 29 | 30 | - [x] Add EmptyStatement (https://github.com/imteekay/mini-typescript-fork/pull/2). 31 | - [x] Make semicolon a statement ender, not statement separator (https://github.com/imteekay/mini-typescript-fork/pull/7). 32 | - Hint: You'll need a predicate to peek at the next token and decide if it's the start of an element. 33 | - [ ] Bonus: Switch from semicolon to newline as statement ender. 34 | - [x] Add string literals (https://github.com/imteekay/mini-typescript-fork/pull/4). 35 | - [x] Refactor: rename `Literal` to `NumericLiteral` (https://github.com/imteekay/mini-typescript-fork/pull/6). 36 | - [x] Add support for the lexer to report errors (https://github.com/imteekay/mini-typescript-fork/pull/12) 37 | - report unterminated string literal error 38 | - [x] Add let (https://github.com/imteekay/mini-typescript-fork/pull/8). 39 | - [x] Make sure the binder resolves variables declared with `var` and `let` the same way. The simplest way is to add a `kind` property to `Symbol`. 40 | - [x] Add use-before-declaration errors in the checker. 41 | - [x] Finally, add an ES2015 -> ES5 transform that transforms `let` to `var`. 42 | - [x] Allow var statements to declare multiple symbols (https://github.com/imteekay/mini-typescript-fork/pull/9). 43 | - [x] Allow var to have multiple declarations. (https://github.com/imteekay/mini-typescript/pull/2) 44 | - You'll need to convert a Symbol's declaration into a list. 45 | - Check that all declarations have the same type. 46 | - [ ] Add objects and object types. 47 | - [ ] Implement mapped types 48 | - [ ] Implement optional member 49 | - [ ] Implement method signature 50 | - [ ] Add `interface`. 51 | - Make sure the binder resolves types declared with `type` and `interface` the same way. 52 | - After the basics are working, allow interface to have multiple declarations. 53 | - Interfaces should have an object type, but that object type should combine the properties from every declaration. 54 | - [ ] Implement union types 55 | - [ ] Add an ES5 transformer that converts `let` -> `var`. 56 | - [ ] Add function declarations and function calls. 57 | - [ ] Add arrow functions with an appropriate transform in ES5. 58 | -------------------------------------------------------------------------------- /baselines/reference/basicLex.lex.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "Identifier", 4 | "x" 5 | ] 6 | ] -------------------------------------------------------------------------------- /baselines/reference/emptyStatement.errors.baseline: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /baselines/reference/emptyStatement.js.baseline: -------------------------------------------------------------------------------- 1 | "var x = 1;\n;\nvar y = 2;\n" -------------------------------------------------------------------------------- /baselines/reference/emptyStatement.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "x": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 18 7 | } 8 | ], 9 | "y": [ 10 | { 11 | "kind": "VariableDeclaration", 12 | "pos": 37 13 | } 14 | ] 15 | }, 16 | "statements": [ 17 | { 18 | "kind": "VariableStatement", 19 | "declarationList": { 20 | "kind": "VariableDeclarationList", 21 | "declarations": { 22 | "0": { 23 | "kind": "VariableDeclaration", 24 | "name": { 25 | "kind": "Identifier", 26 | "text": "x" 27 | }, 28 | "typename": { 29 | "kind": "Identifier", 30 | "text": "number" 31 | }, 32 | "init": { 33 | "kind": "NumericLiteral", 34 | "value": 1 35 | } 36 | } 37 | }, 38 | "flags": 0 39 | } 40 | }, 41 | { 42 | "kind": "EmptyStatement" 43 | }, 44 | { 45 | "kind": "VariableStatement", 46 | "declarationList": { 47 | "kind": "VariableDeclarationList", 48 | "declarations": { 49 | "0": { 50 | "kind": "VariableDeclaration", 51 | "name": { 52 | "kind": "Identifier", 53 | "text": "y" 54 | }, 55 | "typename": { 56 | "kind": "Identifier", 57 | "text": "number" 58 | }, 59 | "init": { 60 | "kind": "NumericLiteral", 61 | "value": 2 62 | } 63 | } 64 | }, 65 | "flags": 0 66 | } 67 | } 68 | ] 69 | } -------------------------------------------------------------------------------- /baselines/reference/firstLex.lex.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "NumericLiteral", 4 | "1200" 5 | ], 6 | [ 7 | "Identifier", 8 | "Hello" 9 | ], 10 | [ 11 | "Identifier", 12 | "World1" 13 | ], 14 | [ 15 | "Unknown" 16 | ], 17 | [ 18 | "NumericLiteral", 19 | "14" 20 | ], 21 | [ 22 | "Identifier", 23 | "d" 24 | ] 25 | ] -------------------------------------------------------------------------------- /baselines/reference/multipleDeclarationsWithDifferentTypes.errors.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pos": 122, 4 | "message": "Subsequent variable declarations must have the same type. Variable 's3' must be of type 'number', but here has type 'string'." 5 | }, 6 | { 7 | "pos": 168, 8 | "message": "Subsequent variable declarations must have the same type. Variable 's4' must be of type 'string', but here has type 'number'." 9 | }, 10 | { 11 | "pos": 222, 12 | "message": "Cannot assign initialiser of type 'number' to variable with declared type 'string'." 13 | } 14 | ] -------------------------------------------------------------------------------- /baselines/reference/multipleDeclarationsWithDifferentTypes.js.baseline: -------------------------------------------------------------------------------- 1 | "var s = 'test';\nvar s1 = 'test';\nvar s2 = 'test';\nvar s2 = 'test';\nvar s3 = 2;\nvar s3 = 'test';\nvar s4 = 'test';\nvar s4 = 2;\nvar s4 = 'test';\nvar s5 = 'test';\nvar s5 = 2;\n" -------------------------------------------------------------------------------- /baselines/reference/multipleDeclarationsWithDifferentTypes.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "s": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 15 7 | } 8 | ], 9 | "s1": [ 10 | { 11 | "kind": "VariableDeclaration", 12 | "pos": 41 13 | } 14 | ], 15 | "s2": [ 16 | { 17 | "kind": "VariableDeclaration", 18 | "pos": 59 19 | }, 20 | { 21 | "kind": "VariableDeclaration", 22 | "pos": 84 23 | } 24 | ], 25 | "s3": [ 26 | { 27 | "kind": "VariableDeclaration", 28 | "pos": 105 29 | }, 30 | { 31 | "kind": "VariableDeclaration", 32 | "pos": 122 33 | } 34 | ], 35 | "s4": [ 36 | { 37 | "kind": "VariableDeclaration", 38 | "pos": 148 39 | }, 40 | { 41 | "kind": "VariableDeclaration", 42 | "pos": 168 43 | }, 44 | { 45 | "kind": "VariableDeclaration", 46 | "pos": 185 47 | } 48 | ], 49 | "s5": [ 50 | { 51 | "kind": "VariableDeclaration", 52 | "pos": 203 53 | }, 54 | { 55 | "kind": "VariableDeclaration", 56 | "pos": 223 57 | } 58 | ] 59 | }, 60 | "statements": [ 61 | { 62 | "kind": "VariableStatement", 63 | "declarationList": { 64 | "kind": "VariableDeclarationList", 65 | "declarations": { 66 | "0": { 67 | "kind": "VariableDeclaration", 68 | "name": { 69 | "kind": "Identifier", 70 | "text": "s" 71 | }, 72 | "init": { 73 | "kind": "StringLiteral", 74 | "value": "test", 75 | "isSingleQuote": true 76 | } 77 | } 78 | }, 79 | "flags": 0 80 | } 81 | }, 82 | { 83 | "kind": "VariableStatement", 84 | "declarationList": { 85 | "kind": "VariableDeclarationList", 86 | "declarations": { 87 | "0": { 88 | "kind": "VariableDeclaration", 89 | "name": { 90 | "kind": "Identifier", 91 | "text": "s1" 92 | }, 93 | "typename": { 94 | "kind": "Identifier", 95 | "text": "string" 96 | }, 97 | "init": { 98 | "kind": "StringLiteral", 99 | "value": "test", 100 | "isSingleQuote": true 101 | } 102 | } 103 | }, 104 | "flags": 0 105 | } 106 | }, 107 | { 108 | "kind": "VariableStatement", 109 | "declarationList": { 110 | "kind": "VariableDeclarationList", 111 | "declarations": { 112 | "0": { 113 | "kind": "VariableDeclaration", 114 | "name": { 115 | "kind": "Identifier", 116 | "text": "s2" 117 | }, 118 | "init": { 119 | "kind": "StringLiteral", 120 | "value": "test", 121 | "isSingleQuote": true 122 | } 123 | } 124 | }, 125 | "flags": 0 126 | } 127 | }, 128 | { 129 | "kind": "VariableStatement", 130 | "declarationList": { 131 | "kind": "VariableDeclarationList", 132 | "declarations": { 133 | "0": { 134 | "kind": "VariableDeclaration", 135 | "name": { 136 | "kind": "Identifier", 137 | "text": "s2" 138 | }, 139 | "typename": { 140 | "kind": "Identifier", 141 | "text": "string" 142 | }, 143 | "init": { 144 | "kind": "StringLiteral", 145 | "value": "test", 146 | "isSingleQuote": true 147 | } 148 | } 149 | }, 150 | "flags": 0 151 | } 152 | }, 153 | { 154 | "kind": "VariableStatement", 155 | "declarationList": { 156 | "kind": "VariableDeclarationList", 157 | "declarations": { 158 | "0": { 159 | "kind": "VariableDeclaration", 160 | "name": { 161 | "kind": "Identifier", 162 | "text": "s3" 163 | }, 164 | "typename": { 165 | "kind": "Identifier", 166 | "text": "number" 167 | }, 168 | "init": { 169 | "kind": "NumericLiteral", 170 | "value": 2 171 | } 172 | } 173 | }, 174 | "flags": 0 175 | } 176 | }, 177 | { 178 | "kind": "VariableStatement", 179 | "declarationList": { 180 | "kind": "VariableDeclarationList", 181 | "declarations": { 182 | "0": { 183 | "kind": "VariableDeclaration", 184 | "name": { 185 | "kind": "Identifier", 186 | "text": "s3" 187 | }, 188 | "init": { 189 | "kind": "StringLiteral", 190 | "value": "test", 191 | "isSingleQuote": true 192 | } 193 | } 194 | }, 195 | "flags": 0 196 | } 197 | }, 198 | { 199 | "kind": "VariableStatement", 200 | "declarationList": { 201 | "kind": "VariableDeclarationList", 202 | "declarations": { 203 | "0": { 204 | "kind": "VariableDeclaration", 205 | "name": { 206 | "kind": "Identifier", 207 | "text": "s4" 208 | }, 209 | "typename": { 210 | "kind": "Identifier", 211 | "text": "string" 212 | }, 213 | "init": { 214 | "kind": "StringLiteral", 215 | "value": "test", 216 | "isSingleQuote": true 217 | } 218 | } 219 | }, 220 | "flags": 0 221 | } 222 | }, 223 | { 224 | "kind": "VariableStatement", 225 | "declarationList": { 226 | "kind": "VariableDeclarationList", 227 | "declarations": { 228 | "0": { 229 | "kind": "VariableDeclaration", 230 | "name": { 231 | "kind": "Identifier", 232 | "text": "s4" 233 | }, 234 | "typename": { 235 | "kind": "Identifier", 236 | "text": "number" 237 | }, 238 | "init": { 239 | "kind": "NumericLiteral", 240 | "value": 2 241 | } 242 | } 243 | }, 244 | "flags": 0 245 | } 246 | }, 247 | { 248 | "kind": "VariableStatement", 249 | "declarationList": { 250 | "kind": "VariableDeclarationList", 251 | "declarations": { 252 | "0": { 253 | "kind": "VariableDeclaration", 254 | "name": { 255 | "kind": "Identifier", 256 | "text": "s4" 257 | }, 258 | "init": { 259 | "kind": "StringLiteral", 260 | "value": "test", 261 | "isSingleQuote": true 262 | } 263 | } 264 | }, 265 | "flags": 0 266 | } 267 | }, 268 | { 269 | "kind": "VariableStatement", 270 | "declarationList": { 271 | "kind": "VariableDeclarationList", 272 | "declarations": { 273 | "0": { 274 | "kind": "VariableDeclaration", 275 | "name": { 276 | "kind": "Identifier", 277 | "text": "s5" 278 | }, 279 | "init": { 280 | "kind": "StringLiteral", 281 | "value": "test", 282 | "isSingleQuote": true 283 | } 284 | } 285 | }, 286 | "flags": 0 287 | } 288 | }, 289 | { 290 | "kind": "VariableStatement", 291 | "declarationList": { 292 | "kind": "VariableDeclarationList", 293 | "declarations": { 294 | "0": { 295 | "kind": "VariableDeclaration", 296 | "name": { 297 | "kind": "Identifier", 298 | "text": "s5" 299 | }, 300 | "typename": { 301 | "kind": "Identifier", 302 | "text": "string" 303 | }, 304 | "init": { 305 | "kind": "NumericLiteral", 306 | "value": 2 307 | } 308 | } 309 | }, 310 | "flags": 0 311 | } 312 | } 313 | ] 314 | } -------------------------------------------------------------------------------- /baselines/reference/newlineLex.lex.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "Identifier", 4 | "x" 5 | ], 6 | [ 7 | "Identifier", 8 | "y" 9 | ] 10 | ] -------------------------------------------------------------------------------- /baselines/reference/redeclare.errors.baseline: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /baselines/reference/redeclare.js.baseline: -------------------------------------------------------------------------------- 1 | "var x = 1;\nvar x = 2;\n" -------------------------------------------------------------------------------- /baselines/reference/redeclare.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "x": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 10 7 | }, 8 | { 9 | "kind": "VariableDeclaration", 10 | "pos": 21 11 | } 12 | ] 13 | }, 14 | "statements": [ 15 | { 16 | "kind": "VariableStatement", 17 | "declarationList": { 18 | "kind": "VariableDeclarationList", 19 | "declarations": { 20 | "0": { 21 | "kind": "VariableDeclaration", 22 | "name": { 23 | "kind": "Identifier", 24 | "text": "x" 25 | }, 26 | "init": { 27 | "kind": "NumericLiteral", 28 | "value": 1 29 | } 30 | } 31 | }, 32 | "flags": 0 33 | } 34 | }, 35 | { 36 | "kind": "VariableStatement", 37 | "declarationList": { 38 | "kind": "VariableDeclarationList", 39 | "declarations": { 40 | "0": { 41 | "kind": "VariableDeclaration", 42 | "name": { 43 | "kind": "Identifier", 44 | "text": "x" 45 | }, 46 | "init": { 47 | "kind": "NumericLiteral", 48 | "value": 2 49 | } 50 | } 51 | }, 52 | "flags": 0 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /baselines/reference/redeclareLetVar.errors.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pos": 21, 4 | "message": "Cannot redeclare x; first declared at 21" 5 | }, 6 | { 7 | "pos": 43, 8 | "message": "Cannot redeclare y; first declared at 43" 9 | } 10 | ] -------------------------------------------------------------------------------- /baselines/reference/redeclareLetVar.js.baseline: -------------------------------------------------------------------------------- 1 | "var x = 1;\nvar x = 2;\nvar y = 1;\nvar y = 2;\n" -------------------------------------------------------------------------------- /baselines/reference/redeclareLetVar.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "x": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 10 7 | } 8 | ], 9 | "y": [ 10 | { 11 | "kind": "VariableDeclaration", 12 | "pos": 32 13 | } 14 | ] 15 | }, 16 | "statements": [ 17 | { 18 | "kind": "VariableStatement", 19 | "declarationList": { 20 | "kind": "VariableDeclarationList", 21 | "declarations": { 22 | "0": { 23 | "kind": "VariableDeclaration", 24 | "name": { 25 | "kind": "Identifier", 26 | "text": "x" 27 | }, 28 | "init": { 29 | "kind": "NumericLiteral", 30 | "value": 1 31 | } 32 | } 33 | }, 34 | "flags": 0 35 | } 36 | }, 37 | { 38 | "kind": "VariableStatement", 39 | "declarationList": { 40 | "kind": "VariableDeclarationList", 41 | "declarations": { 42 | "0": { 43 | "kind": "VariableDeclaration", 44 | "name": { 45 | "kind": "Identifier", 46 | "text": "x" 47 | }, 48 | "init": { 49 | "kind": "NumericLiteral", 50 | "value": 2 51 | } 52 | } 53 | }, 54 | "flags": 1 55 | } 56 | }, 57 | { 58 | "kind": "VariableStatement", 59 | "declarationList": { 60 | "kind": "VariableDeclarationList", 61 | "declarations": { 62 | "0": { 63 | "kind": "VariableDeclaration", 64 | "name": { 65 | "kind": "Identifier", 66 | "text": "y" 67 | }, 68 | "init": { 69 | "kind": "NumericLiteral", 70 | "value": 1 71 | } 72 | } 73 | }, 74 | "flags": 1 75 | } 76 | }, 77 | { 78 | "kind": "VariableStatement", 79 | "declarationList": { 80 | "kind": "VariableDeclarationList", 81 | "declarations": { 82 | "0": { 83 | "kind": "VariableDeclaration", 84 | "name": { 85 | "kind": "Identifier", 86 | "text": "y" 87 | }, 88 | "init": { 89 | "kind": "NumericLiteral", 90 | "value": 2 91 | } 92 | } 93 | }, 94 | "flags": 0 95 | } 96 | } 97 | ] 98 | } -------------------------------------------------------------------------------- /baselines/reference/semicolonLex.lex.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "Identifier", 4 | "x" 5 | ], 6 | [ 7 | "Semicolon" 8 | ], 9 | [ 10 | "Identifier", 11 | "y" 12 | ] 13 | ] -------------------------------------------------------------------------------- /baselines/reference/singleIdentifier.errors.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pos": 1, 4 | "message": "Could not resolve x" 5 | } 6 | ] -------------------------------------------------------------------------------- /baselines/reference/singleIdentifier.js.baseline: -------------------------------------------------------------------------------- 1 | "x;\n" -------------------------------------------------------------------------------- /baselines/reference/singleIdentifier.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": {}, 3 | "statements": [ 4 | { 5 | "kind": "ExpressionStatement", 6 | "expr": { 7 | "kind": "Identifier", 8 | "text": "x" 9 | } 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /baselines/reference/singleLet.errors.baseline: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /baselines/reference/singleLet.js.baseline: -------------------------------------------------------------------------------- 1 | "var variable = 1;\nvariable;\n" -------------------------------------------------------------------------------- /baselines/reference/singleLet.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "variable": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 17 7 | } 8 | ] 9 | }, 10 | "statements": [ 11 | { 12 | "kind": "VariableStatement", 13 | "declarationList": { 14 | "kind": "VariableDeclarationList", 15 | "declarations": { 16 | "0": { 17 | "kind": "VariableDeclaration", 18 | "name": { 19 | "kind": "Identifier", 20 | "text": "variable" 21 | }, 22 | "init": { 23 | "kind": "NumericLiteral", 24 | "value": 1 25 | } 26 | } 27 | }, 28 | "flags": 1 29 | } 30 | }, 31 | { 32 | "kind": "ExpressionStatement", 33 | "expr": { 34 | "kind": "Identifier", 35 | "text": "variable" 36 | } 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /baselines/reference/singleTypedVar.errors.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pos": 17, 4 | "message": "Cannot assign initialiser of type 'number' to variable with declared type 'string'." 5 | }, 6 | { 7 | "pos": 41, 8 | "message": "Cannot assign initialiser of type 'string' to variable with declared type 'number'." 9 | } 10 | ] -------------------------------------------------------------------------------- /baselines/reference/singleTypedVar.js.baseline: -------------------------------------------------------------------------------- 1 | "var s = 1;\nvar n = 'test';\n" -------------------------------------------------------------------------------- /baselines/reference/singleTypedVar.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "s": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 18 7 | } 8 | ], 9 | "n": [ 10 | { 11 | "kind": "VariableDeclaration", 12 | "pos": 42 13 | } 14 | ] 15 | }, 16 | "statements": [ 17 | { 18 | "kind": "VariableStatement", 19 | "declarationList": { 20 | "kind": "VariableDeclarationList", 21 | "declarations": { 22 | "0": { 23 | "kind": "VariableDeclaration", 24 | "name": { 25 | "kind": "Identifier", 26 | "text": "s" 27 | }, 28 | "typename": { 29 | "kind": "Identifier", 30 | "text": "string" 31 | }, 32 | "init": { 33 | "kind": "NumericLiteral", 34 | "value": 1 35 | } 36 | } 37 | }, 38 | "flags": 0 39 | } 40 | }, 41 | { 42 | "kind": "VariableStatement", 43 | "declarationList": { 44 | "kind": "VariableDeclarationList", 45 | "declarations": { 46 | "0": { 47 | "kind": "VariableDeclaration", 48 | "name": { 49 | "kind": "Identifier", 50 | "text": "n" 51 | }, 52 | "typename": { 53 | "kind": "Identifier", 54 | "text": "number" 55 | }, 56 | "init": { 57 | "kind": "StringLiteral", 58 | "value": "test", 59 | "isSingleQuote": true 60 | } 61 | } 62 | }, 63 | "flags": 0 64 | } 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /baselines/reference/singleVar.errors.baseline: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /baselines/reference/singleVar.js.baseline: -------------------------------------------------------------------------------- 1 | "var singleDeclaration = 1;\n" -------------------------------------------------------------------------------- /baselines/reference/singleVar.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "singleDeclaration": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 26 7 | } 8 | ] 9 | }, 10 | "statements": [ 11 | { 12 | "kind": "VariableStatement", 13 | "declarationList": { 14 | "kind": "VariableDeclarationList", 15 | "declarations": { 16 | "0": { 17 | "kind": "VariableDeclaration", 18 | "name": { 19 | "kind": "Identifier", 20 | "text": "singleDeclaration" 21 | }, 22 | "init": { 23 | "kind": "NumericLiteral", 24 | "value": 1 25 | } 26 | } 27 | }, 28 | "flags": 0 29 | } 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /baselines/reference/stringLiteral.errors.baseline: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /baselines/reference/stringLiteral.js.baseline: -------------------------------------------------------------------------------- 1 | "var singleQuote = 'singleQuote';\nvar doubleQuote = \"doubleQuote\";\nvar escapedSingleQuote = 'escapedSingle\\'Quote';\nvar escapedDoubleQuote = \"escapedDouble\\\"Quote\";\nvar escapedB = 'escaped\\bB';\nvar escapedT = 'escaped\\tT';\nvar escapedN = 'escaped\\nN';\nvar escapedR = 'escaped\\rR';\n" -------------------------------------------------------------------------------- /baselines/reference/stringLiteral.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "singleQuote": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 32 7 | } 8 | ], 9 | "doubleQuote": [ 10 | { 11 | "kind": "VariableDeclaration", 12 | "pos": 65 13 | } 14 | ], 15 | "escapedSingleQuote": [ 16 | { 17 | "kind": "VariableDeclaration", 18 | "pos": 114 19 | } 20 | ], 21 | "escapedDoubleQuote": [ 22 | { 23 | "kind": "VariableDeclaration", 24 | "pos": 163 25 | } 26 | ], 27 | "escapedB": [ 28 | { 29 | "kind": "VariableDeclaration", 30 | "pos": 192 31 | } 32 | ], 33 | "escapedT": [ 34 | { 35 | "kind": "VariableDeclaration", 36 | "pos": 221 37 | } 38 | ], 39 | "escapedN": [ 40 | { 41 | "kind": "VariableDeclaration", 42 | "pos": 250 43 | } 44 | ], 45 | "escapedR": [ 46 | { 47 | "kind": "VariableDeclaration", 48 | "pos": 279 49 | } 50 | ] 51 | }, 52 | "statements": [ 53 | { 54 | "kind": "VariableStatement", 55 | "declarationList": { 56 | "kind": "VariableDeclarationList", 57 | "declarations": { 58 | "0": { 59 | "kind": "VariableDeclaration", 60 | "name": { 61 | "kind": "Identifier", 62 | "text": "singleQuote" 63 | }, 64 | "init": { 65 | "kind": "StringLiteral", 66 | "value": "singleQuote", 67 | "isSingleQuote": true 68 | } 69 | } 70 | }, 71 | "flags": 0 72 | } 73 | }, 74 | { 75 | "kind": "VariableStatement", 76 | "declarationList": { 77 | "kind": "VariableDeclarationList", 78 | "declarations": { 79 | "0": { 80 | "kind": "VariableDeclaration", 81 | "name": { 82 | "kind": "Identifier", 83 | "text": "doubleQuote" 84 | }, 85 | "init": { 86 | "kind": "StringLiteral", 87 | "value": "doubleQuote", 88 | "isSingleQuote": false 89 | } 90 | } 91 | }, 92 | "flags": 0 93 | } 94 | }, 95 | { 96 | "kind": "VariableStatement", 97 | "declarationList": { 98 | "kind": "VariableDeclarationList", 99 | "declarations": { 100 | "0": { 101 | "kind": "VariableDeclaration", 102 | "name": { 103 | "kind": "Identifier", 104 | "text": "escapedSingleQuote" 105 | }, 106 | "init": { 107 | "kind": "StringLiteral", 108 | "value": "escapedSingle'Quote", 109 | "isSingleQuote": true 110 | } 111 | } 112 | }, 113 | "flags": 0 114 | } 115 | }, 116 | { 117 | "kind": "VariableStatement", 118 | "declarationList": { 119 | "kind": "VariableDeclarationList", 120 | "declarations": { 121 | "0": { 122 | "kind": "VariableDeclaration", 123 | "name": { 124 | "kind": "Identifier", 125 | "text": "escapedDoubleQuote" 126 | }, 127 | "init": { 128 | "kind": "StringLiteral", 129 | "value": "escapedDouble\"Quote", 130 | "isSingleQuote": false 131 | } 132 | } 133 | }, 134 | "flags": 0 135 | } 136 | }, 137 | { 138 | "kind": "VariableStatement", 139 | "declarationList": { 140 | "kind": "VariableDeclarationList", 141 | "declarations": { 142 | "0": { 143 | "kind": "VariableDeclaration", 144 | "name": { 145 | "kind": "Identifier", 146 | "text": "escapedB" 147 | }, 148 | "init": { 149 | "kind": "StringLiteral", 150 | "value": "escaped\bB", 151 | "isSingleQuote": true 152 | } 153 | } 154 | }, 155 | "flags": 0 156 | } 157 | }, 158 | { 159 | "kind": "VariableStatement", 160 | "declarationList": { 161 | "kind": "VariableDeclarationList", 162 | "declarations": { 163 | "0": { 164 | "kind": "VariableDeclaration", 165 | "name": { 166 | "kind": "Identifier", 167 | "text": "escapedT" 168 | }, 169 | "init": { 170 | "kind": "StringLiteral", 171 | "value": "escaped\tT", 172 | "isSingleQuote": true 173 | } 174 | } 175 | }, 176 | "flags": 0 177 | } 178 | }, 179 | { 180 | "kind": "VariableStatement", 181 | "declarationList": { 182 | "kind": "VariableDeclarationList", 183 | "declarations": { 184 | "0": { 185 | "kind": "VariableDeclaration", 186 | "name": { 187 | "kind": "Identifier", 188 | "text": "escapedN" 189 | }, 190 | "init": { 191 | "kind": "StringLiteral", 192 | "value": "escaped\nN", 193 | "isSingleQuote": true 194 | } 195 | } 196 | }, 197 | "flags": 0 198 | } 199 | }, 200 | { 201 | "kind": "VariableStatement", 202 | "declarationList": { 203 | "kind": "VariableDeclarationList", 204 | "declarations": { 205 | "0": { 206 | "kind": "VariableDeclaration", 207 | "name": { 208 | "kind": "Identifier", 209 | "text": "escapedR" 210 | }, 211 | "init": { 212 | "kind": "StringLiteral", 213 | "value": "escaped\rR", 214 | "isSingleQuote": true 215 | } 216 | } 217 | }, 218 | "flags": 0 219 | } 220 | } 221 | ] 222 | } -------------------------------------------------------------------------------- /baselines/reference/terminator.errors.baseline: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /baselines/reference/terminator.js.baseline: -------------------------------------------------------------------------------- 1 | "var x = 1;\nvar y = 2;\nvar z = 3;\nx;\ny;\nz;\n" -------------------------------------------------------------------------------- /baselines/reference/terminator.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "x": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 10 7 | } 8 | ], 9 | "y": [ 10 | { 11 | "kind": "VariableDeclaration", 12 | "pos": 21 13 | } 14 | ], 15 | "z": [ 16 | { 17 | "kind": "VariableDeclaration", 18 | "pos": 32 19 | } 20 | ] 21 | }, 22 | "statements": [ 23 | { 24 | "kind": "VariableStatement", 25 | "declarationList": { 26 | "kind": "VariableDeclarationList", 27 | "declarations": { 28 | "0": { 29 | "kind": "VariableDeclaration", 30 | "name": { 31 | "kind": "Identifier", 32 | "text": "x" 33 | }, 34 | "init": { 35 | "kind": "NumericLiteral", 36 | "value": 1 37 | } 38 | } 39 | }, 40 | "flags": 0 41 | } 42 | }, 43 | { 44 | "kind": "VariableStatement", 45 | "declarationList": { 46 | "kind": "VariableDeclarationList", 47 | "declarations": { 48 | "0": { 49 | "kind": "VariableDeclaration", 50 | "name": { 51 | "kind": "Identifier", 52 | "text": "y" 53 | }, 54 | "init": { 55 | "kind": "NumericLiteral", 56 | "value": 2 57 | } 58 | } 59 | }, 60 | "flags": 0 61 | } 62 | }, 63 | { 64 | "kind": "VariableStatement", 65 | "declarationList": { 66 | "kind": "VariableDeclarationList", 67 | "declarations": { 68 | "0": { 69 | "kind": "VariableDeclaration", 70 | "name": { 71 | "kind": "Identifier", 72 | "text": "z" 73 | }, 74 | "init": { 75 | "kind": "NumericLiteral", 76 | "value": 3 77 | } 78 | } 79 | }, 80 | "flags": 0 81 | } 82 | }, 83 | { 84 | "kind": "ExpressionStatement", 85 | "expr": { 86 | "kind": "Identifier", 87 | "text": "x" 88 | } 89 | }, 90 | { 91 | "kind": "ExpressionStatement", 92 | "expr": { 93 | "kind": "Identifier", 94 | "text": "y" 95 | } 96 | }, 97 | { 98 | "kind": "ExpressionStatement", 99 | "expr": { 100 | "kind": "Identifier", 101 | "text": "z" 102 | } 103 | } 104 | ] 105 | } -------------------------------------------------------------------------------- /baselines/reference/twoStatements.errors.baseline: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /baselines/reference/twoStatements.js.baseline: -------------------------------------------------------------------------------- 1 | "var arthurTwoShedsJackson = 1;\narthurTwoShedsJackson = 2;\n" -------------------------------------------------------------------------------- /baselines/reference/twoStatements.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "arthurTwoShedsJackson": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 30 7 | } 8 | ] 9 | }, 10 | "statements": [ 11 | { 12 | "kind": "VariableStatement", 13 | "declarationList": { 14 | "kind": "VariableDeclarationList", 15 | "declarations": { 16 | "0": { 17 | "kind": "VariableDeclaration", 18 | "name": { 19 | "kind": "Identifier", 20 | "text": "arthurTwoShedsJackson" 21 | }, 22 | "init": { 23 | "kind": "NumericLiteral", 24 | "value": 1 25 | } 26 | } 27 | }, 28 | "flags": 0 29 | } 30 | }, 31 | { 32 | "kind": "ExpressionStatement", 33 | "expr": { 34 | "kind": "Assignment", 35 | "name": { 36 | "kind": "Identifier", 37 | "text": "arthurTwoShedsJackson" 38 | }, 39 | "value": { 40 | "kind": "NumericLiteral", 41 | "value": 2 42 | } 43 | } 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /baselines/reference/twoTypedStatements.errors.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pos": 17, 4 | "message": "Cannot assign initialiser of type 'number' to variable with declared type 'string'." 5 | }, 6 | { 7 | "pos": 24, 8 | "message": "Cannot assign value of type 'number' to variable of type 'string'." 9 | } 10 | ] -------------------------------------------------------------------------------- /baselines/reference/twoTypedStatements.js.baseline: -------------------------------------------------------------------------------- 1 | "var s = 1;\ns = 2;\n" -------------------------------------------------------------------------------- /baselines/reference/twoTypedStatements.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "s": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 18 7 | } 8 | ] 9 | }, 10 | "statements": [ 11 | { 12 | "kind": "VariableStatement", 13 | "declarationList": { 14 | "kind": "VariableDeclarationList", 15 | "declarations": { 16 | "0": { 17 | "kind": "VariableDeclaration", 18 | "name": { 19 | "kind": "Identifier", 20 | "text": "s" 21 | }, 22 | "typename": { 23 | "kind": "Identifier", 24 | "text": "string" 25 | }, 26 | "init": { 27 | "kind": "NumericLiteral", 28 | "value": 1 29 | } 30 | } 31 | }, 32 | "flags": 0 33 | } 34 | }, 35 | { 36 | "kind": "ExpressionStatement", 37 | "expr": { 38 | "kind": "Assignment", 39 | "name": { 40 | "kind": "Identifier", 41 | "text": "s" 42 | }, 43 | "value": { 44 | "kind": "NumericLiteral", 45 | "value": 2 46 | } 47 | } 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /baselines/reference/typeAlias.errors.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pos": 76, 4 | "message": "Cannot redeclare Not; first declared at 57" 5 | }, 6 | { 7 | "pos": 124, 8 | "message": "Cannot assign initialiser of type 'number' to variable with declared type 'string'." 9 | }, 10 | { 11 | "pos": 153, 12 | "message": "Could not resolve type Nut" 13 | }, 14 | { 15 | "pos": 192, 16 | "message": "Could not resolve Net" 17 | } 18 | ] -------------------------------------------------------------------------------- /baselines/reference/typeAlias.js.baseline: -------------------------------------------------------------------------------- 1 | "var Nat = 12;\nvar nat = 13;\nvar nit = 14;\nvar Nut = 15;\nvar nut = 16;\nvar net = Net;\n" -------------------------------------------------------------------------------- /baselines/reference/typeAlias.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "Nat": [ 4 | { 5 | "kind": "TypeAlias", 6 | "pos": 4 7 | }, 8 | { 9 | "kind": "VariableDeclaration", 10 | "pos": 32 11 | } 12 | ], 13 | "nat": [ 14 | { 15 | "kind": "VariableDeclaration", 16 | "pos": 51 17 | } 18 | ], 19 | "Not": [ 20 | { 21 | "kind": "TypeAlias", 22 | "pos": 57 23 | } 24 | ], 25 | "Nit": [ 26 | { 27 | "kind": "TypeAlias", 28 | "pos": 95 29 | } 30 | ], 31 | "nit": [ 32 | { 33 | "kind": "VariableDeclaration", 34 | "pos": 125 35 | } 36 | ], 37 | "Nut": [ 38 | { 39 | "kind": "VariableDeclaration", 40 | "pos": 140 41 | } 42 | ], 43 | "nut": [ 44 | { 45 | "kind": "VariableDeclaration", 46 | "pos": 159 47 | } 48 | ], 49 | "Net": [ 50 | { 51 | "kind": "TypeAlias", 52 | "pos": 164 53 | } 54 | ], 55 | "net": [ 56 | { 57 | "kind": "VariableDeclaration", 58 | "pos": 193 59 | } 60 | ] 61 | }, 62 | "statements": [ 63 | { 64 | "kind": "TypeAlias", 65 | "name": { 66 | "kind": "Identifier", 67 | "text": "Nat" 68 | }, 69 | "typename": { 70 | "kind": "Identifier", 71 | "text": "number" 72 | } 73 | }, 74 | { 75 | "kind": "VariableStatement", 76 | "declarationList": { 77 | "kind": "VariableDeclarationList", 78 | "declarations": { 79 | "0": { 80 | "kind": "VariableDeclaration", 81 | "name": { 82 | "kind": "Identifier", 83 | "text": "Nat" 84 | }, 85 | "init": { 86 | "kind": "NumericLiteral", 87 | "value": 12 88 | } 89 | } 90 | }, 91 | "flags": 0 92 | } 93 | }, 94 | { 95 | "kind": "VariableStatement", 96 | "declarationList": { 97 | "kind": "VariableDeclarationList", 98 | "declarations": { 99 | "0": { 100 | "kind": "VariableDeclaration", 101 | "name": { 102 | "kind": "Identifier", 103 | "text": "nat" 104 | }, 105 | "typename": { 106 | "kind": "Identifier", 107 | "text": "Nat" 108 | }, 109 | "init": { 110 | "kind": "NumericLiteral", 111 | "value": 13 112 | } 113 | } 114 | }, 115 | "flags": 0 116 | } 117 | }, 118 | { 119 | "kind": "TypeAlias", 120 | "name": { 121 | "kind": "Identifier", 122 | "text": "Not" 123 | }, 124 | "typename": { 125 | "kind": "Identifier", 126 | "text": "string" 127 | } 128 | }, 129 | { 130 | "kind": "TypeAlias", 131 | "name": { 132 | "kind": "Identifier", 133 | "text": "Not" 134 | }, 135 | "typename": { 136 | "kind": "Identifier", 137 | "text": "number" 138 | } 139 | }, 140 | { 141 | "kind": "TypeAlias", 142 | "name": { 143 | "kind": "Identifier", 144 | "text": "Nit" 145 | }, 146 | "typename": { 147 | "kind": "Identifier", 148 | "text": "Not" 149 | } 150 | }, 151 | { 152 | "kind": "VariableStatement", 153 | "declarationList": { 154 | "kind": "VariableDeclarationList", 155 | "declarations": { 156 | "0": { 157 | "kind": "VariableDeclaration", 158 | "name": { 159 | "kind": "Identifier", 160 | "text": "nit" 161 | }, 162 | "typename": { 163 | "kind": "Identifier", 164 | "text": "Not" 165 | }, 166 | "init": { 167 | "kind": "NumericLiteral", 168 | "value": 14 169 | } 170 | } 171 | }, 172 | "flags": 0 173 | } 174 | }, 175 | { 176 | "kind": "VariableStatement", 177 | "declarationList": { 178 | "kind": "VariableDeclarationList", 179 | "declarations": { 180 | "0": { 181 | "kind": "VariableDeclaration", 182 | "name": { 183 | "kind": "Identifier", 184 | "text": "Nut" 185 | }, 186 | "init": { 187 | "kind": "NumericLiteral", 188 | "value": 15 189 | } 190 | } 191 | }, 192 | "flags": 0 193 | } 194 | }, 195 | { 196 | "kind": "VariableStatement", 197 | "declarationList": { 198 | "kind": "VariableDeclarationList", 199 | "declarations": { 200 | "0": { 201 | "kind": "VariableDeclaration", 202 | "name": { 203 | "kind": "Identifier", 204 | "text": "nut" 205 | }, 206 | "typename": { 207 | "kind": "Identifier", 208 | "text": "Nut" 209 | }, 210 | "init": { 211 | "kind": "NumericLiteral", 212 | "value": 16 213 | } 214 | } 215 | }, 216 | "flags": 0 217 | } 218 | }, 219 | { 220 | "kind": "TypeAlias", 221 | "name": { 222 | "kind": "Identifier", 223 | "text": "Net" 224 | }, 225 | "typename": { 226 | "kind": "Identifier", 227 | "text": "number" 228 | } 229 | }, 230 | { 231 | "kind": "VariableStatement", 232 | "declarationList": { 233 | "kind": "VariableDeclarationList", 234 | "declarations": { 235 | "0": { 236 | "kind": "VariableDeclaration", 237 | "name": { 238 | "kind": "Identifier", 239 | "text": "net" 240 | }, 241 | "init": { 242 | "kind": "Identifier", 243 | "text": "Net" 244 | } 245 | } 246 | }, 247 | "flags": 0 248 | } 249 | } 250 | ] 251 | } -------------------------------------------------------------------------------- /baselines/reference/typedNumber.lex.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "Var" 4 | ], 5 | [ 6 | "Identifier", 7 | "num" 8 | ], 9 | [ 10 | "Colon" 11 | ], 12 | [ 13 | "Identifier", 14 | "number" 15 | ], 16 | [ 17 | "Equals" 18 | ], 19 | [ 20 | "NumericLiteral", 21 | "1" 22 | ], 23 | [ 24 | "Semicolon" 25 | ] 26 | ] -------------------------------------------------------------------------------- /baselines/reference/typedString.lex.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "Var" 4 | ], 5 | [ 6 | "Identifier", 7 | "s" 8 | ], 9 | [ 10 | "Colon" 11 | ], 12 | [ 13 | "Identifier", 14 | "string" 15 | ], 16 | [ 17 | "Equals" 18 | ], 19 | [ 20 | "String", 21 | "string" 22 | ], 23 | [ 24 | "Semicolon" 25 | ] 26 | ] -------------------------------------------------------------------------------- /baselines/reference/typedVariableDeclarationList.errors.baseline: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /baselines/reference/typedVariableDeclarationList.js.baseline: -------------------------------------------------------------------------------- 1 | "var n = 1, s = 'test', n2 = 3;\n" -------------------------------------------------------------------------------- /baselines/reference/typedVariableDeclarationList.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "n": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 18 7 | } 8 | ], 9 | "s": [ 10 | { 11 | "kind": "VariableDeclaration", 12 | "pos": 38 13 | } 14 | ], 15 | "n2": [ 16 | { 17 | "kind": "VariableDeclaration", 18 | "pos": 54 19 | } 20 | ] 21 | }, 22 | "statements": [ 23 | { 24 | "kind": "VariableStatement", 25 | "declarationList": { 26 | "kind": "VariableDeclarationList", 27 | "declarations": { 28 | "0": { 29 | "kind": "VariableDeclaration", 30 | "name": { 31 | "kind": "Identifier", 32 | "text": "n" 33 | }, 34 | "typename": { 35 | "kind": "Identifier", 36 | "text": "number" 37 | }, 38 | "init": { 39 | "kind": "NumericLiteral", 40 | "value": 1 41 | } 42 | }, 43 | "1": { 44 | "kind": "VariableDeclaration", 45 | "name": { 46 | "kind": "Identifier", 47 | "text": "s" 48 | }, 49 | "typename": { 50 | "kind": "Identifier", 51 | "text": "string" 52 | }, 53 | "init": { 54 | "kind": "StringLiteral", 55 | "value": "test", 56 | "isSingleQuote": true 57 | } 58 | }, 59 | "2": { 60 | "kind": "VariableDeclaration", 61 | "name": { 62 | "kind": "Identifier", 63 | "text": "n2" 64 | }, 65 | "typename": { 66 | "kind": "Identifier", 67 | "text": "number" 68 | }, 69 | "init": { 70 | "kind": "NumericLiteral", 71 | "value": 3 72 | } 73 | } 74 | }, 75 | "flags": 0 76 | } 77 | } 78 | ] 79 | } -------------------------------------------------------------------------------- /baselines/reference/underscoreLex.lex.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "Identifier", 4 | "x_y" 5 | ], 6 | [ 7 | "Identifier", 8 | "is" 9 | ], 10 | [ 11 | "Identifier", 12 | "_aSingle" 13 | ], 14 | [ 15 | "Identifier", 16 | "Identifier_" 17 | ] 18 | ] -------------------------------------------------------------------------------- /baselines/reference/unterminatedString.errors.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pos": 54, 4 | "message": "Unterminated string literal" 5 | } 6 | ] -------------------------------------------------------------------------------- /baselines/reference/unterminatedString.js.baseline: -------------------------------------------------------------------------------- 1 | "var unterminatedString = \"unterminatedString;\\n\";\n" -------------------------------------------------------------------------------- /baselines/reference/unterminatedString.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "unterminatedString": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 54 7 | } 8 | ] 9 | }, 10 | "statements": [ 11 | { 12 | "kind": "VariableStatement", 13 | "declarationList": { 14 | "kind": "VariableDeclarationList", 15 | "declarations": { 16 | "0": { 17 | "kind": "VariableDeclaration", 18 | "name": { 19 | "kind": "Identifier", 20 | "text": "unterminatedString" 21 | }, 22 | "typename": { 23 | "kind": "Identifier", 24 | "text": "string" 25 | }, 26 | "init": { 27 | "kind": "StringLiteral", 28 | "value": "unterminatedString;\n", 29 | "isSingleQuote": false 30 | } 31 | } 32 | }, 33 | "flags": 0 34 | } 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /baselines/reference/usedBeforeDeclaration.errors.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pos": 8, 4 | "message": "Block-scoped variable 'variable' used before its declaration." 5 | } 6 | ] -------------------------------------------------------------------------------- /baselines/reference/usedBeforeDeclaration.js.baseline: -------------------------------------------------------------------------------- 1 | "variable;\nvar variable = 1;\n" -------------------------------------------------------------------------------- /baselines/reference/usedBeforeDeclaration.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "variable": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 27 7 | } 8 | ] 9 | }, 10 | "statements": [ 11 | { 12 | "kind": "ExpressionStatement", 13 | "expr": { 14 | "kind": "Identifier", 15 | "text": "variable" 16 | } 17 | }, 18 | { 19 | "kind": "VariableStatement", 20 | "declarationList": { 21 | "kind": "VariableDeclarationList", 22 | "declarations": { 23 | "0": { 24 | "kind": "VariableDeclaration", 25 | "name": { 26 | "kind": "Identifier", 27 | "text": "variable" 28 | }, 29 | "init": { 30 | "kind": "NumericLiteral", 31 | "value": 1 32 | } 33 | } 34 | }, 35 | "flags": 1 36 | } 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /baselines/reference/varLex.lex.baseline: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "Var" 4 | ], 5 | [ 6 | "Identifier", 7 | "x" 8 | ], 9 | [ 10 | "Equals" 11 | ], 12 | [ 13 | "NumericLiteral", 14 | "1" 15 | ] 16 | ] -------------------------------------------------------------------------------- /baselines/reference/varWithMultipleDeclarations.errors.baseline: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /baselines/reference/varWithMultipleDeclarations.js.baseline: -------------------------------------------------------------------------------- 1 | "var var1 = 'test';\nvar var1 = 'var';\nvar var1 = 'no type annotation needed';\n" -------------------------------------------------------------------------------- /baselines/reference/varWithMultipleDeclarations.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "var1": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 26 7 | }, 8 | { 9 | "kind": "VariableDeclaration", 10 | "pos": 70 11 | }, 12 | { 13 | "kind": "VariableDeclaration", 14 | "pos": 105 15 | } 16 | ] 17 | }, 18 | "statements": [ 19 | { 20 | "kind": "VariableStatement", 21 | "declarationList": { 22 | "kind": "VariableDeclarationList", 23 | "declarations": { 24 | "0": { 25 | "kind": "VariableDeclaration", 26 | "name": { 27 | "kind": "Identifier", 28 | "text": "var1" 29 | }, 30 | "typename": { 31 | "kind": "Identifier", 32 | "text": "string" 33 | }, 34 | "init": { 35 | "kind": "StringLiteral", 36 | "value": "test", 37 | "isSingleQuote": true 38 | } 39 | } 40 | }, 41 | "flags": 0 42 | } 43 | }, 44 | { 45 | "kind": "VariableStatement", 46 | "declarationList": { 47 | "kind": "VariableDeclarationList", 48 | "declarations": { 49 | "0": { 50 | "kind": "VariableDeclaration", 51 | "name": { 52 | "kind": "Identifier", 53 | "text": "var1" 54 | }, 55 | "typename": { 56 | "kind": "Identifier", 57 | "text": "string" 58 | }, 59 | "init": { 60 | "kind": "StringLiteral", 61 | "value": "var", 62 | "isSingleQuote": true 63 | } 64 | } 65 | }, 66 | "flags": 0 67 | } 68 | }, 69 | { 70 | "kind": "VariableStatement", 71 | "declarationList": { 72 | "kind": "VariableDeclarationList", 73 | "declarations": { 74 | "0": { 75 | "kind": "VariableDeclaration", 76 | "name": { 77 | "kind": "Identifier", 78 | "text": "var1" 79 | }, 80 | "init": { 81 | "kind": "StringLiteral", 82 | "value": "no type annotation needed", 83 | "isSingleQuote": true 84 | } 85 | } 86 | }, 87 | "flags": 0 88 | } 89 | } 90 | ] 91 | } -------------------------------------------------------------------------------- /baselines/reference/variableDeclarationList.errors.baseline: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /baselines/reference/variableDeclarationList.js.baseline: -------------------------------------------------------------------------------- 1 | "var var1 = 1, var2 = 2, var3 = 3;\n" -------------------------------------------------------------------------------- /baselines/reference/variableDeclarationList.tree.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "locals": { 3 | "var1": [ 4 | { 5 | "kind": "VariableDeclaration", 6 | "pos": 13 7 | } 8 | ], 9 | "var2": [ 10 | { 11 | "kind": "VariableDeclaration", 12 | "pos": 23 13 | } 14 | ], 15 | "var3": [ 16 | { 17 | "kind": "VariableDeclaration", 18 | "pos": 33 19 | } 20 | ] 21 | }, 22 | "statements": [ 23 | { 24 | "kind": "VariableStatement", 25 | "declarationList": { 26 | "kind": "VariableDeclarationList", 27 | "declarations": { 28 | "0": { 29 | "kind": "VariableDeclaration", 30 | "name": { 31 | "kind": "Identifier", 32 | "text": "var1" 33 | }, 34 | "init": { 35 | "kind": "NumericLiteral", 36 | "value": 1 37 | } 38 | }, 39 | "1": { 40 | "kind": "VariableDeclaration", 41 | "name": { 42 | "kind": "Identifier", 43 | "text": "var2" 44 | }, 45 | "init": { 46 | "kind": "NumericLiteral", 47 | "value": 2 48 | } 49 | }, 50 | "2": { 51 | "kind": "VariableDeclaration", 52 | "name": { 53 | "kind": "Identifier", 54 | "text": "var3" 55 | }, 56 | "init": { 57 | "kind": "NumericLiteral", 58 | "value": 3 59 | } 60 | } 61 | }, 62 | "flags": 0 63 | } 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /draft.md: -------------------------------------------------------------------------------- 1 | # Learnings 2 | 3 | ## How to debug 4 | 5 | - Use the [`astexplorer`](https://astexplorer.net) to understand tokens/AST 6 | - Use the [`TS AST Viewer`](https://ts-ast-viewer.com) to understand the AST nodes (check text, symbols/declarations) 7 | - Read the [`ECMAScript specification`](https://tc39.es/ecma262/multipage/#sec-intro) 8 | - Use the [`ts playground`](https://www.typescriptlang.org/play) to see the JS output 9 | - Run `npm run test:file test-filename.ts` to debug only one test at a time. Replace the `test-filename.ts` with the test file name you want to test 10 | - Add the baselines and run `npm run test` and compare the local with the references 11 | 12 | ## Lexer 13 | 14 | - scan forward all 15 | - \t - tabs 16 | - \b - empty strings at the beginning and end of a word 17 | - \n - newline char 18 | - if at the end of input, it's a `EOF` token 19 | - if numbers, scan all numbers, add the number to the `text`, and the `NumericLiteral` token to the `token` 20 | - if alphabetical chars, scan all chars, add the string to the `text`, and the token can be a `Keyword` or an `Identifier` 21 | - scan tokens like `Equals`, `Semicolon`, `Colon`, etc 22 | - if not in the list, it's a `Unknown` token 23 | 24 | ## Parser 25 | 26 | - Use the lexer to walkthrough the tokens and create the AST nodes 27 | - Parse statements 28 | - Var statements 29 | - type statements 30 | - expressions 31 | - Var AST node 32 | - name -> identifier 33 | - typename -> type definition (if there's no `Colon` token, the `typename` should be `undefined`) 34 | - init -> the value 35 | - pos -> position 36 | 37 | ### AST nodes 38 | 39 | #### Identifier 40 | 41 | Source code: `s;` 42 | 43 | ```json 44 | { 45 | "kind": "Identifier", 46 | "text": "s" 47 | } 48 | ``` 49 | 50 | #### NumericLiteral 51 | 52 | Source code: `1;` 53 | 54 | ```json 55 | { 56 | "kind": "NumericLiteral", 57 | "value": 1 58 | } 59 | ``` 60 | 61 | #### Assignment 62 | 63 | Source code: `example = 2;` 64 | 65 | ```json 66 | { 67 | "kind": "Assignment", 68 | "name": { 69 | "kind": "Identifier", 70 | "text": "example" 71 | }, 72 | "value": { 73 | "kind": "NumericLiteral", 74 | "value": 2 75 | } 76 | } 77 | ``` 78 | 79 | #### ExpressionStatement 80 | 81 | Source code: `example = 2;` 82 | 83 | ```json 84 | { 85 | "kind": "ExpressionStatement", 86 | "expr": { 87 | "kind": "Assignment", 88 | "name": { 89 | "kind": "Identifier", 90 | "text": "arthurTwoShedsJackson" 91 | }, 92 | "value": { 93 | "kind": "NumericLiteral", 94 | "value": 2 95 | } 96 | } 97 | } 98 | ``` 99 | 100 | #### Var 101 | 102 | Source code: `var s: string = 1;` 103 | 104 | ```json 105 | { 106 | "kind": "Var", 107 | "name": { 108 | "kind": "Identifier", 109 | "text": "s" 110 | }, 111 | "typename": { 112 | "kind": "Identifier", 113 | "text": "string" 114 | }, 115 | "init": { 116 | "kind": "NumericLiteral", 117 | "value": 1 118 | } 119 | } 120 | ``` 121 | 122 | Source code: `var s = 1;` 123 | 124 | ```json 125 | { 126 | "kind": "Var", 127 | "name": { 128 | "kind": "Identifier", 129 | "text": "s" 130 | }, 131 | "init": { 132 | "kind": "NumericLiteral", 133 | "value": 1 134 | } 135 | } 136 | ``` 137 | 138 | #### TypeAlias 139 | 140 | Source code: `type Int = number;` 141 | 142 | ```json 143 | { 144 | "kind": "TypeAlias", 145 | "name": { 146 | "kind": "Identifier", 147 | "text": "Int" 148 | }, 149 | "typename": { 150 | "kind": "Identifier", 151 | "text": "number" 152 | } 153 | } 154 | ``` 155 | 156 | Source code: `type Int = number; var int: Int = 10;` 157 | 158 | ```json 159 | { 160 | "kind": "Var", 161 | "name": { 162 | "kind": "Identifier", 163 | "text": "int" 164 | }, 165 | "typename": { 166 | "kind": "Identifier", 167 | "text": "Int" 168 | }, 169 | "init": { 170 | "kind": "NumericLiteral", 171 | "value": 10 172 | } 173 | } 174 | ``` 175 | 176 | ## Checker 177 | 178 | - `checkExpression` 179 | - when it's a `var` node 180 | - runs `checkExpression` to get the type of the expression's `value` 181 | - runs `checkType` to get the type of the `typename` 182 | - if there's no `typename`, it should just return the `value`'s type 183 | - if there's the `typename`, compare the `typename`'s type with the the `value`'s type 184 | - if there's a mismatch, add a new type error to the compiler 185 | - when it's a `expression` node 186 | - runs `checkExpression` 187 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-typescript", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "mini-typescript", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/node": "20.4.0", 13 | "ts-node": "^10.9.1", 14 | "typescript": "latest" 15 | } 16 | }, 17 | "node_modules/@cspotcode/source-map-support": { 18 | "version": "0.8.1", 19 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 20 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 21 | "dev": true, 22 | "dependencies": { 23 | "@jridgewell/trace-mapping": "0.3.9" 24 | }, 25 | "engines": { 26 | "node": ">=12" 27 | } 28 | }, 29 | "node_modules/@jridgewell/resolve-uri": { 30 | "version": "3.1.1", 31 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", 32 | "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", 33 | "dev": true, 34 | "engines": { 35 | "node": ">=6.0.0" 36 | } 37 | }, 38 | "node_modules/@jridgewell/sourcemap-codec": { 39 | "version": "1.4.15", 40 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 41 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 42 | "dev": true 43 | }, 44 | "node_modules/@jridgewell/trace-mapping": { 45 | "version": "0.3.9", 46 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 47 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 48 | "dev": true, 49 | "dependencies": { 50 | "@jridgewell/resolve-uri": "^3.0.3", 51 | "@jridgewell/sourcemap-codec": "^1.4.10" 52 | } 53 | }, 54 | "node_modules/@tsconfig/node10": { 55 | "version": "1.0.9", 56 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", 57 | "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", 58 | "dev": true 59 | }, 60 | "node_modules/@tsconfig/node12": { 61 | "version": "1.0.11", 62 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 63 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 64 | "dev": true 65 | }, 66 | "node_modules/@tsconfig/node14": { 67 | "version": "1.0.3", 68 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 69 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 70 | "dev": true 71 | }, 72 | "node_modules/@tsconfig/node16": { 73 | "version": "1.0.4", 74 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", 75 | "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", 76 | "dev": true 77 | }, 78 | "node_modules/@types/node": { 79 | "version": "20.4.0", 80 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz", 81 | "integrity": "sha512-jfT7iTf/4kOQ9S7CHV9BIyRaQqHu67mOjsIQBC3BKZvzvUB6zLxEwJ6sBE3ozcvP8kF6Uk5PXN0Q+c0dfhGX0g==", 82 | "dev": true 83 | }, 84 | "node_modules/acorn": { 85 | "version": "8.10.0", 86 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", 87 | "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", 88 | "dev": true, 89 | "bin": { 90 | "acorn": "bin/acorn" 91 | }, 92 | "engines": { 93 | "node": ">=0.4.0" 94 | } 95 | }, 96 | "node_modules/acorn-walk": { 97 | "version": "8.2.0", 98 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 99 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 100 | "dev": true, 101 | "engines": { 102 | "node": ">=0.4.0" 103 | } 104 | }, 105 | "node_modules/arg": { 106 | "version": "4.1.3", 107 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 108 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 109 | "dev": true 110 | }, 111 | "node_modules/create-require": { 112 | "version": "1.1.1", 113 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 114 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 115 | "dev": true 116 | }, 117 | "node_modules/diff": { 118 | "version": "4.0.2", 119 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 120 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 121 | "dev": true, 122 | "engines": { 123 | "node": ">=0.3.1" 124 | } 125 | }, 126 | "node_modules/make-error": { 127 | "version": "1.3.6", 128 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 129 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 130 | "dev": true 131 | }, 132 | "node_modules/ts-node": { 133 | "version": "10.9.1", 134 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", 135 | "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", 136 | "dev": true, 137 | "dependencies": { 138 | "@cspotcode/source-map-support": "^0.8.0", 139 | "@tsconfig/node10": "^1.0.7", 140 | "@tsconfig/node12": "^1.0.7", 141 | "@tsconfig/node14": "^1.0.0", 142 | "@tsconfig/node16": "^1.0.2", 143 | "acorn": "^8.4.1", 144 | "acorn-walk": "^8.1.1", 145 | "arg": "^4.1.0", 146 | "create-require": "^1.1.0", 147 | "diff": "^4.0.1", 148 | "make-error": "^1.1.1", 149 | "v8-compile-cache-lib": "^3.0.1", 150 | "yn": "3.1.1" 151 | }, 152 | "bin": { 153 | "ts-node": "dist/bin.js", 154 | "ts-node-cwd": "dist/bin-cwd.js", 155 | "ts-node-esm": "dist/bin-esm.js", 156 | "ts-node-script": "dist/bin-script.js", 157 | "ts-node-transpile-only": "dist/bin-transpile.js", 158 | "ts-script": "dist/bin-script-deprecated.js" 159 | }, 160 | "peerDependencies": { 161 | "@swc/core": ">=1.2.50", 162 | "@swc/wasm": ">=1.2.50", 163 | "@types/node": "*", 164 | "typescript": ">=2.7" 165 | }, 166 | "peerDependenciesMeta": { 167 | "@swc/core": { 168 | "optional": true 169 | }, 170 | "@swc/wasm": { 171 | "optional": true 172 | } 173 | } 174 | }, 175 | "node_modules/typescript": { 176 | "version": "4.3.2", 177 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", 178 | "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", 179 | "dev": true, 180 | "bin": { 181 | "tsc": "bin/tsc", 182 | "tsserver": "bin/tsserver" 183 | }, 184 | "engines": { 185 | "node": ">=4.2.0" 186 | } 187 | }, 188 | "node_modules/v8-compile-cache-lib": { 189 | "version": "3.0.1", 190 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 191 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 192 | "dev": true 193 | }, 194 | "node_modules/yn": { 195 | "version": "3.1.1", 196 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 197 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 198 | "dev": true, 199 | "engines": { 200 | "node": ">=6" 201 | } 202 | } 203 | }, 204 | "dependencies": { 205 | "@cspotcode/source-map-support": { 206 | "version": "0.8.1", 207 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 208 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 209 | "dev": true, 210 | "requires": { 211 | "@jridgewell/trace-mapping": "0.3.9" 212 | } 213 | }, 214 | "@jridgewell/resolve-uri": { 215 | "version": "3.1.1", 216 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", 217 | "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", 218 | "dev": true 219 | }, 220 | "@jridgewell/sourcemap-codec": { 221 | "version": "1.4.15", 222 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 223 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 224 | "dev": true 225 | }, 226 | "@jridgewell/trace-mapping": { 227 | "version": "0.3.9", 228 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 229 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 230 | "dev": true, 231 | "requires": { 232 | "@jridgewell/resolve-uri": "^3.0.3", 233 | "@jridgewell/sourcemap-codec": "^1.4.10" 234 | } 235 | }, 236 | "@tsconfig/node10": { 237 | "version": "1.0.9", 238 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", 239 | "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", 240 | "dev": true 241 | }, 242 | "@tsconfig/node12": { 243 | "version": "1.0.11", 244 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 245 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 246 | "dev": true 247 | }, 248 | "@tsconfig/node14": { 249 | "version": "1.0.3", 250 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 251 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 252 | "dev": true 253 | }, 254 | "@tsconfig/node16": { 255 | "version": "1.0.4", 256 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", 257 | "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", 258 | "dev": true 259 | }, 260 | "@types/node": { 261 | "version": "20.4.0", 262 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz", 263 | "integrity": "sha512-jfT7iTf/4kOQ9S7CHV9BIyRaQqHu67mOjsIQBC3BKZvzvUB6zLxEwJ6sBE3ozcvP8kF6Uk5PXN0Q+c0dfhGX0g==", 264 | "dev": true 265 | }, 266 | "acorn": { 267 | "version": "8.10.0", 268 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", 269 | "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", 270 | "dev": true 271 | }, 272 | "acorn-walk": { 273 | "version": "8.2.0", 274 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 275 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 276 | "dev": true 277 | }, 278 | "arg": { 279 | "version": "4.1.3", 280 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 281 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 282 | "dev": true 283 | }, 284 | "create-require": { 285 | "version": "1.1.1", 286 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 287 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 288 | "dev": true 289 | }, 290 | "diff": { 291 | "version": "4.0.2", 292 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 293 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 294 | "dev": true 295 | }, 296 | "make-error": { 297 | "version": "1.3.6", 298 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 299 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 300 | "dev": true 301 | }, 302 | "ts-node": { 303 | "version": "10.9.1", 304 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", 305 | "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", 306 | "dev": true, 307 | "requires": { 308 | "@cspotcode/source-map-support": "^0.8.0", 309 | "@tsconfig/node10": "^1.0.7", 310 | "@tsconfig/node12": "^1.0.7", 311 | "@tsconfig/node14": "^1.0.0", 312 | "@tsconfig/node16": "^1.0.2", 313 | "acorn": "^8.4.1", 314 | "acorn-walk": "^8.1.1", 315 | "arg": "^4.1.0", 316 | "create-require": "^1.1.0", 317 | "diff": "^4.0.1", 318 | "make-error": "^1.1.1", 319 | "v8-compile-cache-lib": "^3.0.1", 320 | "yn": "3.1.1" 321 | } 322 | }, 323 | "typescript": { 324 | "version": "4.3.2", 325 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", 326 | "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", 327 | "dev": true 328 | }, 329 | "v8-compile-cache-lib": { 330 | "version": "3.0.1", 331 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 332 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 333 | "dev": true 334 | }, 335 | "yn": { 336 | "version": "3.1.1", 337 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 338 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 339 | "dev": true 340 | } 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-typescript", 3 | "version": "1.0.0", 4 | "description": "A miniature model of the TypeScript compiler", 5 | "main": "compiler/index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "test": "rm baselines/local/*; tsc && node built/test.js", 9 | "test:file": "tsc && node built/testFile.js", 10 | "test:parser": "ts-node ./src/testParser", 11 | "test:binder": "ts-node ./src/testBinder", 12 | "accept": "mv baselines/local/* baselines/reference/", 13 | "mtsc": "node built/index.js" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/sandersn/mini-typescript.git" 18 | }, 19 | "keywords": [ 20 | "TypeScript", 21 | "compiler", 22 | "miniature", 23 | "model", 24 | "example", 25 | "teaching" 26 | ], 27 | "author": "Nathan Shively-Sanders", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/sandersn/mini-typescript/issues" 31 | }, 32 | "homepage": "https://github.com/sandersn/mini-typescript#readme", 33 | "devDependencies": { 34 | "@types/node": "20.4.0", 35 | "ts-node": "^10.9.1", 36 | "typescript": "latest" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/bind.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Module, 3 | Node, 4 | NodeFlags, 5 | Statement, 6 | SymbolFlags, 7 | Table, 8 | TypeAlias, 9 | VariableDeclaration, 10 | } from './types'; 11 | import { error } from './error'; 12 | 13 | export function bind(m: Module) { 14 | for (const statement of m.statements) { 15 | bindStatement(m.locals, statement); 16 | } 17 | 18 | function bindStatement(locals: Table, statement: Statement) { 19 | if (statement.kind === Node.VariableStatement) { 20 | statement.declarationList.declarations.forEach((declaration) => 21 | bindSymbol(locals, declaration, statement.declarationList.flags), 22 | ); 23 | } 24 | 25 | if (statement.kind === Node.TypeAlias) { 26 | bindTypeSymbol(locals, statement); 27 | } 28 | } 29 | 30 | function bindTypeSymbol(locals: Table, declaration: TypeAlias) { 31 | const symbol = locals.get(declaration.name.text); 32 | if (symbol) { 33 | const other = symbol.declarations.find( 34 | (d) => d.kind === declaration.kind, 35 | ); 36 | if (other) { 37 | error( 38 | declaration.pos, 39 | `Cannot redeclare ${declaration.name.text}; first declared at ${other.pos}`, 40 | ); 41 | } else { 42 | symbol.declarations.push(declaration); 43 | } 44 | } else { 45 | locals.set(declaration.name.text, { 46 | declarations: [declaration], 47 | valueDeclaration: undefined, 48 | flags: SymbolFlags.Type, 49 | }); 50 | } 51 | } 52 | 53 | function bindSymbol( 54 | locals: Table, 55 | declaration: VariableDeclaration, 56 | flags: NodeFlags, 57 | ) { 58 | const symbol = locals.get(declaration.name.text); 59 | const isLet = flags & NodeFlags.Let; 60 | if (symbol) { 61 | const hasOther = 62 | willRedeclareLet(flags, symbol.flags) || 63 | willRedeclareVarWithLet(flags, symbol.flags) || 64 | willRedeclareLetWithVar(flags, symbol.flags); 65 | if (hasOther) { 66 | error( 67 | declaration.pos, 68 | `Cannot redeclare ${declaration.name.text}; first declared at ${declaration.pos}`, 69 | ); 70 | } else { 71 | symbol.declarations.push(declaration); 72 | symbol.valueDeclaration ||= declaration; 73 | symbol.flags |= isLet 74 | ? SymbolFlags.BlockScopedVariable 75 | : SymbolFlags.FunctionScopedVariable; 76 | } 77 | } else { 78 | locals.set(declaration.name.text, { 79 | declarations: [declaration], 80 | valueDeclaration: declaration, 81 | flags: isLet 82 | ? SymbolFlags.BlockScopedVariable 83 | : SymbolFlags.FunctionScopedVariable, 84 | }); 85 | } 86 | } 87 | 88 | function willRedeclareLet(nodeFlags: NodeFlags, symbolFlags: SymbolFlags) { 89 | return ( 90 | nodeFlags & NodeFlags.Let && symbolFlags & SymbolFlags.BlockScopedVariable 91 | ); 92 | } 93 | 94 | function willRedeclareVarWithLet( 95 | nodeFlags: NodeFlags, 96 | symbolFlags: SymbolFlags, 97 | ) { 98 | return ( 99 | nodeFlags & NodeFlags.Let && 100 | symbolFlags & SymbolFlags.FunctionScopedVariable 101 | ); 102 | } 103 | 104 | function willRedeclareLetWithVar( 105 | nodeFlags: NodeFlags, 106 | symbolFlags: SymbolFlags, 107 | ) { 108 | return !nodeFlags && symbolFlags & SymbolFlags.BlockScopedVariable; 109 | } 110 | } 111 | 112 | export function resolve(locals: Table, name: string, meaning: SymbolFlags) { 113 | const symbol = locals.get(name); 114 | if (symbol && symbol.flags & meaning) { 115 | return symbol; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/check.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Module, 3 | Statement, 4 | Type, 5 | Node, 6 | Expression, 7 | Identifier, 8 | TypeAlias, 9 | VariableDeclaration, 10 | SymbolFlags, 11 | Symbol, 12 | } from './types'; 13 | import { error } from './error'; 14 | import { resolve } from './bind'; 15 | 16 | const stringType: Type = { id: 'string' }; 17 | const numberType: Type = { id: 'number' }; 18 | const errorType: Type = { id: 'error' }; 19 | const empty: Type = { id: 'empty' }; 20 | const anyType: Type = { id: 'any' }; 21 | 22 | function typeToString(type: Type) { 23 | return type.id; 24 | } 25 | 26 | export function check(module: Module) { 27 | return module.statements.map(checkStatement); 28 | 29 | function checkStatement(statement: Statement): Type { 30 | switch (statement.kind) { 31 | case Node.ExpressionStatement: 32 | return checkExpression(statement.expr); 33 | case Node.TypeAlias: 34 | return checkType(statement.typename); 35 | case Node.VariableStatement: 36 | statement.declarationList.declarations.forEach( 37 | checkVariableDeclaration, 38 | ); 39 | return anyType; 40 | case Node.EmptyStatement: 41 | return empty; 42 | } 43 | } 44 | 45 | function checkExpression(expression: Expression): Type { 46 | switch (expression.kind) { 47 | case Node.Identifier: 48 | const symbol = resolve( 49 | module.locals, 50 | expression.text, 51 | SymbolFlags.FunctionScopedVariable | SymbolFlags.BlockScopedVariable, 52 | ); 53 | 54 | if (symbol) { 55 | if (isBlockScopedVarUsedBeforeItsDeclaration(symbol, expression)) { 56 | error( 57 | expression.pos, 58 | `Block-scoped variable '${expression.text}' used before its declaration.`, 59 | ); 60 | } 61 | 62 | return checkVariableDeclaration(symbol.valueDeclaration!); 63 | } 64 | 65 | error(expression.pos, 'Could not resolve ' + expression.text); 66 | return errorType; 67 | case Node.NumericLiteral: 68 | return numberType; 69 | case Node.StringLiteral: 70 | return stringType; 71 | case Node.Assignment: 72 | const v = checkExpression(expression.value); 73 | const t = checkExpression(expression.name); 74 | if (t !== v) 75 | error( 76 | expression.value.pos, 77 | `Cannot assign value of type '${typeToString( 78 | v, 79 | )}' to variable of type '${typeToString(t)}'.`, 80 | ); 81 | return t; 82 | } 83 | } 84 | 85 | function isBlockScopedVarUsedBeforeItsDeclaration( 86 | symbol: Symbol, 87 | expression: Expression, 88 | ) { 89 | const isBlockScopedVar = symbol.flags & SymbolFlags.BlockScopedVariable; 90 | return isBlockScopedVar && symbol.valueDeclaration!.pos > expression.pos; 91 | } 92 | 93 | function checkVariableDeclaration(declaration: VariableDeclaration) { 94 | const initType = checkExpression(declaration.init); 95 | const symbol = resolve( 96 | module.locals, 97 | declaration.name.text, 98 | SymbolFlags.FunctionScopedVariable, 99 | ); 100 | 101 | if (symbol && declaration !== symbol.valueDeclaration) { 102 | const valueDeclarationType = checkVariableDeclarationType( 103 | symbol.valueDeclaration!, 104 | ); 105 | 106 | const type = declaration.typename 107 | ? checkType(declaration.typename) 108 | : initType; 109 | 110 | handleSubsequentVariableDeclarationsTypes( 111 | declaration, 112 | valueDeclarationType, 113 | type, 114 | ); 115 | } 116 | 117 | if (!declaration.typename) { 118 | return initType; 119 | } 120 | 121 | const type = checkType(declaration.typename); 122 | if (type !== initType && type !== errorType) 123 | error( 124 | declaration.init.pos, 125 | `Cannot assign initialiser of type '${typeToString( 126 | initType, 127 | )}' to variable with declared type '${typeToString(type)}'.`, 128 | ); 129 | return type; 130 | } 131 | 132 | function checkVariableDeclarationType(declaration: VariableDeclaration) { 133 | return declaration.typename 134 | ? checkType(declaration.typename) 135 | : checkExpression(declaration.init); 136 | } 137 | 138 | function checkType(name: Identifier): Type { 139 | switch (name.text) { 140 | case 'string': 141 | return stringType; 142 | case 'number': 143 | return numberType; 144 | default: 145 | const symbol = resolve(module.locals, name.text, SymbolFlags.Type); 146 | if (symbol) { 147 | return checkType( 148 | ( 149 | symbol.declarations.find( 150 | (d) => d.kind === Node.TypeAlias, 151 | ) as TypeAlias 152 | ).typename, 153 | ); 154 | } 155 | error(name.pos, 'Could not resolve type ' + name.text); 156 | return errorType; 157 | } 158 | } 159 | 160 | function handleSubsequentVariableDeclarationsTypes( 161 | declaration: VariableDeclaration, 162 | valueDeclarationType: Type, 163 | declarationType: Type, 164 | ) { 165 | if (valueDeclarationType !== declarationType) { 166 | error( 167 | declaration.pos, 168 | `Subsequent variable declarations must have the same type. Variable '${ 169 | declaration.name.text 170 | }' must be of type '${typeToString( 171 | valueDeclarationType, 172 | )}', but here has type '${typeToString(declarationType)}'.`, 173 | ); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/compile.ts: -------------------------------------------------------------------------------- 1 | import { CompilerOptions, Error, Module } from './types'; 2 | import { errors } from './error'; 3 | import { lex } from './lex'; 4 | import { parse } from './parse'; 5 | import { bind } from './bind'; 6 | import { check } from './check'; 7 | import { transform } from './transform'; 8 | import { emit } from './emit'; 9 | 10 | const compilerOptions: CompilerOptions = { 11 | target: 'es5', 12 | }; 13 | 14 | export function compile(sourceCode: string): [Module, Error[], string] { 15 | errors.clear(); 16 | const tree = parse(lex(sourceCode)); 17 | bind(tree); 18 | check(tree); 19 | const js = emit(transform(tree.statements, compilerOptions)); 20 | return [tree, Array.from(errors.values()), js]; 21 | } 22 | -------------------------------------------------------------------------------- /src/emit.ts: -------------------------------------------------------------------------------- 1 | import { Statement, Node, Expression, VariableDeclaration } from './types'; 2 | 3 | const singleQuoteRegex = /[\\\'\t\v\f\b\r\n]/g; 4 | const doubleQuoteRegex = /[\\\"\t\v\f\b\r\n]/g; 5 | 6 | const escapedCharsMap = new Map( 7 | Object.entries({ 8 | '\t': '\\t', 9 | '\v': '\\v', 10 | '\f': '\\f', 11 | '\b': '\\b', 12 | '\r': '\\r', 13 | '\n': '\\n', 14 | '\\': '\\\\', 15 | '"': '\\"', 16 | "'": "\\'", 17 | }), 18 | ); 19 | 20 | export function emit(statements: Statement[]) { 21 | return statements 22 | .map((statement) => `${emitStatement(statement)};\n`) 23 | .join(''); 24 | } 25 | 26 | function emitStatement(statement: Statement): string { 27 | switch (statement.kind) { 28 | case Node.ExpressionStatement: 29 | return emitExpression(statement.expr); 30 | case Node.VariableStatement: 31 | return `var ${statement.declarationList.declarations 32 | .map(emitVar) 33 | .join(', ')}`; 34 | case Node.TypeAlias: 35 | return `type ${statement.name.text} = ${statement.typename.text}`; 36 | case Node.EmptyStatement: 37 | return ''; 38 | } 39 | } 40 | 41 | function emitExpression(expression: Expression): string { 42 | switch (expression.kind) { 43 | case Node.Identifier: 44 | return expression.text; 45 | case Node.NumericLiteral: 46 | return '' + expression.value; 47 | case Node.StringLiteral: 48 | return expression.isSingleQuote 49 | ? `'${escapeString(expression.value, true)}'` 50 | : `"${escapeString(expression.value, false)}"`; 51 | case Node.Assignment: 52 | return `${expression.name.text} = ${emitExpression(expression.value)}`; 53 | } 54 | } 55 | 56 | function emitVar(declaration: VariableDeclaration) { 57 | const typestring = declaration.typename ? ': ' + declaration.name : ''; 58 | return `${declaration.name.text}${typestring} = ${emitExpression( 59 | declaration.init, 60 | )}`; 61 | } 62 | 63 | function escapeString(string: string, isSingleQuote: boolean) { 64 | return string.replace( 65 | isSingleQuote ? singleQuoteRegex : doubleQuoteRegex, 66 | replacement, 67 | ); 68 | } 69 | 70 | function replacement(char: string) { 71 | return escapedCharsMap.get(char) || char; 72 | } 73 | -------------------------------------------------------------------------------- /src/error.ts: -------------------------------------------------------------------------------- 1 | import { Error } from './types'; 2 | 3 | export const errors: Map = new Map(); 4 | export function error(pos: number, message: string) { 5 | if (!errors.has(pos)) { 6 | errors.set(pos, { pos, message }); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import fs = require('fs'); 2 | import { compile } from './compile'; 3 | 4 | const args = process.argv.slice(2); 5 | const title = (str: string) => console.log('\x1b[1m%s\x1b[0m', str); 6 | 7 | if (!args.length) { 8 | console.error('Expected a path to a TS file as the argument'); 9 | process.exit(1); 10 | } 11 | 12 | title(`Looking at: ${args[0]}\n`); 13 | const ts = fs.readFileSync(args[0], 'utf8'); 14 | const [_tree, errors, js] = compile(ts); 15 | 16 | title('> TS input:'); 17 | console.log(ts); 18 | 19 | if (errors.length) { 20 | title('> Errors:'); 21 | console.log(errors); 22 | } 23 | 24 | title('> Output:'); 25 | console.log(js); 26 | 27 | // Print errors, write js to file 28 | fs.writeFileSync(args[0] + '.js', js); 29 | -------------------------------------------------------------------------------- /src/lex.ts: -------------------------------------------------------------------------------- 1 | import { error } from './error'; 2 | import { Token, Lexer, CharCodes } from './types'; 3 | 4 | const keywords = { 5 | function: Token.Function, 6 | var: Token.Var, 7 | let: Token.Let, 8 | type: Token.Type, 9 | return: Token.Return, 10 | }; 11 | 12 | export function lex(s: string): Lexer { 13 | let pos = 0; 14 | let text = ''; 15 | let token = Token.BOF; 16 | let firstChar: string; 17 | 18 | return { 19 | scan, 20 | token: () => token, 21 | pos: () => pos, 22 | text: () => text, 23 | isSingleQuote: () => firstChar === "'", 24 | }; 25 | 26 | function scan() { 27 | scanForward(isEmptyStrings); 28 | const start = pos; 29 | 30 | if (pos === s.length) { 31 | token = Token.EOF; 32 | } else if (/[0-9]/.test(s.charAt(pos))) { 33 | scanForward(isNumber); 34 | text = s.slice(start, pos); 35 | token = Token.NumericLiteral; 36 | } else if (/[_a-zA-Z]/.test(s.charAt(pos))) { 37 | scanForward(isAlphanumerical); 38 | text = s.slice(start, pos); 39 | token = 40 | text in keywords 41 | ? keywords[text as keyof typeof keywords] 42 | : Token.Identifier; 43 | } else if (['"', "'"].includes(s.charAt(pos))) { 44 | firstChar = s.charAt(pos); 45 | text = scanString(); 46 | token = Token.String; 47 | } else { 48 | pos++; 49 | switch (s.charAt(pos - 1)) { 50 | case '=': 51 | token = Token.Equals; 52 | break; 53 | case ';': 54 | token = Token.Semicolon; 55 | break; 56 | case ':': 57 | token = Token.Colon; 58 | break; 59 | case ',': 60 | token = Token.Comma; 61 | break; 62 | default: 63 | token = Token.Unknown; 64 | break; 65 | } 66 | } 67 | } 68 | 69 | function isEmptyStrings(c: string) { 70 | // scan forward all 71 | // \t - tabs 72 | // \b - empty strings at the beginning and end of a word 73 | // \n - newline char 74 | return /[ \t\b\n]/.test(c); 75 | } 76 | 77 | function isNumber(c: string) { 78 | return /[0-9]/.test(c); 79 | } 80 | 81 | function isAlphanumerical(c: string) { 82 | return /[_a-zA-Z0-9]/.test(c); 83 | } 84 | 85 | function scanForward(pred: (x: string) => boolean) { 86 | while (pos < s.length && pred(s.charAt(pos))) pos++; 87 | } 88 | 89 | function scanString() { 90 | const quote = s.charCodeAt(pos); 91 | pos++; 92 | 93 | let stringValue = ''; 94 | let start = pos; 95 | 96 | while (true) { 97 | if (pos >= s.length) { 98 | error(pos, 'Unterminated string literal'); 99 | stringValue += s.slice(start, pos); 100 | break; 101 | } 102 | 103 | const char = s.charCodeAt(pos); 104 | 105 | if (char === quote) { 106 | stringValue += s.slice(start, pos); 107 | pos++; 108 | break; 109 | } 110 | 111 | if (char === CharCodes.backslash) { 112 | stringValue += s.slice(start, pos); 113 | stringValue += scanEscapeSequence(); 114 | start = pos; 115 | continue; 116 | } 117 | 118 | pos++; 119 | } 120 | 121 | return stringValue; 122 | } 123 | 124 | function scanEscapeSequence() { 125 | pos++; 126 | const char = s.charCodeAt(pos); 127 | pos++; 128 | 129 | switch (char) { 130 | case CharCodes.b: 131 | return '\b'; 132 | case CharCodes.t: 133 | return '\t'; 134 | case CharCodes.n: 135 | return '\n'; 136 | case CharCodes.r: 137 | return '\r'; 138 | case CharCodes.singleQuote: 139 | // prettier-ignore 140 | return "\'"; 141 | case CharCodes.doubleQuote: 142 | // prettier-ignore 143 | return '\"'; 144 | default: 145 | return String.fromCharCode(char); 146 | } 147 | } 148 | } 149 | 150 | export function lexAll(s: string) { 151 | const lexer = lex(s); 152 | let tokens = []; 153 | let token; 154 | 155 | while (true) { 156 | lexer.scan(); 157 | token = lexer.token(); 158 | 159 | switch (token) { 160 | case Token.EOF: 161 | return tokens; 162 | case Token.Identifier: 163 | case Token.NumericLiteral: 164 | case Token.String: 165 | tokens.push({ token, text: lexer.text() }); 166 | break; 167 | default: 168 | tokens.push({ token }); 169 | break; 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/parse.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Lexer, 3 | Token, 4 | Node, 5 | Statement, 6 | Identifier, 7 | Expression, 8 | Module, 9 | VariableDeclaration, 10 | NodeFlags, 11 | VariableStatement, 12 | } from './types'; 13 | import { error } from './error'; 14 | 15 | export function parse(lexer: Lexer): Module { 16 | lexer.scan(); 17 | return parseModule(); 18 | 19 | function parseModule(): Module { 20 | return { 21 | statements: parseStatements( 22 | parseStatement, 23 | () => tryParseToken(Token.Semicolon), 24 | () => lexer.token() !== Token.EOF, 25 | ), 26 | locals: new Map(), 27 | }; 28 | } 29 | 30 | function parseExpression(): Expression { 31 | const pos = lexer.pos(); 32 | const e = parseIdentifierOrLiteral(); 33 | if (e.kind === Node.Identifier && tryParseToken(Token.Equals)) { 34 | return { kind: Node.Assignment, name: e, value: parseExpression(), pos }; 35 | } 36 | return e; 37 | } 38 | 39 | function parseIdentifierOrLiteral(): Expression { 40 | const pos = lexer.pos(); 41 | if (tryParseToken(Token.Identifier)) { 42 | return { kind: Node.Identifier, text: lexer.text(), pos }; 43 | } else if (tryParseToken(Token.NumericLiteral)) { 44 | return { kind: Node.NumericLiteral, value: +lexer.text(), pos }; 45 | } else if (tryParseToken(Token.String)) { 46 | return { 47 | kind: Node.StringLiteral, 48 | value: lexer.text(), 49 | pos, 50 | isSingleQuote: lexer.isSingleQuote(), 51 | }; 52 | } 53 | error( 54 | pos, 55 | 'Expected identifier or literal but got ' + Token[lexer.token()], 56 | ); 57 | lexer.scan(); 58 | return { kind: Node.Identifier, text: '(missing)', pos }; 59 | } 60 | 61 | function parseIdentifier(): Identifier { 62 | const e = parseIdentifierOrLiteral(); 63 | if (e.kind === Node.Identifier) { 64 | return e; 65 | } 66 | error(e.pos, 'Expected identifier but got a literal'); 67 | return { kind: Node.Identifier, text: '(missing)', pos: e.pos }; 68 | } 69 | 70 | function parseStatement(): Statement { 71 | const pos = lexer.pos(); 72 | 73 | if (tryParseToken(Token.Var)) { 74 | return parseVariableStatement(NodeFlags.None); 75 | } else if (tryParseToken(Token.Let)) { 76 | return parseVariableStatement(NodeFlags.Let); 77 | } else if (tryParseToken(Token.Type)) { 78 | const name = parseIdentifier(); 79 | parseExpected(Token.Equals); 80 | const typename = parseIdentifier(); 81 | return { kind: Node.TypeAlias, name, typename, pos }; 82 | } else if (tryParseToken(Token.Semicolon)) { 83 | return { kind: Node.EmptyStatement }; 84 | } 85 | return { kind: Node.ExpressionStatement, expr: parseExpression(), pos }; 86 | } 87 | 88 | function parseVariableStatement(flags: NodeFlags): VariableStatement { 89 | const pos = lexer.pos(); 90 | return { 91 | kind: Node.VariableStatement, 92 | pos, 93 | declarationList: { 94 | kind: Node.VariableDeclarationList, 95 | declarations: parseVariableDeclarations(), 96 | flags, 97 | pos, 98 | }, 99 | }; 100 | } 101 | 102 | function parseVariableDeclarations() { 103 | const declarations: VariableDeclaration[] = []; 104 | do { 105 | const name = parseIdentifier(); 106 | const typename = tryParseToken(Token.Colon) 107 | ? parseIdentifier() 108 | : undefined; 109 | parseExpected(Token.Equals); 110 | const init = parseExpression(); 111 | declarations.push({ 112 | kind: Node.VariableDeclaration, 113 | name, 114 | typename, 115 | init, 116 | pos: lexer.pos(), 117 | }); 118 | } while (tryParseToken(Token.Comma)); 119 | return declarations; 120 | } 121 | 122 | function tryParseToken(expected: Token) { 123 | const ok = lexer.token() === expected; 124 | if (ok) { 125 | lexer.scan(); 126 | } 127 | return ok; 128 | } 129 | 130 | function parseExpected(expected: Token) { 131 | if (!tryParseToken(expected)) { 132 | error( 133 | lexer.pos(), 134 | `parseToken: Expected ${Token[expected]} but got ${ 135 | Token[lexer.token()] 136 | }`, 137 | ); 138 | } 139 | } 140 | 141 | function parseStatements( 142 | element: () => T, 143 | terminator: () => boolean, 144 | peek: () => boolean, 145 | ) { 146 | const list = []; 147 | while (peek()) { 148 | list.push(element()); 149 | terminator(); 150 | } 151 | return list; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import { Module, Token, Node, Table } from './types'; 3 | import { lexAll } from './lex'; 4 | import { compile } from './compile'; 5 | 6 | const args = process.argv.slice(2); 7 | const write = args.includes('--write'); 8 | 9 | const strong = (str: string) => console.log('\x1b[1m%s\x1b[0m', str); 10 | 11 | function test(kind: string, name: string, value: unknown) { 12 | const reference = `baselines/reference/${name}.${kind}.baseline`; 13 | const local = `baselines/local/${name}.${kind}.baseline`; 14 | const actual = JSON.stringify(value, undefined, 2); 15 | const expected = fs.existsSync(reference) 16 | ? fs.readFileSync(reference, 'utf8') 17 | : ''; 18 | if (actual !== expected) { 19 | if (!fs.existsSync('./baselines/local')) fs.mkdirSync('./baselines/local'); 20 | fs.writeFileSync(local, actual); 21 | 22 | strong(`${name} failed: Expected baselines to match`); 23 | if (actual && expected) { 24 | console.log(` - result - ${local}`); 25 | console.log(` - expected - ${reference}`); 26 | console.log(` - run: diff ${local} ${reference}`); 27 | } else if (actual && !expected) { 28 | console.log(` - result - ${local}`); 29 | console.log(` - missing - ${reference}`); 30 | if (!write) { 31 | console.log(` - run with '--write' to update the baselines`); 32 | } else { 33 | console.log(` - updated baselines`); 34 | fs.writeFileSync(reference, actual); 35 | } 36 | } 37 | console.log(``); 38 | return 1; 39 | } 40 | return 0; 41 | } 42 | 43 | function sum(ns: number[]) { 44 | let total = 0; 45 | for (const n of ns) total += n; 46 | return total; 47 | } 48 | 49 | const lexTests = { 50 | basicLex: 'x', 51 | firstLex: ' 1200Hello World1! 14d', 52 | underscoreLex: 'x_y is _aSingle Identifier_', 53 | varLex: 'var x = 1', 54 | semicolonLex: 'x; y', 55 | newlineLex: 'x\n y \n', 56 | typedNumber: 'var num: number = 1;', 57 | typedString: 'var s: string = "string";', 58 | }; 59 | 60 | let lexResult = sum( 61 | Object.entries(lexTests).map(([name, text]) => 62 | test( 63 | 'lex', 64 | name, 65 | lexAll(text).map((t) => 66 | t.text ? [Token[t.token], t.text] : [Token[t.token]], 67 | ), 68 | ), 69 | ), 70 | ); 71 | 72 | let compileResult = sum( 73 | fs.readdirSync('tests').map((file) => { 74 | const [tree, errors, js] = compile( 75 | fs.readFileSync('tests/' + file, 'utf8'), 76 | ); 77 | const name = file.slice(0, file.length - 3); 78 | return ( 79 | test('tree', name, displayModule(tree)) + 80 | test('errors', name, errors) + 81 | test('js', name, js) 82 | ); 83 | }), 84 | ); 85 | 86 | function displayModule(m: Module) { 87 | return { 88 | locals: displayTable(m.locals), 89 | statements: m.statements.map(display), 90 | }; 91 | } 92 | 93 | function displayTable(table: Table) { 94 | const o = {} as any; 95 | for (const [k, v] of table) { 96 | o[k] = v.declarations.map(({ kind, pos }) => ({ kind: Node[kind], pos })); 97 | } 98 | return o; 99 | } 100 | 101 | function display(o: any) { 102 | const o2 = {} as any; 103 | for (const k in o) { 104 | if (k === 'pos') continue; 105 | else if (k === 'kind') o2[k] = Node[o.kind]; 106 | else if (typeof o[k] === 'object') o2[k] = display(o[k]); 107 | else o2[k] = o[k]; 108 | } 109 | return o2; 110 | } 111 | 112 | let result = lexResult + compileResult; 113 | 114 | if (result === 0) { 115 | strong('All tests passed'); 116 | } else { 117 | console.log(result, 'tests failed.'); 118 | } 119 | 120 | console.log(''); 121 | process.exit(result); 122 | -------------------------------------------------------------------------------- /src/testBinder.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import { Module, Node, Table } from './types'; 3 | import { parse } from './parse'; 4 | import { lex } from './lex'; 5 | import { bind } from './bind'; 6 | 7 | const args = process.argv.slice(2); 8 | const file = args[0]; 9 | 10 | if (!file) { 11 | console.log('Missing test file'); 12 | process.exit(); 13 | } 14 | 15 | function display(o: any) { 16 | const o2 = {} as any; 17 | for (const k in o) { 18 | if (k === 'pos') continue; 19 | else if (k === 'kind') o2[k] = Node[o.kind]; 20 | else if (typeof o[k] === 'object') o2[k] = display(o[k]); 21 | else o2[k] = o[k]; 22 | } 23 | return o2; 24 | } 25 | 26 | function displayTable(table: Table) { 27 | const o = {} as any; 28 | for (const [k, v] of table) { 29 | o[k] = v.declarations.map(({ kind, pos }) => ({ kind: Node[kind], pos })); 30 | } 31 | return o; 32 | } 33 | 34 | function displayModule(m: Module) { 35 | return { 36 | locals: displayTable(m.locals), 37 | statements: m.statements.map(display), 38 | }; 39 | } 40 | 41 | const tree = parse(lex(fs.readFileSync('tests/' + file, 'utf8'))); 42 | bind(tree); 43 | 44 | console.log(JSON.stringify(displayModule(tree), null, 2)); 45 | -------------------------------------------------------------------------------- /src/testFile.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import { Module, Node, Table } from './types'; 3 | import { compile } from './compile'; 4 | 5 | const args = process.argv.slice(2); 6 | const file = args[0]; 7 | 8 | if (!file) { 9 | console.log('Missing test file'); 10 | process.exit(); 11 | } 12 | 13 | function display(o: any) { 14 | const o2 = {} as any; 15 | for (const k in o) { 16 | if (k === 'pos') continue; 17 | else if (k === 'kind') o2[k] = Node[o.kind]; 18 | else if (typeof o[k] === 'object') o2[k] = display(o[k]); 19 | else o2[k] = o[k]; 20 | } 21 | return o2; 22 | } 23 | 24 | function displayTable(table: Table) { 25 | const o = {} as any; 26 | for (const [k, v] of table) { 27 | o[k] = v.declarations.map(({ kind, pos }) => ({ kind: Node[kind], pos })); 28 | } 29 | return o; 30 | } 31 | 32 | function displayModule(m: Module) { 33 | return { 34 | locals: displayTable(m.locals), 35 | statements: m.statements.map(display), 36 | }; 37 | } 38 | 39 | const [tree] = compile(fs.readFileSync('tests/' + file, 'utf8')); 40 | displayModule(tree); 41 | -------------------------------------------------------------------------------- /src/testParser.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import { Node } from './types'; 3 | import { parse } from './parse'; 4 | import { lex } from './lex'; 5 | 6 | const args = process.argv.slice(2); 7 | const file = args[0]; 8 | 9 | if (!file) { 10 | console.log('Missing test file'); 11 | process.exit(); 12 | } 13 | 14 | function display(o: any) { 15 | const o2 = {} as any; 16 | for (const k in o) { 17 | if (k === 'pos') continue; 18 | else if (k === 'kind') o2[k] = Node[o.kind]; 19 | else if (typeof o[k] === 'object') o2[k] = display(o[k]); 20 | else o2[k] = o[k]; 21 | } 22 | return o2; 23 | } 24 | 25 | console.log( 26 | JSON.stringify( 27 | display(parse(lex(fs.readFileSync('tests/' + file, 'utf8')))), 28 | null, 29 | 2, 30 | ), 31 | ); 32 | -------------------------------------------------------------------------------- /src/transform.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Statement, 3 | Node, 4 | CompilerOptions, 5 | SymbolFlags, 6 | VariableStatement, 7 | } from './types'; 8 | 9 | export function transform( 10 | statements: Statement[], 11 | compilerOptions: CompilerOptions, 12 | ) { 13 | switch (compilerOptions.target) { 14 | case 'es5': 15 | return es2015(typescript(statements)); 16 | default: 17 | return typescript(statements); 18 | } 19 | } 20 | 21 | /** Convert TS to JS: remove type annotations and declarations */ 22 | function typescript(statements: Statement[]) { 23 | return statements.flatMap(transformStatement); 24 | 25 | function transformStatement(statement: Statement): Statement[] { 26 | switch (statement.kind) { 27 | case Node.ExpressionStatement: 28 | return [statement]; 29 | case Node.TypeAlias: 30 | return []; 31 | case Node.EmptyStatement: 32 | return [statement]; 33 | case Node.VariableStatement: 34 | return [ 35 | { 36 | ...statement, 37 | declarationList: { 38 | ...statement.declarationList, 39 | declarations: statement.declarationList.declarations.map( 40 | (declaration) => ({ ...declaration, typename: undefined }), 41 | ), 42 | }, 43 | }, 44 | ]; 45 | } 46 | } 47 | } 48 | 49 | /** Convert TS to ES5 JS: remove type annotations and declarations */ 50 | function es2015(statements: Statement[]) { 51 | return statements.flatMap(transformStatement); 52 | 53 | function transformLetIntoVar(statement: VariableStatement) { 54 | return [ 55 | { 56 | ...statement, 57 | declarationList: { 58 | ...statement.declarationList, 59 | declarations: statement.declarationList.declarations.map( 60 | (declaration) => ({ 61 | ...declaration, 62 | name: { ...declaration.name, text: 'var' }, 63 | }), 64 | ), 65 | }, 66 | }, 67 | ]; 68 | } 69 | 70 | function transformStatement(statement: Statement): Statement[] { 71 | switch (statement.kind) { 72 | case Node.VariableStatement: 73 | return statement.declarationList.flags & SymbolFlags.BlockScopedVariable 74 | ? transformLetIntoVar(statement) 75 | : [statement]; 76 | default: 77 | return [statement]; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export enum Token { 2 | Function = 'Function', 3 | Var = 'Var', 4 | Let = 'Let', 5 | Type = 'Type', 6 | Return = 'Return', 7 | Equals = 'Equals', 8 | NumericLiteral = 'NumericLiteral', 9 | Identifier = 'Identifier', 10 | Newline = 'Newline', 11 | Semicolon = 'Semicolon', 12 | Colon = 'Colon', 13 | Comma = 'Comma', 14 | Whitespace = 'Whitespace', 15 | String = 'String', 16 | Unknown = 'Unknown', 17 | BOF = 'BOF', 18 | EOF = 'EOF', 19 | } 20 | 21 | export type Lexer = { 22 | scan(): void; 23 | token(): Token; 24 | pos(): number; 25 | text(): string; 26 | isSingleQuote(): boolean; 27 | }; 28 | 29 | export enum Node { 30 | Identifier, 31 | NumericLiteral, 32 | Assignment, 33 | ExpressionStatement, 34 | Var, 35 | Let, 36 | TypeAlias, 37 | StringLiteral, 38 | EmptyStatement, 39 | VariableStatement, 40 | VariableDeclarationList, 41 | VariableDeclaration, 42 | } 43 | 44 | export type Error = { 45 | pos: number; 46 | message: string; 47 | }; 48 | 49 | export interface Location { 50 | pos: number; 51 | } 52 | 53 | export type Expression = 54 | | Identifier 55 | | NumericLiteral 56 | | Assignment 57 | | StringLiteral; 58 | 59 | export type Identifier = Location & { 60 | kind: Node.Identifier; 61 | text: string; 62 | }; 63 | 64 | export type NumericLiteral = Location & { 65 | kind: Node.NumericLiteral; 66 | value: number; 67 | }; 68 | 69 | export type StringLiteral = Location & { 70 | kind: Node.StringLiteral; 71 | value: string; 72 | isSingleQuote: boolean; 73 | }; 74 | 75 | export type Assignment = Location & { 76 | kind: Node.Assignment; 77 | name: Identifier; 78 | value: Expression; 79 | }; 80 | 81 | export type Statement = 82 | | ExpressionStatement 83 | | TypeAlias 84 | | EmptyStatement 85 | | VariableStatement; 86 | 87 | export type ExpressionStatement = Location & { 88 | kind: Node.ExpressionStatement; 89 | expr: Expression; 90 | }; 91 | 92 | export type VariableStatement = Location & { 93 | kind: Node.VariableStatement; 94 | declarationList: VariableDeclarationList; 95 | }; 96 | 97 | export type VariableDeclarationList = Location & { 98 | kind: Node.VariableDeclarationList; 99 | declarations: VariableDeclaration[]; 100 | flags: NodeFlags; 101 | }; 102 | 103 | export type VariableDeclaration = Location & { 104 | kind: Node.VariableDeclaration; 105 | name: Identifier; 106 | typename?: Identifier | undefined; 107 | init: Expression; 108 | }; 109 | 110 | export type TypeAlias = Location & { 111 | kind: Node.TypeAlias; 112 | name: Identifier; 113 | typename: Identifier; 114 | }; 115 | 116 | export type EmptyStatement = { 117 | kind: Node.EmptyStatement; 118 | }; 119 | 120 | export type Declaration = VariableDeclaration | TypeAlias; 121 | 122 | export type Symbol = { 123 | valueDeclaration: VariableDeclaration | undefined; 124 | declarations: Declaration[]; 125 | flags: SymbolFlags; 126 | }; 127 | 128 | export type Table = Map; 129 | 130 | export type Module = { 131 | locals: Table; 132 | statements: Statement[]; 133 | }; 134 | 135 | export type Type = { id: string }; 136 | 137 | export enum CharCodes { 138 | b = 98, 139 | t = 116, 140 | n = 110, 141 | r = 114, 142 | singleQuote = 39, 143 | doubleQuote = 34, 144 | backslash = 92, 145 | } 146 | 147 | type CompilerTarget = 'es5' | 'es2015' | 'es2017' | 'es2022'; 148 | 149 | export interface CompilerOptions { 150 | target: CompilerTarget; 151 | } 152 | 153 | export const enum SymbolFlags { 154 | None = 0, 155 | FunctionScopedVariable = 1 << 0, // Variable (var) or parameter 156 | BlockScopedVariable = 1 << 1, // A block-scoped variable (let or const) 157 | Type = 1 << 2, 158 | } 159 | 160 | export const enum NodeFlags { 161 | None = 0, 162 | Let = 1 << 0, 163 | } 164 | -------------------------------------------------------------------------------- /tests/emptyStatement.ts: -------------------------------------------------------------------------------- 1 | var x: number = 1;;var y: number = 2; 2 | -------------------------------------------------------------------------------- /tests/multipleDeclarationsWithDifferentTypes.ts: -------------------------------------------------------------------------------- 1 | var s = 'test'; 2 | 3 | var s1: string = 'test'; 4 | 5 | var s2 = 'test'; 6 | var s2: string = 'test'; 7 | 8 | var s3: number = 2; 9 | var s3 = 'test'; 10 | 11 | var s4: string = 'test'; 12 | var s4: number = 2; 13 | var s4 = 'test'; 14 | 15 | var s5 = 'test'; 16 | var s5: string = 2; -------------------------------------------------------------------------------- /tests/redeclare.ts: -------------------------------------------------------------------------------- 1 | var x = 1; 2 | var x = 2; 3 | -------------------------------------------------------------------------------- /tests/redeclareLetVar.ts: -------------------------------------------------------------------------------- 1 | var x = 1; 2 | let x = 2; 3 | let y = 1; 4 | var y = 2; -------------------------------------------------------------------------------- /tests/singleIdentifier.ts: -------------------------------------------------------------------------------- 1 | x; 2 | -------------------------------------------------------------------------------- /tests/singleLet.ts: -------------------------------------------------------------------------------- 1 | let variable = 1; 2 | variable; -------------------------------------------------------------------------------- /tests/singleTypedVar.ts: -------------------------------------------------------------------------------- 1 | var s: string = 1; 2 | var n: number = 'test'; 3 | -------------------------------------------------------------------------------- /tests/singleVar.ts: -------------------------------------------------------------------------------- 1 | var singleDeclaration = 1; 2 | -------------------------------------------------------------------------------- /tests/stringLiteral.ts: -------------------------------------------------------------------------------- 1 | var singleQuote = 'singleQuote'; 2 | var doubleQuote = "doubleQuote"; 3 | var escapedSingleQuote = 'escapedSingle\'Quote'; 4 | var escapedDoubleQuote = "escapedDouble\"Quote"; 5 | var escapedB = 'escaped\bB'; 6 | var escapedT = 'escaped\tT'; 7 | var escapedN = 'escaped\nN'; 8 | var escapedR = 'escaped\rR'; 9 | -------------------------------------------------------------------------------- /tests/terminator.ts: -------------------------------------------------------------------------------- 1 | var x = 1; 2 | var y = 2; 3 | var z = 3; 4 | x;y;z; 5 | -------------------------------------------------------------------------------- /tests/twoStatements.ts: -------------------------------------------------------------------------------- 1 | var arthurTwoShedsJackson = 1; 2 | arthurTwoShedsJackson = 2; 3 | -------------------------------------------------------------------------------- /tests/twoTypedStatements.ts: -------------------------------------------------------------------------------- 1 | var s: string = 1; 2 | s = 2; 3 | -------------------------------------------------------------------------------- /tests/typeAlias.ts: -------------------------------------------------------------------------------- 1 | type Nat = number; 2 | var Nat = 12; 3 | var nat: Nat = 13; 4 | 5 | type Not = string; 6 | type Not = number; 7 | type Nit = Not; 8 | var nit: Not = 14; 9 | 10 | var Nut = 15; 11 | var nut: Nut = 16; 12 | type Net = number; 13 | var net = Net; 14 | -------------------------------------------------------------------------------- /tests/typedVariableDeclarationList.ts: -------------------------------------------------------------------------------- 1 | var n: number = 1, s: string = 'test', n2: number = 3; 2 | -------------------------------------------------------------------------------- /tests/unterminatedString.ts: -------------------------------------------------------------------------------- 1 | var unterminatedString: string = "unterminatedString; 2 | -------------------------------------------------------------------------------- /tests/usedBeforeDeclaration.ts: -------------------------------------------------------------------------------- 1 | variable; 2 | let variable = 1; -------------------------------------------------------------------------------- /tests/varWithMultipleDeclarations.ts: -------------------------------------------------------------------------------- 1 | var var1: string = 'test'; 2 | var var1: string = 'what, another one?' 3 | var var1 = 'no type annotation needed' -------------------------------------------------------------------------------- /tests/variableDeclarationList.ts: -------------------------------------------------------------------------------- 1 | var var1 = 1, var2 = 2, var3 = 3; 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "strict": true, 5 | "target": "esnext", 6 | "lib": ["es2020", "dom"], 7 | "outDir": "built" 8 | }, 9 | "include": ["src"] 10 | } 11 | --------------------------------------------------------------------------------