├── .github └── workflows │ └── build.yaml ├── .gitignore ├── README.md ├── Yabal.sln ├── examples ├── console.yabal ├── custom │ ├── config.jsonc │ └── console.yabal ├── font │ ├── LICENSE.txt │ └── RobotoMono-Regular.ttf ├── image.yabal ├── image_apple.webp ├── inline.yabal ├── inline_asm.yabal ├── memory_bank.yabal ├── mouse.yabal ├── platformer │ ├── console.yabal │ ├── input.yabal │ ├── platformer.yabal │ └── screen.yabal ├── struct.yabal ├── variable.yabal └── write.yabal ├── nuget.config ├── src ├── Yabal.Bot │ ├── Handler │ │ └── ImageHandler.cs │ ├── Program.cs │ ├── Responders │ │ └── MessageCreateResponder.cs │ └── Yabal.Bot.csproj ├── Yabal.Compiler │ ├── ErrorMessages.cs │ ├── Extensions │ │ └── StringExtensions.cs │ ├── FileReader.cs │ ├── InstructionBuildResult.cs │ ├── Instructions │ │ ├── InstructionBuilder.cs │ │ ├── InstructionLabel.cs │ │ └── InstructionPointer.cs │ ├── Usings.cs │ ├── Yabal.Compiler.csproj │ ├── Yabal.Compiler.csproj.DotSettings │ └── Yabal │ │ ├── Address │ │ ├── FileAddress.cs │ │ ├── IAddress.cs │ │ ├── RawAddress.cs │ │ └── StringAddress.cs │ │ ├── Ast │ │ ├── Abstractions │ │ │ ├── IAddressExpression.cs │ │ │ ├── IAssignExpression.cs │ │ │ ├── IConstantValue.cs │ │ │ ├── IExpression.cs │ │ │ ├── IExpressionToB.cs │ │ │ └── INode.cs │ │ ├── BinaryOperator.cs │ │ ├── Expression.cs │ │ ├── Expression │ │ │ ├── ArrayAccessExpression.cs │ │ │ ├── ArrowFunctionExpression.cs │ │ │ ├── AsmExpression.cs │ │ │ ├── AssignExpression.cs │ │ │ ├── BinaryExpression.cs │ │ │ ├── CallExpression.cs │ │ │ ├── Constant │ │ │ │ ├── BooleanExpression.cs │ │ │ │ ├── CastExpression.cs │ │ │ │ ├── CharExpression.cs │ │ │ │ ├── IntegerExpressionBase.cs │ │ │ │ ├── SizeOfExpression.cs │ │ │ │ └── StringExpression.cs │ │ │ ├── ConstantMemoryExpression.cs │ │ │ ├── CreatePointerExpression.cs │ │ │ ├── IdentifierExpression.cs │ │ │ ├── IncludeFileExpression.cs │ │ │ ├── InitStructExpression.cs │ │ │ ├── MemberExpression.cs │ │ │ ├── ReferenceExpression.cs │ │ │ ├── StackAllocationExpression.cs │ │ │ ├── SwitchExpression.cs │ │ │ ├── TernaryExpression.cs │ │ │ ├── UnaryExpression.cs │ │ │ └── UpdateExpression.cs │ │ ├── LanguageType.cs │ │ ├── Node.cs │ │ ├── Statement.cs │ │ ├── Statement │ │ │ ├── BlockStatement.cs │ │ │ ├── BreakStatement.cs │ │ │ ├── ContinueStatement.cs │ │ │ ├── EmptyStatement.cs │ │ │ ├── ExpressionStatement.cs │ │ │ ├── ForStatement.cs │ │ │ ├── FunctionDeclarationStatement.cs │ │ │ ├── GlobalNamespaceStatement.cs │ │ │ ├── GotoStatement.cs │ │ │ ├── IfStatement.cs │ │ │ ├── ImportStatement.cs │ │ │ ├── LabelStatement.cs │ │ │ ├── NamespaceStatement.cs │ │ │ ├── ProgramStatement.cs │ │ │ ├── ReturnStatement.cs │ │ │ ├── StructDeclarationStatement.cs │ │ │ ├── UsingStatement.cs │ │ │ ├── VariableDeclarationStatement.cs │ │ │ └── WhileStatement.cs │ │ ├── StaticType.cs │ │ └── UnaryOperator.cs │ │ ├── CompileError.cs │ │ ├── ErrorLevel.cs │ │ ├── Exceptions │ │ └── InvalidCodeException.cs │ │ ├── FileContent.cs │ │ ├── Loaders │ │ └── ByteLoader.cs │ │ ├── Pointer │ │ ├── AbsolutePointer.cs │ │ ├── Pointer.cs │ │ ├── PointerExtensions.cs │ │ └── PointerWithOffset.cs │ │ ├── PointerCollection.cs │ │ ├── SourceRange.cs │ │ ├── TemporaryVariable.cs │ │ ├── Visitor │ │ ├── AsmVisitor.cs │ │ ├── BlockCompileStack.cs │ │ ├── BlockStack.cs │ │ ├── TypeVisitor.cs │ │ ├── Variable.cs │ │ └── YabalVisitor.cs │ │ ├── YabalBuilder.cs │ │ ├── YabalLexer.g4 │ │ ├── YabalParser.cs │ │ └── YabalParser.g4 ├── Yabal.Core │ ├── Character.cs │ ├── DebugOffset.cs │ ├── Either.cs │ ├── Extensions │ │ └── SpanExtensions.cs │ ├── HexFile.cs │ ├── IProgram.cs │ ├── Instructions │ │ ├── Instruction.cs │ │ ├── InstructionReference.cs │ │ └── MicroInstruction.cs │ └── Yabal.Core.csproj ├── Yabal.Desktop │ ├── Config │ │ ├── AddressConverter.cs │ │ ├── ConfigContext.cs │ │ └── IntJsonConverter.cs │ ├── DesktopHandler.cs │ ├── Devices │ │ └── Keyboard.cs │ ├── Libraries │ │ └── SDL2.cs │ ├── Program.cs │ ├── Utils │ │ ├── AliasAttribute.cs │ │ ├── EnumHelper.cs │ │ └── OutputFormat.cs │ ├── Yabal.Desktop.csproj │ ├── Yabal.Desktop.csproj.DotSettings │ ├── config.jsonc │ ├── icon.ico │ └── native │ │ └── win-x64 │ │ └── SDL2.dll ├── Yabal.Emulator │ ├── Config │ │ ├── Config.cs │ │ ├── CpuConfig.cs │ │ ├── MemoryConfig.cs │ │ ├── MemoryDeviceConfig.cs │ │ └── ScreenConfig.cs │ ├── CpuBuilder.cs │ ├── Devices │ │ ├── Address.cs │ │ ├── Cpu.cs │ │ ├── CpuContext.cs │ │ ├── CpuMemory.cs │ │ ├── Memory │ │ │ ├── CharacterDevice.cs │ │ │ ├── DebugDevice.cs │ │ │ ├── MemoryDevice.cs │ │ │ └── Screen.cs │ │ └── ScreenColor.cs │ ├── Handler.cs │ ├── Yabal.Emulator.csproj │ └── Yabal.Emulator.csproj.DotSettings ├── Yabal.LanguageServer │ ├── Document.cs │ ├── Extensions │ │ ├── LanguageServerOptionsCommandLineExtensions.cs │ │ └── RangeExtensions.cs │ ├── Handlers │ │ ├── DefinitionHandler.cs │ │ ├── HighlightHandler.cs │ │ ├── HoverHandler.cs │ │ ├── RenameHandler.cs │ │ └── TextDocumentSyncHandler.cs │ ├── Logging │ │ └── LanguageServerSink.cs │ ├── Program.cs │ ├── TextDocumentContainer.cs │ ├── Yabal.LanguageServer.csproj │ └── Yabal.LanguageServer.csproj.DotSettings ├── Yabal.Loaders.Font │ ├── FontLoader.cs │ └── Yabal.Loaders.Font.csproj ├── Yabal.Loaders.Image │ ├── ImageLoader.cs │ └── Yabal.Loaders.Image.csproj ├── Yabal.SourceGenerator │ ├── EmbeddedResources.cs │ ├── InstructionGenerator.cs │ ├── Yabal.SourceGenerator.csproj │ └── template.scriban └── Yabal.Wasm │ ├── Interop.cs │ ├── WasmHandler.cs │ ├── Yabal.Wasm.csproj │ └── build.cmd ├── tests ├── Yabal.SourceGenerator.Tests │ ├── GenerateTest.GenerateInstructions#CpuInstructions.verified.cs │ ├── GenerateTest.cs │ ├── Usings.cs │ └── Yabal.SourceGenerator.Tests.csproj └── Yabal.Tests │ ├── AssemblerTest.cs │ ├── InstructionBuilderTest.cs │ ├── InstructionDefinitionTest.Parse.verified.txt │ ├── InstructionDefinitionTest.cs │ ├── SpanTest.cs │ ├── Usings.cs │ └── Yabal.Tests.csproj └── vscode ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── .vscodeignore ├── LICENSE ├── images └── icon.jpeg ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── acquireDotNet.ts └── extension.ts ├── syntaxes └── yabal.tmLanguage.json ├── tsconfig.json └── yarn.lock /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: dotnet publish 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | if: startsWith(github.ref, 'refs/tags/') 8 | runs-on: ${{ matrix.config.os }} 9 | 10 | strategy: 11 | matrix: 12 | config: 13 | - { os: ubuntu-latest, arch: x64, name: linux-x64 } 14 | - { os: ubuntu-latest, arch: arm64, name: linux-arm64 } 15 | - { os: windows-latest, arch: x64, name: windows-x64 } 16 | - { os: windows-latest, arch: arm64, name: windows-arm64 } 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Setup dotnet 22 | uses: actions/setup-dotnet@v3 23 | with: 24 | dotnet-version: 8.0.x 25 | 26 | - name: Install dependencies 27 | run: dotnet restore 28 | 29 | - name: Publish native 30 | run: dotnet publish src/Yabal.Desktop/Yabal.Desktop.csproj -c Release -p:DesktopAot=true -o .output-native 31 | 32 | - name: Upload native artifact 33 | uses: actions/upload-artifact@v3 34 | with: 35 | name: yabal-native-${{ matrix.config.name }} 36 | path: .output-native 37 | 38 | - name: Publish 39 | run: dotnet publish src/Yabal.Desktop/Yabal.Desktop.csproj -c Release -o .output-runtime 40 | 41 | - name: Upload artifact 42 | uses: actions/upload-artifact@v3 43 | with: 44 | name: yabal-runtime-${{ matrix.config.name }} 45 | path: .output-runtime 46 | 47 | release: 48 | needs: build 49 | runs-on: ubuntu-latest 50 | steps: 51 | - name: Download artifacts 52 | uses: actions/download-artifact@v3 53 | with: 54 | path: files 55 | 56 | - name: Create zip files 57 | run: | 58 | mkdir -p release 59 | for f in files/*; do 60 | pushd "${f}" 61 | zip -r "../../release/$(basename "${f}").zip" * 62 | popd 63 | done 64 | 65 | - name: Release 66 | uses: softprops/action-gh-release@v1 67 | with: 68 | files: release/*.zip 69 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Astro-8 2 | This repository houses two Astro-8 projects: 3 | 1. A port of [Astro-8 Emulator by sam-astro](https://github.com/sam-astro/Astro8-Computer/tree/main/Astro8-Emulator) in C#. 4 | 2. Custom language "Yabal" that compiles into valid Astro-8 assembly/binary. 5 | 6 | ## Yabal 7 | Yabal is a custom language that compiles into valid Astro-8 assembly. 8 | 9 | For examples of Yabal, see the [examples](examples) folder. 10 | 11 | ### Features 12 | - ✅ Variables 13 | - ✅ Functions (with parameters and call stack) 14 | - ✅ If statements 15 | - ✅ Comparison operators (`==`, `!=`, `>`, `<`, `>=`, `<=`) 16 | - ✅ Arithmetic operators (`+`, `-`, `*`, `/`, `%`) 17 | - ✅ Bitwise operators (`&`, `|`, `^`, `~`, `<<`, `>>`) 18 | - ✅ Logical operators (`&&`, `||`, `!`) 19 | - ✅ Comments 20 | - ✅ Create pointers (`create_pointer`) 21 | - ✅ Inline assembly 22 | - ✅ For and while loops 23 | - ✅ Structs 24 | - ✅ Include files 25 | - 🚧 Chars and strings 26 | - ❌ Arrays 27 | - ❌ Classes 28 | 29 | ### Usage 30 | Download the latest release from the [releases page](https://github.com/GerardSmit/Astro8/releases). 31 | 32 | When running Linux, install `libsdl2-2.0-0`: 33 | 34 | ```bash 35 | # Linux 36 | sudo apt-get install libsdl2-2.0-0 37 | ``` 38 | 39 | To view all the commands with a description run `astro --help`. 40 | 41 | #### Run 42 | To run a file with the C# emulator run the following command: 43 | 44 | ```bash 45 | # Windows 46 | astro run source.yabal 47 | 48 | # Linux 49 | ./astro run source.yabal 50 | ``` 51 | 52 | #### Compile 53 | To compile a file to assembly run the following command: 54 | 55 | ```bash 56 | # Windows 57 | astro build source.yabal 58 | 59 | # Linux 60 | ./astro build source.yabal 61 | ``` 62 | 63 | It is possible to change the output file with `-f ` to the following formats: 64 | 65 | | Flag | Name | Extension | 66 | | --- | --- | --- | 67 | | `a` | Assembly _(default)_ | `asm` | 68 | | `c` | Assembly with comments | `asmc` | 69 | | `h` | Astro-8 Emulator HEX file | `aexe` | 70 | | `l` | Logisim Evolution HEX file | `hex` | 71 | 72 | For example: 73 | 74 | ```bash 75 | astro build -f h source.yabal # Compile aexe 76 | astro build -f ha source.yabal # Compile aexe and asm 77 | 78 | astro build -f aexe source.yabal # Compile aexe 79 | astro build -f aexe,asm source.yabal # Compile aexe and asm 80 | ``` 81 | -------------------------------------------------------------------------------- /examples/console.yabal: -------------------------------------------------------------------------------- 1 | var font = include_font("./font/RobotoMono-Regular.ttf;Size=10"); 2 | var screen = create_pointer(53871, 1); 3 | var screenWidth = 108; 4 | var screenHeight = 108; 5 | var currentX = 0; 6 | var currentY = 0; 7 | 8 | void render_char(int screenX, int screenY, int character) { 9 | var width = 8; 10 | var height = 8; 11 | var offset = character * width * height; 12 | 13 | for (var y = 0; y < height; y++) { 14 | for (var x = 0; x < width; x++) { 15 | var pixel = font[offset + y * width + x]; 16 | 17 | if (pixel > 0) { 18 | screen[(screenY + y) * screenWidth + (screenX + x)] = pixel; 19 | } 20 | } 21 | } 22 | } 23 | 24 | void write_char(int character) { 25 | render_char(currentX, currentY, character); 26 | currentX += 8; 27 | 28 | if (currentX >= 104) { 29 | currentX = 0; 30 | currentY += 8; 31 | } 32 | } 33 | 34 | void write_string(int[] str) { 35 | for (var i = 0; true; i++) { 36 | var c = str[i]; 37 | 38 | if (c == 0xFFFF) { 39 | break; 40 | } 41 | 42 | write_char(c); 43 | } 44 | } 45 | 46 | void reset_screen() { 47 | for (var i = 0; i < 108 * 108; i++) { 48 | screen[i] = 0; 49 | } 50 | } 51 | 52 | while (true) { 53 | currentX = 0; 54 | currentY = 0; 55 | write_string("abcdefghijklmnopqrstuvwxyz0123456789"); 56 | asm { VBUF } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /examples/custom/config.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "Cpu": { 3 | // Duration of one cycle in ticks. 4 | // Lower values will result in faster execution. 5 | "CycleDuration": 300, 6 | 7 | // Amount of instructions that should be executed per cycle. 8 | // Higher values will result in faster execution, since the CPU doesn't have to fetch the registers from the heap every time. 9 | "InstructionsPerCycle": 100 10 | }, 11 | 12 | "Screen": { 13 | // Note: changing the resolution will also resize the memory allocation of the screen and character device. 14 | "Width": 255, 15 | "Height": 255, 16 | "Scale": 2 17 | }, 18 | 19 | "Memory": { 20 | // Max amount of memory in bytes. 21 | "Size": "0xFFFF", 22 | "Banks": 3, 23 | 24 | // Mapping of device in memory. 25 | "Devices": { 26 | "Program": [0, "0x0000"], 27 | "Character": [1, "0xD12A"], 28 | "Keyboard": [1, "0xD0FC"], 29 | "Mouse": [1, "0xD0FD"], 30 | "Screen": [2, "0x0000"] 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/custom/console.yabal: -------------------------------------------------------------------------------- 1 | var font = include_font("Ubuntu Mono"); 2 | var screen = create_pointer(0, 2); 3 | var screenWidth = 255; 4 | var screenOffset = 0; 5 | 6 | void render_char(int screenX, int screenY, int character) { 7 | var width = 8; 8 | var height = 8; 9 | var offset = character * width * height; 10 | 11 | for (var y = 0; y < height; y++) { 12 | for (var x = 0; x < width; x++) { 13 | var pixel = font[offset + y * width + x]; 14 | 15 | if (pixel > 0) { 16 | screen[(screenY + y) * screenWidth + (screenX + x)] = pixel; 17 | } 18 | } 19 | } 20 | } 21 | 22 | while (true) { 23 | screenOffset = 0; 24 | 25 | render_char(0, 0, 'H') 26 | render_char(7, 0, 'E') 27 | render_char(14, 0, 'L') 28 | render_char(21, 0, 'L') 29 | render_char(28, 0, 'O') 30 | } 31 | 32 | -------------------------------------------------------------------------------- /examples/font/RobotoMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YabalLang/compiler/659a278a29007ec65f00449a63f5fa09e95c663d/examples/font/RobotoMono-Regular.ttf -------------------------------------------------------------------------------- /examples/image.yabal: -------------------------------------------------------------------------------- 1 | /* 2 | In Yabal it's possible to include images files with "include_image". 3 | 4 | Every pixel in the image is converted to a single uint16: 5 | 6 | | Alpha (1) | Red (5) | Green (5) | Blue (5) | 7 | | 0 | 00000 | 00000 | 00000 | 8 | 9 | To convert the pixel value to seperate variables in Yabal, you can do the following: 10 | 11 | var file = include_image("file.png") 12 | var pixel = file[0] 13 | var a = pixel >> 15 14 | var r = pixel >> 10 & 0b11111 15 | var g = pixel >> 5 & 0b11111 16 | var b = pixel & 0b11111 17 | 18 | The structure is almost the same as the video pixel color data. The only difference is that the graphics ignores the alpha channel. 19 | So in code, you have to check the alpha channel for youself before copying it to the graphics buffer. 20 | 21 | In front of the pointer, there is a uint16 that contains the width and height of the image. 22 | 23 | | Width (8) | Height (8) | 24 | | 00000000 | 00000000 | 25 | 26 | To get the width and height in Yabal, you can do the following: 27 | 28 | var file = include_image("file.png") 29 | var meta = file[-1] 30 | var width = meta >> 8 & 0xFF 31 | var height = meta & 0xFF 32 | 33 | In the following example, the image "image_apple.webp" is loaded and displayed. 34 | */ 35 | 36 | var file = include_image("./image_apple.webp") 37 | var screen = create_pointer(53871, 1) 38 | 39 | var meta = file[-1] 40 | var width = meta >> 8 & 0xFF 41 | var height = meta & 0xFF 42 | 43 | while(true) { 44 | for (var x = 0; x < width; x++) { 45 | for (var y = 0; y < height; y++) { 46 | var screenOffset = (y * 108) + x 47 | var pixelOffset = (y * width) + x 48 | 49 | var pixel = file[pixelOffset] 50 | var a = pixel >> 15 51 | 52 | if (a == 1) { 53 | screen[screenOffset] = pixel 54 | } 55 | } 56 | } 57 | 58 | asm { VBUF } 59 | } -------------------------------------------------------------------------------- /examples/image_apple.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YabalLang/compiler/659a278a29007ec65f00449a63f5fa09e95c663d/examples/image_apple.webp -------------------------------------------------------------------------------- /examples/inline.yabal: -------------------------------------------------------------------------------- 1 | /* 2 | The following example shows an inline function 3 | Every time the function is called, the compiler will copy the code of the function and paste it in the place where the function is called 4 | */ 5 | 6 | inline int get_color(int r, int g, int b) { 7 | return (r / 8 << 10) + (g / 8 << 5) + (b / 8) 8 | } 9 | 10 | var red = get_color(255, 0, 0) 11 | 12 | int[] screen = create_pointer(53871, 1) 13 | 14 | while(true) { 15 | screen[0] = get_color(255, 0, 0) 16 | 17 | /* 18 | The compiler will replace the function call with the following code: 19 | 20 | screen[0] = (255 / 8 << 10) + (0 / 8 << 5) + (0 / 8) 21 | 22 | After that the compiler will try to optimize the code. 23 | Because all the values are constants, the compiler will replace the code with: 24 | 25 | screen[0] = 31744 26 | */ 27 | } -------------------------------------------------------------------------------- /examples/inline_asm.yabal: -------------------------------------------------------------------------------- 1 | var chars = create_pointer(0xD12A, 1) 2 | var value = 0; 3 | 4 | while (true) { 5 | asm { 6 | LDW 53500 7 | BNK 1 8 | LDAIN 9 | BNK 0 10 | STA @value 11 | } 12 | 13 | chars[0] = value 14 | } -------------------------------------------------------------------------------- /examples/memory_bank.yabal: -------------------------------------------------------------------------------- 1 | /* 2 | The following example shows how to write to different memory banks. 3 | 4 | Astro-8 has two memory banks: 5 | 0 - Program 6 | 1 - Graphics 7 | 8 | In Yabal you can create a pointer that points to an address: 9 | 10 | var pointer = create_pointer(16382) 11 | 12 | By default, the memory bank is 0. So this pointer points to address 16382 in the program memory. 13 | If you want to point to the graphics memory, you can specify the memory bank as the second argument: 14 | 15 | var pointer = create_pointer(53546, 1) 16 | 17 | Now the pointer points to address 53546 in the graphics memory. 18 | */ 19 | 20 | var chars = create_pointer(0xD12A, 1) 21 | var message = "Hello world" 22 | 23 | while(true) { 24 | for (int i = 0; i < sizeof(message); i++) { 25 | chars[i] = message[i] 26 | } 27 | } -------------------------------------------------------------------------------- /examples/mouse.yabal: -------------------------------------------------------------------------------- 1 | var screen = create_pointer(53871, 1); 2 | var screen_width = 108; 3 | 4 | var mouse = create_pointer(53501, 1); 5 | 6 | inline int mask(int n) => (1 << n) - 1 7 | inline int get_color(int r, int g, int b) => (r / 8 << 10) + (g / 8 << 5) + (b / 8) 8 | 9 | while (true) { 10 | MouseInput input = mouse[0]; 11 | var offset = input.y * screen_width + input.x; 12 | 13 | if (input.left == 1) { 14 | screen[offset] = get_color(255, 255, 255); 15 | } else if (input.right == 1) { 16 | screen[offset] = get_color(0, 0, 0); 17 | } 18 | } 19 | 20 | struct MouseInput { 21 | int y : 7; 22 | int x : 7; 23 | int left : 1; 24 | int right : 1; 25 | }; -------------------------------------------------------------------------------- /examples/platformer/console.yabal: -------------------------------------------------------------------------------- 1 | var chars = create_pointer(53546,1) 2 | var offset = 0 3 | var maxOffset = 18 * 18 4 | 5 | namespace console; 6 | 7 | void write(char character) { 8 | chars[offset] = character 9 | offset++ 10 | 11 | if (offset >= 170) { 12 | offset = 0 13 | } 14 | } 15 | 16 | void write(char[] str) { 17 | for (var i = 0; true; i++) { 18 | var c = str[i]; 19 | 20 | if (c == 0xFFFF) { 21 | break; 22 | } 23 | 24 | write(c); 25 | } 26 | } 27 | 28 | void write(int value) { 29 | if (value < 10) { 30 | write((char)(value + 39)) 31 | return 32 | } 33 | 34 | var temp = stackalloc char[5] 35 | var i = 0 36 | 37 | while (value > 0) { 38 | temp[i] = (value % 10) + 39 39 | value /= 10 40 | i += 1 41 | } 42 | 43 | while (i > 0) { 44 | i -= 1 45 | write(temp[i]) 46 | } 47 | } 48 | 49 | void setPosition(int x, int y) { 50 | offset = x + y * 18 51 | } 52 | 53 | void clear() { 54 | for (var i = 0; i < maxOffset; i++) { 55 | chars[i] = 0 56 | } 57 | 58 | offset = 0 59 | } -------------------------------------------------------------------------------- /examples/platformer/input.yabal: -------------------------------------------------------------------------------- 1 | namespace input 2 | 3 | inline char getKey() => asm { 4 | LDW 53500 5 | BNK 1 6 | LDAIN 7 | BNK 0 8 | }; 9 | 10 | inline Mouse getMouse() => asm { 11 | LDW 53501 12 | BNK 1 13 | LDAIN 14 | BNK 0 15 | }; 16 | 17 | struct Mouse { 18 | int y : 7 19 | int x : 7 20 | int left : 1 21 | int right : 1 22 | } -------------------------------------------------------------------------------- /examples/platformer/platformer.yabal: -------------------------------------------------------------------------------- 1 | import "./console.yabal" 2 | import "./input.yabal" 3 | import "./screen.yabal" 4 | 5 | Player player = { 6 | position: { x: 64, y: 64 }, 7 | color: { r: 31, g: 0, b: 0 } 8 | } 9 | 10 | while (true) { 11 | var key = input.getKey() 12 | var mouse = input.getMouse() 13 | 14 | if (key == 'W') { 15 | player.position.y -= 1 16 | } else if (key == 'S') { 17 | player.position.y += 1 18 | } else if (key == 'A') { 19 | player.position.x -= 1 20 | } else if (key == 'D') { 21 | player.position.x += 1 22 | } 23 | 24 | Size size = { width: 3, height: 3 } 25 | screen.draw(player.position, size, player.color) 26 | 27 | console.clear() 28 | console.setPosition(0, 0) 29 | console.write("K: ") 30 | console.write((int)key) 31 | console.write(' ') 32 | console.write(key) 33 | 34 | console.setPosition(0, 1) 35 | console.write("X: ") 36 | console.write(player.position.x) 37 | 38 | console.setPosition(0, 2) 39 | console.write("Y: ") 40 | console.write(player.position.y) 41 | 42 | screen.flush() 43 | } 44 | 45 | struct Player { 46 | Position position 47 | Color color 48 | } 49 | -------------------------------------------------------------------------------- /examples/platformer/screen.yabal: -------------------------------------------------------------------------------- 1 | namespace screen; 2 | 3 | var screen = create_pointer(53870, 1) 4 | 5 | inline void clear() { 6 | for (var i = 0; i < 108 * 108; i++) { 7 | screen[i] = 0 8 | } 9 | } 10 | 11 | inline void flush() { 12 | asm { VBUF } 13 | clear() 14 | } 15 | 16 | inline int getColor(Color color) => color 17 | inline int getColor(int r, int g, int b) => (r / 8 << 10) + (g / 8 << 5) + (b / 8) 18 | 19 | inline int getOffset(Position pos) => pos.y * 108 + pos.x 20 | inline int getOffset(int x, int y) => y * 108 + x 21 | 22 | inline operator int(Position pos) => getOffset(pos) 23 | 24 | inline void set(Position pos, Color color) => screen[getOffset(pos)] = color 25 | inline void set(int x, int y, Color color) => screen[getOffset(x, y)] = color 26 | inline void set(Position pos, int r, int g, int b) => screen[getOffset(pos)] = getColor(r, g, b) 27 | inline void set(int x, int y, int r, int g, int b) => screen[getOffset(x, y)] = getColor(r, g, b) 28 | 29 | void draw(Position pos, Size size, Color color) { 30 | for (var y = 0; y < size.height; y++) { 31 | for (var x = 0; x < size.width; x++) { 32 | screen.set(pos.x + x, pos.y + y, color) 33 | } 34 | } 35 | } 36 | 37 | struct Color { 38 | int b : 5 39 | int g : 5 40 | int r : 5 41 | } 42 | 43 | struct Position { 44 | int x 45 | int y 46 | } 47 | 48 | struct Size { 49 | int width 50 | int height 51 | } -------------------------------------------------------------------------------- /examples/struct.yabal: -------------------------------------------------------------------------------- 1 | var chars = create_pointer(0xD12A, 1); 2 | var items = create_pointer(0x3FFE); 3 | 4 | items[0] = { x: 0, y: 0, character: '1' }; 5 | items[1] = { x: 17, y: 0, character: '2' }; 6 | items[2] = { x: 0, y: 17, character: '3' }; 7 | items[3] = { x: 17, y: 17, character: '4' }; 8 | 9 | while (true) { 10 | for (var i = 0; i < 4; i++) { 11 | var item = items[i]; 12 | 13 | chars[item.y * 18 + item.x] = item.character; 14 | } 15 | } 16 | 17 | struct Item { 18 | int x 19 | int y 20 | int character 21 | } -------------------------------------------------------------------------------- /examples/variable.yabal: -------------------------------------------------------------------------------- 1 | int a = 1 2 | 3 | asm { 4 | AIN @a // Read the value of a into register A 5 | LDIB 1 // Load the value 1 into register B 6 | ADD // Add the value of register B to register A 7 | STA @a // Store the value of register A into a 8 | } 9 | 10 | a -------------------------------------------------------------------------------- /examples/write.yabal: -------------------------------------------------------------------------------- 1 | import "write" 2 | 3 | struct string { 4 | int[] data 5 | int length 6 | } 7 | 8 | while(true) { 9 | setPosition(0, 0) 10 | write("Hello") 11 | write("world") 12 | asm { VBUF } 13 | } -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Yabal.Bot/Handler/ImageHandler.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Devices; 2 | using SixLabors.ImageSharp; 3 | using SixLabors.ImageSharp.PixelFormats; 4 | using Yabal.Devices; 5 | 6 | namespace Yabal.Bot.Handler; 7 | 8 | public class ImageHandler : global::Yabal.Handler 9 | { 10 | private ImageFrame? _frame; 11 | 12 | public ImageHandler() 13 | { 14 | Image = new Image(108, 108); 15 | _frame = Image.Frames.RootFrame; 16 | } 17 | 18 | public bool DidFlush { get; private set; } 19 | 20 | public Image Image { get; } 21 | 22 | public override void SetPixel(int address, ScreenColor color) 23 | { 24 | if (_frame == null) 25 | { 26 | _frame = Image.Frames.CreateFrame(); 27 | 28 | var meta = _frame.Metadata.GetGifMetadata(); 29 | meta.FrameDelay = 5; 30 | } 31 | 32 | _frame[address % 108, address / 108] = new Rgba32(color.R, color.G, color.B, color.A); 33 | } 34 | 35 | public override void LogSpeed(int steps, float value) 36 | { 37 | } 38 | 39 | public override void FlushScreen() 40 | { 41 | if (Image.Frames.Count > 10) 42 | { 43 | return; 44 | } 45 | 46 | DidFlush = true; 47 | _frame = null; 48 | } 49 | 50 | public override void Halt() 51 | { 52 | } 53 | 54 | public override void ShowVariable(int line, int offset, Span value) 55 | { 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Yabal.Bot/Program.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Bot.Responders; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | using Remora.Discord.API.Abstractions.Gateway.Commands; 7 | using Remora.Discord.Gateway; 8 | using Remora.Discord.Gateway.Extensions; 9 | using Remora.Discord.Hosting.Options; 10 | using Remora.Discord.Hosting.Services; 11 | using Remora.Extensions.Options.Immutable; 12 | 13 | var builder = Host.CreateApplicationBuilder(args); 14 | 15 | // Logging 16 | builder.Logging.AddFilter("Microsoft", LogLevel.Warning); 17 | builder.Logging.AddFilter("System.Net.Http.HttpClient.*.LogicalHandler", LogLevel.Warning); 18 | builder.Logging.AddFilter("System.Net.Http.HttpClient.*.ClientHandler", LogLevel.Warning); 19 | 20 | // Options 21 | builder.Services.AddOptions(); 22 | builder.Services.Configure(() => new DiscordServiceOptions(TerminateApplicationOnCriticalGatewayErrors: true)); 23 | 24 | // Infrastructure 25 | builder.Services.AddDiscordGateway(_ => builder.Configuration.GetConnectionString("Discord")!); 26 | builder.Services.Configure(g => g.Intents |= GatewayIntents.MessageContents); 27 | 28 | // Commands 29 | builder.Services.AddResponder(); 30 | 31 | // Hosted services 32 | builder.Services.AddHostedService(); 33 | 34 | var host = builder.Build(); 35 | 36 | await host.RunAsync(); 37 | -------------------------------------------------------------------------------- /src/Yabal.Bot/Yabal.Bot.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | yabal-bot 9 | partial 10 | 11 | 12 | 13 | true 14 | true 15 | true 16 | embedded 17 | 18 | 19 | 20 | win-x64 21 | 22 | 23 | 24 | linux-x64 25 | 26 | 27 | 28 | true 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | PreserveNewest 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/FileReader.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Exceptions; 2 | using Zio; 3 | 4 | namespace Yabal; 5 | 6 | public sealed class FileReader : IDisposable 7 | { 8 | private readonly HttpClient _client; 9 | private readonly IFileSystem? _fileSystem; 10 | 11 | public FileReader(IFileSystem? fileSystem) 12 | { 13 | _fileSystem = fileSystem; 14 | _client = new HttpClient(); 15 | } 16 | 17 | public async Task<(Uri Uri, string Content)> ReadAllTextAsync(SourceRange range, string path) 18 | { 19 | await using var result = await GetStreamAsync(range, path); 20 | using var reader = new StreamReader(result.Stream); 21 | return (result.Uri, await reader.ReadToEndAsync()); 22 | } 23 | 24 | public async Task<(Uri Uri, byte[] Bytes)> ReadAllBytesAsync(SourceRange range, string path) 25 | { 26 | await using var result = await GetStreamAsync(range, path); 27 | using var memoryStream = new MemoryStream(); 28 | await result.Stream.CopyToAsync(memoryStream); 29 | return (result.Uri, memoryStream.ToArray()); 30 | } 31 | 32 | public async Task GetStreamAsync(SourceRange range, string path) 33 | { 34 | var uri = GetUri(range, path); 35 | 36 | if (uri == null) 37 | { 38 | throw new InvalidOperationException(); 39 | } 40 | 41 | return new StreamResult(uri, await GetFromUri(range, uri)); 42 | } 43 | 44 | public Uri GetUri(SourceRange range, string path) 45 | { 46 | Uri? uri; 47 | 48 | if (path.StartsWith(".")) 49 | { 50 | uri = new Uri(range.File, path); 51 | } 52 | else if (path.StartsWith("/")) 53 | { 54 | uri = new Uri(range.File, "." + path); 55 | } 56 | else if (!Uri.TryCreate(path, UriKind.Absolute, out uri)) 57 | { 58 | uri = new Uri($"https://yabal.dev/x/{path}.yabal"); 59 | } 60 | 61 | return uri; 62 | } 63 | 64 | private Task GetFromUri(SourceRange range, Uri uri) 65 | { 66 | return uri.Scheme switch 67 | { 68 | "file" => GetFromFile(range, uri), 69 | "http" or "https" => GetFromHttp(range, uri), 70 | _ => throw new InvalidCodeException("Invalid import scheme '" + uri.Scheme + "'", range) 71 | }; 72 | } 73 | 74 | private Task GetFromFile(SourceRange range, Uri uri) 75 | { 76 | if (_fileSystem is null) 77 | { 78 | throw new InvalidCodeException("File imports are not supported", range); 79 | } 80 | 81 | try 82 | { 83 | return Task.FromResult(_fileSystem.OpenFile(uri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read)); 84 | } 85 | catch (Exception) 86 | { 87 | throw new InvalidCodeException("Could not find or read file '" + _fileSystem.ConvertPathToInternal(uri.LocalPath) + "'", range); 88 | } 89 | } 90 | 91 | private async Task GetFromHttp(SourceRange range, Uri uri) 92 | { 93 | try 94 | { 95 | var response = await _client.GetAsync(uri); 96 | response.EnsureSuccessStatusCode(); 97 | 98 | return await response.Content.ReadAsStreamAsync(); 99 | } 100 | catch (Exception) 101 | { 102 | throw new InvalidCodeException("Failed to import '" + uri + "'", range); 103 | } 104 | } 105 | 106 | public void Dispose() 107 | { 108 | _client.Dispose(); 109 | } 110 | } 111 | 112 | public record struct StreamResult(Uri Uri, Stream Stream) : IAsyncDisposable 113 | { 114 | public async ValueTask DisposeAsync() 115 | { 116 | await Stream.DisposeAsync(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Instructions/InstructionLabel.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Instructions; 2 | 3 | public class InstructionLabel : InstructionPointer 4 | { 5 | public InstructionLabel(string name) 6 | : base(name) 7 | { 8 | } 9 | 10 | public override string? ToString() 11 | { 12 | return Name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Instructions/InstructionPointer.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Visitor; 2 | 3 | namespace Yabal.Instructions; 4 | 5 | public class InstructionPointer : Pointer 6 | { 7 | private int? _address; 8 | 9 | public InstructionPointer(string name, int size = 1, bool isSmall = false) 10 | { 11 | Name = name; 12 | Size = size; 13 | IsSmall = isSmall; 14 | } 15 | 16 | public override string Name { get; } 17 | 18 | public override bool IsSmall { get; } 19 | 20 | public override int Bank => 0; 21 | 22 | public int Size { get; } 23 | 24 | public List AssignedVariables { get; } = new(); 25 | 26 | public string AssignedVariableNames => string.Join(", ", AssignedVariables.Select(i => i.Identifier)); 27 | 28 | public override int Address => _address ?? throw new InvalidOperationException($"No address has been set, make sure {nameof(InstructionBuilder)}.{nameof(InstructionBuilder.CopyTo)} is called before accessing the value"); 29 | 30 | public string? Comment { get; set; } 31 | 32 | public override string? ToString() 33 | { 34 | return $"[{Address}:{Bank}]"; 35 | } 36 | 37 | public override int Get(IReadOnlyDictionary mappings) 38 | { 39 | if (!mappings.TryGetValue(this, out var value)) 40 | { 41 | throw new InvalidOperationException($"No address has been set for {this}"); 42 | } 43 | 44 | return value; 45 | } 46 | 47 | public void Set(int address) 48 | { 49 | _address = address; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Usings.cs: -------------------------------------------------------------------------------- 1 | global using PointerOrData = Yabal.Either; 2 | global using ReferenceList = System.Collections.Generic.List>; 3 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal.Compiler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | Yabal 8 | True 9 | embedded 10 | True 11 | true 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | all 26 | runtime; build; native; contentfiles; analyzers 27 | 28 | 29 | Yabal 30 | 31 | 32 | Yabal 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal.Compiler.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True 6 | True 7 | True 8 | True 9 | True 10 | True -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Address/FileAddress.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Ast; 2 | using Yabal.Instructions; 3 | 4 | namespace Yabal; 5 | 6 | public class FileAddress : IAddress 7 | { 8 | private readonly string _path; 9 | private readonly FileType _type; 10 | private FileContent? _content; 11 | 12 | public FileAddress(string path, FileType type, InstructionPointer pointer) 13 | { 14 | _path = path; 15 | _type = type; 16 | Pointer = pointer; 17 | } 18 | 19 | Pointer IAddress.Pointer => Pointer; 20 | 21 | public InstructionPointer Pointer { get; } 22 | 23 | public FileContent Content 24 | { 25 | get => _content ?? throw new InvalidOperationException("File content not loaded"); 26 | set => _content = value; 27 | } 28 | 29 | public int? Length 30 | { 31 | get 32 | { 33 | var (offset, bytes) = Content; 34 | return bytes.Length - offset; 35 | } 36 | } 37 | 38 | public LanguageType Type => LanguageType.Integer; 39 | 40 | public int? GetValue(int offset) 41 | { 42 | var (contentOffset, bytes) = Content; 43 | return bytes[contentOffset + offset]; 44 | } 45 | 46 | public static IAddress From(string path, FileType type, InstructionPointer pointer) => new FileAddress(path, type, pointer); 47 | 48 | public override string ToString() 49 | { 50 | return Pointer.ToString() ?? ""; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Address/IAddress.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Ast; 2 | 3 | namespace Yabal; 4 | 5 | public interface IAddress 6 | { 7 | Pointer? Pointer { get; } 8 | 9 | int? Length { get; } 10 | 11 | LanguageType Type { get; } 12 | 13 | int? GetValue(int offset); 14 | } 15 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Address/RawAddress.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Ast; 2 | 3 | namespace Yabal; 4 | 5 | public class RawAddress : IAddress 6 | { 7 | private RawAddress(int? length, LanguageType type, Pointer pointer) 8 | { 9 | Length = length; 10 | Type = type; 11 | Pointer = pointer; 12 | } 13 | 14 | public Pointer Pointer { get; } 15 | 16 | public int? Length { get; } 17 | 18 | public LanguageType Type { get; } 19 | 20 | public int? GetValue(int offset) 21 | { 22 | return null; 23 | } 24 | 25 | public static IAddress From(LanguageType type, Pointer pointer, int? length = null) => new RawAddress(length, type, pointer); 26 | 27 | public override string ToString() 28 | { 29 | return Pointer.ToString() ?? ""; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Address/StringAddress.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Ast; 2 | 3 | namespace Yabal; 4 | 5 | public class StringAddress : IAddress 6 | { 7 | private StringAddress(string value, Pointer pointer) 8 | { 9 | Value = value; 10 | Pointer = pointer; 11 | } 12 | 13 | public string Value { get; } 14 | 15 | public Pointer? Pointer { get; } 16 | 17 | public int? Length => Value.Length; 18 | 19 | public LanguageType Type => LanguageType.Integer; 20 | 21 | public int? GetValue(int offset) 22 | { 23 | return offset < Value.Length ? Character.CharToInt[Value[offset]] : null; 24 | } 25 | 26 | public static IAddress From(string value, Pointer pointer) => new StringAddress(value, pointer); 27 | } 28 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Abstractions/IAssignExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public abstract record AssignableExpression(SourceRange Range) : Expression(Range) 4 | { 5 | public abstract void Assign(YabalBuilder builder, Expression expression, SourceRange range); 6 | 7 | public abstract void LoadToA(YabalBuilder builder, int offset); 8 | 9 | public abstract void StoreFromA(YabalBuilder builder, int offset); 10 | 11 | public abstract void ShowDebug(YabalBuilder builder, SourceRange? range = null); 12 | 13 | public virtual void MarkModified() 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Abstractions/IConstantValue.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Visitor; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public interface IConstantValue 6 | { 7 | bool HasConstantValue => true; 8 | 9 | object? Value { get; } 10 | 11 | void StoreConstantValue(Span buffer); 12 | } 13 | 14 | public interface IBankSource : IExpression 15 | { 16 | int Bank { get; } 17 | } 18 | 19 | public interface IPointerSource : IExpression 20 | { 21 | Pointer? Pointer { get; } 22 | } 23 | 24 | public interface IVariableSource : IExpression 25 | { 26 | (IVariable, int? Offset) GetVariable(YabalBuilder builder); 27 | 28 | bool CanGetVariable { get; } 29 | } 30 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Abstractions/IExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public interface IExpression : INode 4 | { 5 | LanguageType Type { get; } 6 | 7 | bool OverwritesB { get; } 8 | 9 | void BuildExpression(YabalBuilder builder, bool isVoid, LanguageType? suggestedType); 10 | } 11 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Abstractions/IExpressionToB.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public interface IExpressionToB 4 | { 5 | void BuildExpressionToB(YabalBuilder builder); 6 | 7 | bool OverwritesA { get; } 8 | } 9 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Abstractions/INode.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public interface INode 4 | { 5 | SourceRange Range { get; } 6 | 7 | void Initialize(YabalBuilder builder); 8 | } 9 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/BinaryOperator.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public enum BinaryOperator 4 | { 5 | Add, 6 | Subtract, 7 | Multiply, 8 | Divide, 9 | Equal, 10 | NotEqual, 11 | GreaterThan, 12 | GreaterThanOrEqual, 13 | LessThan, 14 | LessThanOrEqual, 15 | AndAlso, 16 | OrElse, 17 | And, 18 | Or, 19 | Xor, 20 | LeftShift, 21 | RightShift, 22 | Modulo 23 | } 24 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public interface ITypeExpression 6 | { 7 | void Initialize(YabalBuilder builder, LanguageType type); 8 | } 9 | 10 | public abstract record Expression(SourceRange Range) : Node(Range), IExpression 11 | { 12 | public void BuildExpression(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 13 | { 14 | // TODO: Add debug 15 | BuildExpressionCore(builder, isVoid, suggestedType); 16 | } 17 | 18 | public abstract void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer); 19 | 20 | protected abstract void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType); 21 | 22 | public virtual Expression Optimize(LanguageType? suggestedType) => this; 23 | 24 | public abstract bool OverwritesB { get; } 25 | 26 | public abstract LanguageType Type { get; } 27 | 28 | public abstract Expression CloneExpression(); 29 | } 30 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/ArrowFunctionExpression.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Exceptions; 2 | using Yabal.Instructions; 3 | 4 | namespace Yabal.Ast; 5 | 6 | public record ArrowFunctionExpression( 7 | SourceRange Range, 8 | List Identifiers, 9 | BlockStatement Body 10 | ) : Expression(Range), ITypeExpression, IBankSource, IExpressionToB 11 | { 12 | private bool _didBuild = false; 13 | private FunctionDeclarationStatement _declarationStatement = null!; 14 | private LanguageFunction? _typeFunctionType; 15 | 16 | public Function Function => _declarationStatement.Function; 17 | 18 | public override void Initialize(YabalBuilder builder) 19 | { 20 | } 21 | 22 | public void Initialize(YabalBuilder builder, LanguageType type) 23 | { 24 | if (type.FunctionType is null) 25 | { 26 | throw new InvalidCodeException("Cannot create an arrow function with a non-function type", Range); 27 | } 28 | 29 | _typeFunctionType = type.FunctionType; 30 | _declarationStatement = new FunctionDeclarationStatement( 31 | Range, 32 | null, 33 | type.FunctionType.ReturnType, 34 | Identifiers.Zip(type.FunctionType.Parameters, (i, p) => new FunctionParameter(i, p, false)).ToList(), 35 | Body, 36 | false 37 | ); 38 | 39 | _declarationStatement.Declare(builder); 40 | _declarationStatement.Initialize(builder); 41 | 42 | _declarationStatement.Function.MarkUsed(); 43 | } 44 | 45 | private void Build(YabalBuilder builder) 46 | { 47 | if (_didBuild) 48 | { 49 | return; 50 | } 51 | 52 | _declarationStatement.Build(builder); 53 | _didBuild = true; 54 | } 55 | 56 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 57 | { 58 | Build(builder); 59 | 60 | builder.SetA(_declarationStatement.Function.Label); 61 | builder.StoreA(pointer); 62 | } 63 | 64 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 65 | { 66 | Build(builder); 67 | 68 | builder.SetA(_declarationStatement.Function.Label); 69 | } 70 | 71 | public override bool OverwritesB => false; 72 | public override LanguageType Type => _typeFunctionType is null 73 | ? LanguageType.Unknown 74 | : new LanguageType(StaticType.Function, FunctionType: _typeFunctionType); 75 | 76 | public override Expression CloneExpression() 77 | { 78 | return new ArrowFunctionExpression(Range, Identifiers, Body.Optimize()) 79 | { 80 | _declarationStatement = _declarationStatement.CloneStatement(), 81 | _typeFunctionType = _typeFunctionType, 82 | _didBuild = _didBuild 83 | }; 84 | } 85 | 86 | public override Expression Optimize(LanguageType? suggestedType) 87 | { 88 | return new ArrowFunctionExpression(Range, Identifiers, Body.Optimize()) 89 | { 90 | _declarationStatement = _declarationStatement?.Optimize()!, 91 | _typeFunctionType = _typeFunctionType, 92 | _didBuild = _didBuild 93 | }; 94 | } 95 | 96 | int IBankSource.Bank => 0; 97 | 98 | public void BuildExpressionToB(YabalBuilder builder) 99 | { 100 | Build(builder); 101 | 102 | builder.SetB(_declarationStatement.Function.Label); 103 | } 104 | 105 | bool IExpressionToB.OverwritesA => false; 106 | } 107 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/AssignExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record AssignExpression(SourceRange Range, AssignableExpression Object, Expression Value) : Expression(Range) 4 | { 5 | public override void Initialize(YabalBuilder builder) 6 | { 7 | Object.Initialize(builder); 8 | Value.Initialize(builder); 9 | 10 | if (Value is ITypeExpression typeExpression) 11 | { 12 | typeExpression.Initialize(builder, Object.Type); 13 | } 14 | 15 | Object.MarkModified(); 16 | 17 | if (Value is ArrowFunctionExpression arrowFunction) 18 | { 19 | arrowFunction.Function.MarkUsed(); 20 | } 21 | } 22 | 23 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 24 | { 25 | Object.Assign(builder, Value, Range); 26 | Object.ShowDebug(builder); 27 | } 28 | 29 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 30 | { 31 | Object.Assign(builder, Value, Range); 32 | Object.ShowDebug(builder); 33 | } 34 | 35 | public override bool OverwritesB => true; 36 | 37 | public override LanguageType Type => Value.Type; 38 | 39 | public override Expression CloneExpression() 40 | { 41 | return new AssignExpression(Range, (AssignableExpression) Object.CloneExpression(), Value.CloneExpression()); 42 | } 43 | 44 | public override Expression Optimize(LanguageType? suggestedType) 45 | { 46 | return new AssignExpression(Range, Object, Value.Optimize(suggestedType ?? Object.Type)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/Constant/BooleanExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record BooleanExpression(SourceRange Range, bool Value) : Expression(Range), IConstantValue, IExpressionToB 4 | { 5 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 6 | { 7 | BuildExpressionCore(builder, false, suggestedType); 8 | builder.StoreA(pointer); 9 | } 10 | 11 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 12 | { 13 | builder.SetA(Value ? 1 : 0); 14 | builder.SetComment($"load boolean {(Value ? "true" : "false")}"); 15 | } 16 | 17 | void IExpressionToB.BuildExpressionToB(YabalBuilder builder) 18 | { 19 | builder.SetB(Value ? 1 : 0); 20 | builder.SetComment($"load boolean {(Value ? "true" : "false")}"); 21 | } 22 | 23 | object? IConstantValue.Value => Value; 24 | 25 | public void StoreConstantValue(Span buffer) 26 | { 27 | buffer[0] = Value ? 1 : 0; 28 | } 29 | 30 | public override bool OverwritesB => false; 31 | 32 | public override LanguageType Type => LanguageType.Boolean; 33 | 34 | bool IExpressionToB.OverwritesA => false; 35 | 36 | public override Expression CloneExpression() 37 | { 38 | return this; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/Constant/CastExpression.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Exceptions; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public record CastExpression(SourceRange Range, LanguageType CastType, Expression Expression) : AddressExpression(Range) 6 | { 7 | private CallExpression? _callExpression; 8 | 9 | public override void Initialize(YabalBuilder builder) 10 | { 11 | Expression.Initialize(builder); 12 | 13 | if (Expression is ITypeExpression typeExpression) 14 | { 15 | typeExpression.Initialize(builder, CastType); 16 | } 17 | 18 | if (builder.CastOperators.TryGetValue((Expression.Type, CastType), out var castOperator)) 19 | { 20 | _callExpression = new CallExpression(Range, castOperator, new List { Expression }); 21 | castOperator.MarkUsed(); 22 | } 23 | else if (CastType.Size != Expression.Type.Size) 24 | { 25 | throw new InvalidCodeException("Cannot cast between types of different sizes", Range); 26 | } 27 | } 28 | 29 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 30 | { 31 | if (_callExpression is { } callExpression) 32 | { 33 | callExpression.BuildExpressionToPointer(builder, suggestedType, pointer); 34 | } 35 | else 36 | { 37 | Expression.BuildExpressionToPointer(builder, suggestedType, pointer); 38 | } 39 | } 40 | 41 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 42 | { 43 | if (_callExpression is { } callExpression) 44 | { 45 | callExpression.BuildExpression(builder, isVoid, suggestedType); 46 | } 47 | else 48 | { 49 | Expression.BuildExpression(builder, false, suggestedType); 50 | } 51 | } 52 | 53 | public override bool OverwritesB => Expression.OverwritesB; 54 | 55 | public override LanguageType Type => CastType; 56 | 57 | public override Pointer? Pointer => _callExpression is null ? (Expression as AddressExpression)?.Pointer : null; 58 | 59 | public override bool DirectCopy => _callExpression is null && ((Expression as AddressExpression)?.DirectCopy ?? false); 60 | 61 | public override void StoreAddressInA(YabalBuilder builder, int offset) 62 | { 63 | if (_callExpression is null && Expression is AddressExpression addressExpression) 64 | { 65 | addressExpression.StoreAddressInA(builder, offset); 66 | } 67 | else 68 | { 69 | throw new NotSupportedException(); 70 | } 71 | } 72 | 73 | public override Expression Optimize(LanguageType? suggestedType) 74 | { 75 | return _callExpression?.Optimize(suggestedType) ?? new CastExpression(Range, CastType, Expression.Optimize(suggestedType)) { _callExpression = _callExpression }; 76 | } 77 | 78 | public override AddressExpression CloneExpression() 79 | { 80 | return new CastExpression(Range, Type, Expression.CloneExpression()) { _callExpression = _callExpression }; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/Constant/CharExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record CharExpression(SourceRange Range, char Value) : Expression(Range), IConstantValue, IExpressionToB 4 | { 5 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 6 | { 7 | BuildExpressionCore(builder, false, suggestedType); 8 | builder.StoreA(pointer); 9 | } 10 | 11 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 12 | { 13 | if (Character.CharToInt.TryGetValue(Value, out var intValue)) 14 | { 15 | builder.SetA(intValue); 16 | } 17 | else 18 | { 19 | builder.SetA(0); 20 | builder.AddError(ErrorLevel.Error, Range, ErrorMessages.InvalidCharacter(Value)); 21 | } 22 | 23 | builder.SetComment($"load character '{Value}'"); 24 | } 25 | 26 | void IExpressionToB.BuildExpressionToB(YabalBuilder builder) 27 | { 28 | if (Character.CharToInt.TryGetValue(Value, out var intValue)) 29 | { 30 | builder.SetB(intValue); 31 | } 32 | else 33 | { 34 | builder.SetB(0); 35 | builder.AddError(ErrorLevel.Error, Range, ErrorMessages.InvalidCharacter(Value)); 36 | } 37 | 38 | builder.SetComment($"load character '{Value}'"); 39 | } 40 | 41 | object? IConstantValue.Value => Character.CharToInt.TryGetValue(Value, out var intValue) ? intValue : 0; 42 | 43 | public void StoreConstantValue(Span buffer) 44 | { 45 | buffer[0] = Character.CharToInt.TryGetValue(Value, out var intValue) ? intValue : 0; 46 | } 47 | 48 | public override bool OverwritesB => false; 49 | 50 | public override LanguageType Type => LanguageType.Char; 51 | 52 | bool IExpressionToB.OverwritesA => false; 53 | 54 | public override string ToString() 55 | { 56 | return $"'{Value}'"; 57 | } 58 | 59 | public override Expression CloneExpression() 60 | { 61 | return this; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/Constant/IntegerExpressionBase.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public record IntegerExpression(SourceRange Range, int Value) : IntegerExpressionBase(Range) 6 | { 7 | public override string ToString() 8 | { 9 | return Value.ToString(); 10 | } 11 | } 12 | 13 | public abstract record IntegerExpressionBase(SourceRange Range) : Expression(Range), IExpressionToB, IConstantValue 14 | { 15 | public abstract int Value { get; init; } 16 | 17 | public void StoreConstantValue(Span buffer) 18 | { 19 | buffer[0] = Value; 20 | } 21 | 22 | public bool IsSmall => Value is >= 0 and <= InstructionReference.MaxData; 23 | 24 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 25 | { 26 | BuildExpressionCore(builder, false, suggestedType); 27 | builder.StoreA(pointer); 28 | } 29 | 30 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 31 | { 32 | if (IsSmall) 33 | { 34 | builder.SetA(Value); 35 | builder.SetComment("load small integer"); 36 | } 37 | else 38 | { 39 | builder.SetA_Large(Value); 40 | builder.SetComment("load large integer"); 41 | } 42 | } 43 | 44 | void IExpressionToB.BuildExpressionToB(YabalBuilder builder) 45 | { 46 | if (IsSmall) 47 | { 48 | builder.SetB(Value); 49 | builder.SetComment("load small integer"); 50 | } 51 | else 52 | { 53 | builder.SetB_Large(Value); 54 | builder.SetComment("load large integer"); 55 | } 56 | } 57 | 58 | bool IExpressionToB.OverwritesA => false; 59 | 60 | object? IConstantValue.Value => Value; 61 | 62 | public override bool OverwritesB => false; 63 | 64 | public override LanguageType Type => LanguageType.Integer; 65 | 66 | public override Expression CloneExpression() 67 | { 68 | return this; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/Constant/SizeOfExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record SizeOfExpression(SourceRange Range, Expression Expression) : IntegerExpressionBase(Range) 4 | { 5 | public override void Initialize(YabalBuilder builder) 6 | { 7 | Expression.Initialize(builder); 8 | } 9 | 10 | public override bool OverwritesB => false; 11 | 12 | public override int Value 13 | { 14 | get => ((Expression as IConstantValue)?.Value as IAddress)?.Length ?? Type.Size; 15 | init => throw new NotSupportedException(); 16 | } 17 | 18 | public override string ToString() 19 | { 20 | return $"sizeof({Expression})"; 21 | } 22 | 23 | public override Expression CloneExpression() 24 | { 25 | return new SizeOfExpression(Range, Expression.CloneExpression()); 26 | } 27 | 28 | public override Expression Optimize(LanguageType? suggestedType) 29 | { 30 | return new IntegerExpression(Range, Value); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/Constant/StringExpression.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public record StringExpression(SourceRange Range, string Value) : AddressExpression(Range), IConstantValue, IPointerSource, IBankSource 6 | { 7 | private InstructionPointer _pointer = null!; 8 | 9 | public override void Initialize(YabalBuilder builder) 10 | { 11 | _pointer = builder.GetString(Value); 12 | } 13 | 14 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 15 | { 16 | builder.SetA(_pointer); 17 | builder.StoreA(pointer); 18 | } 19 | 20 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 21 | { 22 | builder.SetA(_pointer); 23 | } 24 | 25 | public override void StoreAddressInA(YabalBuilder builder, int offset) 26 | { 27 | builder.SetA(_pointer.Add(offset)); 28 | } 29 | 30 | public override bool OverwritesB => false; 31 | 32 | public override LanguageType Type => LanguageType.Pointer(LanguageType.Char); 33 | 34 | object IConstantValue.Value => StringAddress.From(Value, _pointer); 35 | 36 | public void StoreConstantValue(Span buffer) 37 | { 38 | buffer[0] = _pointer.Address; 39 | buffer[1] = _pointer.Bank; 40 | } 41 | 42 | public override string ToString() 43 | { 44 | return $"\"{Value}\""; 45 | } 46 | 47 | public override StringExpression CloneExpression() 48 | { 49 | return this; 50 | } 51 | 52 | public override Pointer Pointer => _pointer; 53 | 54 | public override int? Bank => 0; 55 | 56 | int IBankSource.Bank => 0; 57 | 58 | public override bool DirectCopy => false; 59 | } 60 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/ConstantMemoryExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record ConstantMemoryExpression(SourceRange Range, LanguageType Type, int[] Data) : Expression(Range) 4 | { 5 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 6 | { 7 | for (var i = 0; i < Data.Length; i++) 8 | { 9 | builder.SetA(Data[i]); 10 | builder.SetComment($"constant value: {Type} offset {i}"); 11 | builder.StoreA(pointer.Add(i)); 12 | } 13 | } 14 | 15 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 16 | { 17 | builder.SetA(Data[0]); 18 | } 19 | 20 | public override bool OverwritesB => false; 21 | 22 | public override LanguageType Type { get; } = Type; 23 | 24 | public override Expression CloneExpression() 25 | { 26 | return new ConstantMemoryExpression(Range, Type, Data); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/CreatePointerExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record CreatePointerExpression(SourceRange Range, Expression Value, int BankValue, LanguageType ElementType) : AddressExpression(Range), IConstantValue, IBankSource 4 | { 5 | public override void Initialize(YabalBuilder builder) 6 | { 7 | Value.Initialize(builder); 8 | } 9 | 10 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 11 | { 12 | Value.BuildExpression(builder, false, null); 13 | builder.StoreA(pointer); 14 | 15 | if (suggestedType.Size == 2) 16 | { 17 | builder.SetA(1); 18 | builder.StoreA(pointer.Add(1)); 19 | } 20 | } 21 | 22 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 23 | { 24 | Value.BuildExpression(builder, isVoid, suggestedType); 25 | } 26 | 27 | public override int? Bank => BankValue; 28 | 29 | public override void StoreAddressInA(YabalBuilder builder, int offset) 30 | { 31 | Value.BuildExpression(builder, false, null); 32 | } 33 | 34 | public bool HasConstantValue => Pointer is not null; 35 | 36 | object? IConstantValue.Value => Pointer is {} pointer 37 | ? RawAddress.From(ElementType, pointer) 38 | : null; 39 | 40 | public void StoreConstantValue(Span buffer) 41 | { 42 | if (Pointer is { } pointer) 43 | { 44 | buffer[0] = pointer.Address; 45 | buffer[1] = pointer.Bank; 46 | } 47 | else 48 | { 49 | throw new InvalidOperationException("Cannot store a null pointer."); 50 | } 51 | } 52 | 53 | public override bool OverwritesB => Value.OverwritesB; 54 | 55 | public override LanguageType Type => LanguageType.Pointer(ElementType); 56 | 57 | public override CreatePointerExpression CloneExpression() 58 | { 59 | return new CreatePointerExpression(Range, Value.CloneExpression(), BankValue, ElementType); 60 | } 61 | 62 | public override Pointer? Pointer => Value is IConstantValue { Value: int value } 63 | ? new AbsolutePointer(value, BankValue) 64 | : null; 65 | 66 | int IBankSource.Bank => BankValue; 67 | } 68 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/IncludeFileExpression.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public record IncludeFileExpression(SourceRange Range, string Path, FileType FileType) : Expression(Range), IConstantValue, IPointerSource, IBankSource 6 | { 7 | private FileAddress _address = null!; 8 | private readonly LanguageType _type = LanguageType.Pointer(LanguageType.Integer); 9 | 10 | public override void Initialize(YabalBuilder builder) 11 | { 12 | _address = builder.GetFile(Range, Path, FileType); 13 | } 14 | 15 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 16 | { 17 | builder.SetA_Large(_address.Pointer); 18 | builder.StoreA(pointer); 19 | } 20 | 21 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 22 | { 23 | builder.SetA_Large(_address.Pointer); 24 | } 25 | 26 | public override bool OverwritesB => false; 27 | 28 | public override LanguageType Type => _type; 29 | 30 | public object Value => _address; 31 | 32 | public void StoreConstantValue(Span buffer) 33 | { 34 | buffer[0] = _address.Pointer.Address; 35 | buffer[1] = _address.Pointer.Bank; 36 | } 37 | 38 | public override IncludeFileExpression CloneExpression() 39 | { 40 | return new IncludeFileExpression(Range, Path, FileType) 41 | { 42 | _address = _address 43 | }; 44 | } 45 | 46 | int IBankSource.Bank => 0; 47 | Pointer IPointerSource.Pointer => _address.Pointer; 48 | } 49 | 50 | public enum FileType 51 | { 52 | Byte, 53 | Image, 54 | Font 55 | } 56 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/ReferenceExpression.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Visitor; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public record ReferenceExpression(SourceRange Range, Expression Expression) : Expression(Range), IVariableSource 6 | { 7 | public override void Initialize(YabalBuilder builder) 8 | { 9 | Expression.Initialize(builder); 10 | 11 | if (Expression is IdentifierExpression identifierExpression) 12 | { 13 | identifierExpression.MarkModified(); 14 | } 15 | 16 | if (Expression.Type.StaticType == StaticType.Reference) 17 | { 18 | builder.AddError(ErrorLevel.Warning, Range, "Taking a reference of a reference is redundant"); 19 | } 20 | } 21 | 22 | public override bool OverwritesB => Expression.OverwritesB; 23 | 24 | public (IVariable, int? Offset) GetVariable(YabalBuilder builder) 25 | { 26 | if (Expression is not IVariableSource variableSource) 27 | { 28 | builder.AddError(ErrorLevel.Error, Range, "Cannot take reference of non-variable expression"); 29 | return default; 30 | } 31 | 32 | return variableSource.GetVariable(builder); 33 | } 34 | 35 | public bool CanGetVariable => Expression is IVariableSource; 36 | 37 | public override LanguageType Type => LanguageType.Reference(Expression.Type); 38 | 39 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 40 | { 41 | BuildExpressionCore(builder, false, suggestedType); 42 | builder.StoreA(pointer); 43 | } 44 | 45 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 46 | { 47 | if (Expression.Type.StaticType == StaticType.Reference) 48 | { 49 | Expression.BuildExpression(builder, isVoid, suggestedType); 50 | return; 51 | } 52 | 53 | if (Expression is not AddressExpression addressExpression) 54 | { 55 | builder.AddError(ErrorLevel.Error, Range, "Cannot take reference of non-address expression"); 56 | return; 57 | } 58 | 59 | if (addressExpression.Bank != 0) 60 | { 61 | builder.AddError(ErrorLevel.Error, Range, "Cannot take reference of expression with non-zero bank"); 62 | return; 63 | } 64 | 65 | addressExpression.StoreAddressInA(builder); 66 | } 67 | 68 | public override Expression CloneExpression() 69 | { 70 | return new ReferenceExpression(Range, Expression.CloneExpression()); 71 | } 72 | 73 | public override Expression Optimize(LanguageType? suggestedType) 74 | { 75 | return new ReferenceExpression(Range, Expression.Optimize(suggestedType)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/StackAllocationExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record StackAllocationExpression(SourceRange Range, LanguageType PointerType, Expression Length) : AddressExpression(Range), IConstantValue, IPointerSource, IBankSource 4 | { 5 | private int? _fixedOffset; 6 | 7 | public override void Initialize(YabalBuilder builder) 8 | { 9 | builder.HasStackAllocation = true; 10 | 11 | if (builder.Block.IsGlobal && Length is IConstantValue { Value: int length }) 12 | { 13 | var size = PointerType.Size; 14 | _fixedOffset = builder.Options.StackAllocationStart; 15 | builder.Options.StackAllocationStart += size * length; 16 | } 17 | } 18 | 19 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 20 | { 21 | BuildExpressionCore(builder, false, suggestedType); 22 | builder.StoreA(pointer); 23 | } 24 | 25 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 26 | { 27 | if (_fixedOffset.HasValue) 28 | { 29 | builder.SetA(_fixedOffset.Value); 30 | } 31 | else 32 | { 33 | using var temp = builder.GetTemporaryVariable(global: true); 34 | 35 | builder.LoadA(builder.StackAllocPointer); 36 | builder.StoreA(temp); 37 | 38 | Length.BuildExpression(builder, false, suggestedType); 39 | builder.LoadB(builder.StackAllocPointer); 40 | builder.Add(); 41 | builder.StoreA(builder.StackAllocPointer); 42 | 43 | builder.LoadA(temp); 44 | } 45 | } 46 | 47 | public override bool OverwritesB => true; 48 | 49 | public override bool DirectCopy => false; 50 | 51 | public override LanguageType Type => LanguageType.RefPointer(PointerType); 52 | 53 | public override Pointer? Pointer => _fixedOffset.HasValue ? new AbsolutePointer(_fixedOffset.Value, 0) : null; 54 | 55 | public override void StoreAddressInA(YabalBuilder builder, int offset) 56 | { 57 | BuildExpressionCore(builder, false, null); 58 | } 59 | 60 | public override AddressExpression CloneExpression() 61 | { 62 | return new StackAllocationExpression(Range, PointerType, Length) 63 | { 64 | _fixedOffset = _fixedOffset 65 | }; 66 | } 67 | 68 | public override Expression Optimize(LanguageType? suggestedType) 69 | { 70 | return new StackAllocationExpression(Range, PointerType, Length.Optimize(suggestedType)) 71 | { 72 | _fixedOffset = _fixedOffset 73 | }; 74 | } 75 | 76 | object? IConstantValue.Value => Pointer is {} pointer 77 | ? RawAddress.From(LanguageType.Pointer(PointerType), pointer) 78 | : null; 79 | 80 | public void StoreConstantValue(Span buffer) 81 | { 82 | if (Pointer is { } pointer) 83 | { 84 | buffer[0] = pointer.Address; 85 | buffer[1] = pointer.Bank; 86 | } 87 | else 88 | { 89 | throw new InvalidOperationException("Cannot store a null pointer."); 90 | } 91 | } 92 | 93 | int IBankSource.Bank => 0; 94 | } 95 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/SwitchExpression.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public record SwitchItem(List Cases, Expression Value); 6 | 7 | public record SwitchExpression(SourceRange Range, Expression Value, List Items, Expression Default) : Expression(Range) 8 | { 9 | public override void Initialize(YabalBuilder builder) 10 | { 11 | Value.Initialize(builder); 12 | 13 | foreach (var item in Items) 14 | { 15 | foreach (var @case in item.Cases) 16 | { 17 | @case.Initialize(builder); 18 | } 19 | 20 | item.Value.Initialize(builder); 21 | } 22 | 23 | Default.Initialize(builder); 24 | } 25 | 26 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 27 | { 28 | BuildSwitch(builder, suggestedType, e => e.BuildExpressionToPointer(builder, suggestedType, pointer)); 29 | } 30 | 31 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 32 | { 33 | BuildSwitch(builder, suggestedType, e => e.BuildExpression(builder, false, suggestedType)); 34 | } 35 | 36 | private void BuildSwitch(YabalBuilder builder, LanguageType? suggestedType, Action callback) 37 | { 38 | Value.BuildExpression(builder, false, suggestedType); 39 | 40 | using var tempValue = builder.GetTemporaryVariable(); 41 | builder.StoreA(tempValue); 42 | 43 | var end = builder.CreateLabel(); 44 | 45 | foreach (var item in Items) 46 | { 47 | var returnValue = builder.CreateLabel(); 48 | var next = builder.CreateLabel(); 49 | 50 | foreach (var caseValue in item.Cases) 51 | { 52 | caseValue.BuildExpression(builder, false, suggestedType); 53 | builder.LoadB(tempValue); 54 | builder.Sub(); 55 | builder.JumpIfZero(returnValue); 56 | builder.Jump(next); 57 | } 58 | 59 | builder.Mark(returnValue); 60 | callback(item.Value); 61 | builder.Jump(end); 62 | builder.Mark(next); 63 | } 64 | 65 | callback(Default); 66 | builder.Mark(end); 67 | } 68 | 69 | public override bool OverwritesB => true; 70 | 71 | public override LanguageType Type => Default.Type; 72 | 73 | public override Expression CloneExpression() 74 | { 75 | return new SwitchExpression( 76 | Range, 77 | Value.CloneExpression(), 78 | Items.Select(i => new SwitchItem(i.Cases.Select(c => c.CloneExpression()).ToList(), i.Value.CloneExpression())).ToList(), 79 | Default.CloneExpression() 80 | ); 81 | } 82 | 83 | public override Expression Optimize(LanguageType? suggestedType) 84 | { 85 | var value = Value.Optimize(null); 86 | var items = Items.Select(i => new SwitchItem(i.Cases.Select(c => c.Optimize(suggestedType)).ToList(), i.Value.Optimize(suggestedType))).ToList(); 87 | var defaultValue = Default.Optimize(suggestedType); 88 | 89 | if (value is IConstantValue { Value: { } constantValue } && 90 | items.All(i => i.Cases.All(c => c is IConstantValue ))) 91 | { 92 | var item = items.FirstOrDefault(i => i.Cases.Any(c => c is IConstantValue { Value: { } caseValue } && Equals(caseValue, constantValue))); 93 | 94 | if (item is not null) 95 | { 96 | return item.Value; 97 | } 98 | 99 | return defaultValue; 100 | } 101 | 102 | return new SwitchExpression(Range, value, items, defaultValue); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/TernaryExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record TernaryExpression(SourceRange Range, Expression Expression, Expression Consequent, Expression Alternate) : Expression(Range) 4 | { 5 | public override void Initialize(YabalBuilder builder) 6 | { 7 | Expression.Initialize(builder); 8 | Consequent.Initialize(builder); 9 | Alternate.Initialize(builder); 10 | } 11 | 12 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 13 | { 14 | BuildTernary(builder, suggestedType, e => e.BuildExpressionToPointer(builder, suggestedType, pointer)); 15 | } 16 | 17 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 18 | { 19 | BuildTernary(builder, suggestedType, e => e.BuildExpression(builder, false, suggestedType)); 20 | } 21 | 22 | private void BuildTernary(YabalBuilder builder, LanguageType? suggestedType, Action callback) 23 | { 24 | var consequentLabel = builder.CreateLabel(); 25 | var alternateLabel = builder.CreateLabel(); 26 | var end = builder.CreateLabel(); 27 | var expression = Expression.Optimize(LanguageType.Boolean); 28 | 29 | expression.CreateComparison(builder, alternateLabel, consequentLabel); 30 | 31 | builder.Mark(consequentLabel); 32 | callback(Consequent); 33 | 34 | builder.Jump(end); 35 | builder.Mark(alternateLabel); 36 | 37 | callback(Alternate); 38 | builder.Mark(end); 39 | } 40 | 41 | public override Expression Optimize(LanguageType? suggestedType) 42 | { 43 | var expression = Expression.Optimize(suggestedType); 44 | var consequent = Consequent.Optimize(suggestedType); 45 | var alternate = Alternate.Optimize(suggestedType); 46 | 47 | if (expression is IConstantValue { Value: bool value }) 48 | { 49 | return value ? consequent : alternate; 50 | } 51 | 52 | return this with 53 | { 54 | Expression = expression, 55 | Consequent = consequent, 56 | Alternate = alternate 57 | }; 58 | } 59 | 60 | public override bool OverwritesB => true; 61 | 62 | public override LanguageType Type => Consequent.Type; 63 | 64 | public override Expression CloneExpression() 65 | { 66 | return new TernaryExpression( 67 | Range, 68 | Expression.CloneExpression(), 69 | Consequent.CloneExpression(), 70 | Alternate.CloneExpression() 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/UnaryExpression.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public record UnaryExpression(SourceRange Range, Expression Value, UnaryOperator Operator) : Expression(Range), IComparisonExpression 6 | { 7 | public override void Initialize(YabalBuilder builder) 8 | { 9 | Value.Initialize(builder); 10 | } 11 | 12 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 13 | { 14 | BuildExpression(builder, false, suggestedType); 15 | builder.StoreA(pointer); 16 | } 17 | 18 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 19 | { 20 | switch (Operator) 21 | { 22 | case UnaryOperator.Not: 23 | Value.BuildExpression(builder, isVoid, suggestedType); 24 | builder.Not(); 25 | break; 26 | case UnaryOperator.Minus: 27 | Value.BuildExpression(builder, isVoid, suggestedType); 28 | builder.SetB(-1); 29 | builder.Mult(); 30 | break; 31 | case UnaryOperator.Negate: 32 | { 33 | Value.BuildExpression(builder, isVoid, suggestedType); 34 | var skip = builder.CreateLabel(); 35 | 36 | builder.SetB(1); 37 | builder.Sub(); 38 | builder.JumpIfZero(skip); 39 | builder.SetA(1); 40 | builder.Mark(skip); 41 | break; 42 | } 43 | default: 44 | throw new InvalidOperationException(); 45 | } 46 | } 47 | 48 | public void CreateComparison(YabalBuilder builder, InstructionLabel falseLabel, InstructionLabel trueLabel) 49 | { 50 | if (Operator != UnaryOperator.Negate) 51 | { 52 | builder.AddError(ErrorLevel.Error, Range, ErrorMessages.InvalidComparison); 53 | return; 54 | } 55 | 56 | if (Value is IComparisonExpression comparisonExpression) 57 | { 58 | comparisonExpression.CreateComparison(builder, trueLabel, falseLabel); 59 | return; 60 | } 61 | 62 | Value.BuildExpression(builder, false, null); 63 | builder.SetB(0); 64 | builder.Sub(); 65 | builder.JumpIfZero(falseLabel); 66 | builder.Jump(trueLabel); 67 | } 68 | 69 | public override Expression Optimize(LanguageType? suggestedType) 70 | { 71 | var value = Value.Optimize(suggestedType); 72 | 73 | return Operator switch 74 | { 75 | UnaryOperator.Negate when value is IConstantValue { Value: bool b } => new BooleanExpression(Range, !b), 76 | UnaryOperator.Not when value is IConstantValue { Value: int i } => new IntegerExpression(Range, ~i & ushort.MaxValue), 77 | UnaryOperator.Minus when value is IConstantValue { Value: int i } => new IntegerExpression(Range, -i), 78 | _ => this 79 | }; 80 | } 81 | 82 | public override bool OverwritesB => true; 83 | 84 | public override LanguageType Type => 85 | Operator switch 86 | { 87 | UnaryOperator.Not => LanguageType.Integer, 88 | UnaryOperator.Negate => LanguageType.Boolean, 89 | UnaryOperator.Minus => LanguageType.Integer, 90 | _ => throw new InvalidOperationException() 91 | }; 92 | 93 | public override Expression CloneExpression() 94 | { 95 | return new UnaryExpression(Range, Value.CloneExpression(), Operator); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Expression/UpdateExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record UpdateExpression(SourceRange Range, AssignableExpression Value, bool Prefix, BinaryOperator Operator) : Expression(Range) 4 | { 5 | public override void Initialize(YabalBuilder builder) 6 | { 7 | Value.Initialize(builder); 8 | 9 | Value.MarkModified(); 10 | } 11 | 12 | public override void BuildExpressionToPointer(YabalBuilder builder, LanguageType suggestedType, Pointer pointer) 13 | { 14 | BuildExpression(builder, false, suggestedType); 15 | builder.StoreA(pointer); 16 | } 17 | 18 | protected override void BuildExpressionCore(YabalBuilder builder, bool isVoid, LanguageType? suggestedType) 19 | { 20 | var isPrefix = !isVoid && !Prefix; 21 | var variable = isPrefix ? builder.GetTemporaryVariable() : null; 22 | 23 | Value.BuildExpression(builder, false, suggestedType); 24 | 25 | if (variable != null) 26 | { 27 | builder.StoreA(variable); 28 | } 29 | 30 | builder.SetB(1); 31 | 32 | switch (Operator) 33 | { 34 | case BinaryOperator.Add: 35 | builder.Add(); 36 | builder.SetComment("increment value"); 37 | break; 38 | case BinaryOperator.Subtract: 39 | builder.Sub(); 40 | builder.SetComment("decrement value"); 41 | break; 42 | default: 43 | throw new InvalidOperationException("Unknown operator"); 44 | } 45 | 46 | Value.StoreFromA(builder, 0); 47 | 48 | if (variable != null) 49 | { 50 | builder.LoadA(variable); 51 | variable.Dispose(); 52 | } 53 | 54 | builder.AddVariableDebug(Range, Type); 55 | } 56 | 57 | public override bool OverwritesB => true; 58 | 59 | public override LanguageType Type => LanguageType.Integer; 60 | 61 | public override Expression CloneExpression() 62 | { 63 | return new UpdateExpression(Range, (AssignableExpression) Value.CloneExpression(), Prefix, Operator); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Node.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public abstract record Node(SourceRange Range) : INode 4 | { 5 | public virtual void Initialize(YabalBuilder builder) 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Visitor; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public abstract record Statement(SourceRange Range) : Node(Range) 6 | { 7 | public virtual void Declare(YabalBuilder builder) 8 | { 9 | } 10 | 11 | public abstract void Build(YabalBuilder builder); 12 | 13 | public abstract Statement CloneStatement(); 14 | 15 | public abstract Statement Optimize(); 16 | } 17 | 18 | public abstract record ScopeStatement(SourceRange Range) : Statement(Range) 19 | { 20 | private BlockStack? _block; 21 | 22 | protected BlockStack Block 23 | { 24 | get => _block ?? throw new InvalidOperationException("Block not set"); 25 | set => _block = value; 26 | } 27 | 28 | protected virtual BlockStack CreateBlock(YabalBuilder builder) 29 | { 30 | return builder.PushBlock(); 31 | } 32 | 33 | public virtual void OnDeclare(YabalBuilder builder) 34 | { 35 | } 36 | 37 | public sealed override void Declare(YabalBuilder builder) 38 | { 39 | OnDeclare(builder); 40 | } 41 | 42 | public virtual void OnInitialize(YabalBuilder builder) 43 | { 44 | } 45 | 46 | public sealed override void Initialize(YabalBuilder builder) 47 | { 48 | if (_block == null) 49 | { 50 | _block = CreateBlock(builder); 51 | } 52 | else 53 | { 54 | builder.PushBlock(_block); 55 | } 56 | OnInitialize(builder); 57 | builder.PopBlock(); 58 | } 59 | 60 | public virtual void OnBuild(YabalBuilder builder) 61 | { 62 | } 63 | 64 | public sealed override void Build(YabalBuilder builder) 65 | { 66 | builder.PushBlock(Block); 67 | OnBuild(builder); 68 | builder.PopBlock(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/BlockStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record BlockStatement(SourceRange Range, List Statements, bool NewScope = true) : Statement(Range) 4 | { 5 | public override void Declare(YabalBuilder builder) 6 | { 7 | foreach (var statement in Statements) 8 | { 9 | statement.Declare(builder); 10 | } 11 | } 12 | 13 | public override BlockStatement CloneStatement() 14 | { 15 | return new BlockStatement( 16 | Range, 17 | Statements.Select(s => s.CloneStatement()).ToList(), 18 | NewScope 19 | ); 20 | } 21 | 22 | public override BlockStatement Optimize() 23 | { 24 | return new BlockStatement( 25 | Range, 26 | Statements.Select(s => s.Optimize()).ToList(), 27 | NewScope 28 | ); 29 | } 30 | 31 | public override void Initialize(YabalBuilder builder) 32 | { 33 | foreach (var statement in Statements) 34 | { 35 | statement.Initialize(builder); 36 | } 37 | } 38 | 39 | public override void Build(YabalBuilder builder) 40 | { 41 | if (NewScope) 42 | { 43 | builder.PushBlock(); 44 | } 45 | 46 | foreach (var statement in Statements) 47 | { 48 | statement.Build(builder); 49 | } 50 | 51 | if (NewScope) 52 | { 53 | builder.PopBlock(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/BreakStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record BreakStatement(SourceRange Range) : Statement(Range) 4 | { 5 | public override void Initialize(YabalBuilder builder) 6 | { 7 | if (builder.Block.Break is null) 8 | { 9 | builder.AddError(ErrorLevel.Error, Range, ErrorMessages.BreakOutsideLoop); 10 | } 11 | } 12 | 13 | public override void Build(YabalBuilder builder) 14 | { 15 | builder.Jump(builder.Block.Break); 16 | } 17 | 18 | public override Statement CloneStatement() => this; 19 | 20 | public override Statement Optimize() => this; 21 | } 22 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/ContinueStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record ContinueStatement(SourceRange Range) : Statement(Range) 4 | { 5 | public override void Initialize(YabalBuilder builder) 6 | { 7 | if (builder.Block.Continue is null) 8 | { 9 | builder.AddError(ErrorLevel.Error, Range, ErrorMessages.ContinueOutsideLoop); 10 | } 11 | } 12 | 13 | public override void Build(YabalBuilder builder) 14 | { 15 | builder.Jump(builder.Block.Continue); 16 | } 17 | 18 | public override Statement CloneStatement() => this; 19 | 20 | public override Statement Optimize() => this; 21 | } 22 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/EmptyStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record EmptyStatement(SourceRange Range) : Statement(Range) 4 | { 5 | public override void Build(YabalBuilder builder) 6 | { 7 | } 8 | 9 | public override Statement CloneStatement() => this; 10 | 11 | public override Statement Optimize() => this; 12 | } 13 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/ExpressionStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record ExpressionStatement(SourceRange Range, Expression Expression) : Statement(Range) 4 | { 5 | public override void Initialize(YabalBuilder builder) 6 | { 7 | Expression.Initialize(builder); 8 | } 9 | 10 | public override void Build(YabalBuilder builder) 11 | { 12 | if (builder.Debug && Expression is CallExpression { Type.StaticType: not (StaticType.Void or StaticType.Unknown) } callExpression) 13 | { 14 | var temp = builder.GetTemporaryVariable(global: true, size: callExpression.Type.Size); 15 | 16 | callExpression.BuildExpressionToPointer(builder, callExpression.Type, temp); 17 | 18 | builder.AddVariableDebug(Range, callExpression.Type, temp); 19 | } 20 | else 21 | { 22 | Expression.BuildExpression(builder, true, null); 23 | 24 | if (Expression is AddressExpression address and (IdentifierExpression or MemberExpression or ArrayAccessExpression)) 25 | { 26 | address.ShowDebug(builder); 27 | } 28 | } 29 | } 30 | 31 | public bool ShowDebug => Expression is 32 | (AddressExpression and (IdentifierExpression or MemberExpression or ArrayAccessExpression)) or 33 | (CallExpression { Type.StaticType: not StaticType.Void }); 34 | 35 | public override Statement CloneStatement() 36 | { 37 | return new ExpressionStatement(Range, Expression.CloneExpression()); 38 | } 39 | 40 | public override Statement Optimize() 41 | { 42 | return new ExpressionStatement(Range, Expression switch 43 | { 44 | IdentifierExpression or MemberExpression or ArrayAccessExpression or CallExpression => Expression, 45 | _ => Expression.Optimize(null) 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/ForStatement.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public record ForStatement(SourceRange Range, Statement? Init, Statement? Update, Expression Test, BlockStatement Body) : ScopeStatement(Range) 6 | { 7 | private InstructionLabel _nextLabel; 8 | private InstructionLabel _bodyLabel; 9 | private InstructionLabel _endLabel; 10 | private InstructionLabel _testLabel; 11 | 12 | public override void OnDeclare(YabalBuilder builder) 13 | { 14 | Init?.Declare(builder); 15 | Update?.Declare(builder); 16 | Body.Declare(builder); 17 | } 18 | 19 | public override void OnInitialize(YabalBuilder builder) 20 | { 21 | _nextLabel = builder.CreateLabel(); 22 | _bodyLabel = builder.CreateLabel(); 23 | _endLabel = builder.CreateLabel(); 24 | _testLabel = builder.CreateLabel(); 25 | 26 | Block.Continue = _nextLabel; 27 | Block.Break = _endLabel; 28 | 29 | Init?.Initialize(builder); 30 | Update?.Initialize(builder); 31 | Test.Initialize(builder); 32 | Body.Initialize(builder); 33 | } 34 | 35 | public override void OnBuild(YabalBuilder builder) 36 | { 37 | Init?.Build(builder); 38 | builder.Jump(_testLabel); 39 | 40 | builder.Mark(_nextLabel); 41 | Update?.Build(builder); 42 | 43 | builder.Mark(_testLabel); 44 | Test.CreateComparison(builder, _endLabel, _bodyLabel); 45 | 46 | builder.Mark(_bodyLabel); 47 | Body.Build(builder); 48 | builder.Jump(_nextLabel); 49 | builder.SetComment("jump to next iteration"); 50 | 51 | builder.Mark(_endLabel); 52 | } 53 | 54 | public override Statement CloneStatement() 55 | { 56 | return new ForStatement( 57 | Range, 58 | Init?.CloneStatement(), 59 | Update?.CloneStatement(), 60 | Test.CloneExpression(), 61 | Body.CloneStatement() 62 | ); 63 | } 64 | 65 | public override Statement Optimize() 66 | { 67 | return new ForStatement( 68 | Range, 69 | Init?.Optimize(), 70 | Update?.Optimize(), 71 | Test.Optimize(LanguageType.Boolean), 72 | Body.Optimize() 73 | ) 74 | { 75 | Block = Block, 76 | _nextLabel = _nextLabel, 77 | _bodyLabel = _bodyLabel, 78 | _endLabel = _endLabel, 79 | _testLabel = _testLabel 80 | }; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/GlobalNamespaceStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record GlobalNamespaceStatement(SourceRange Range, Namespace Namespace) : Statement(Range) 4 | { 5 | public override void Declare(YabalBuilder builder) 6 | { 7 | builder.Block.Namespace = Namespace; 8 | } 9 | 10 | public override void Build(YabalBuilder builder) 11 | { 12 | } 13 | 14 | public override Statement CloneStatement() 15 | { 16 | return new GlobalNamespaceStatement(Range, Namespace); 17 | } 18 | 19 | public override Statement Optimize() 20 | { 21 | return this; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/GotoStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record GotoStatement(SourceRange Range, string Name) : Statement(Range) 4 | { 5 | public override void Build(YabalBuilder builder) 6 | { 7 | if (!builder.Block.TryGetLabel(Name, out var label)) 8 | { 9 | builder.AddError(ErrorLevel.Error, Range, ErrorMessages.UndefinedLabel(Name)); 10 | return; 11 | } 12 | 13 | builder.Jump(label); 14 | } 15 | 16 | public override Statement CloneStatement() => this; 17 | 18 | public override Statement Optimize() => this; 19 | } 20 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/IfStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record IfStatement(SourceRange Range, Expression Expression, Statement Consequent, Statement? Alternate) : Statement(Range) 4 | { 5 | public override void Declare(YabalBuilder builder) 6 | { 7 | Consequent.Declare(builder); 8 | Alternate?.Declare(builder); 9 | } 10 | 11 | public override Statement CloneStatement() 12 | { 13 | return new IfStatement( 14 | Range, 15 | Expression.CloneExpression(), 16 | Consequent.CloneStatement(), 17 | Alternate?.CloneStatement() 18 | ); 19 | } 20 | 21 | public override void Initialize(YabalBuilder builder) 22 | { 23 | Expression.Initialize(builder); 24 | Consequent.Initialize(builder); 25 | Alternate?.Initialize(builder); 26 | } 27 | 28 | public override void Build(YabalBuilder builder) 29 | { 30 | var consequentLabel = builder.CreateLabel(); 31 | var alternateLabel = Alternate != null ? builder.CreateLabel() : null; 32 | var end = builder.CreateLabel(); 33 | 34 | switch (Expression) 35 | { 36 | case IComparisonExpression binaryExpression: 37 | { 38 | binaryExpression.CreateComparison(builder, alternateLabel ?? end, consequentLabel); 39 | break; 40 | } 41 | default: 42 | { 43 | Expression.BuildExpression(builder, false, LanguageType.Boolean); 44 | builder.SetB(0); 45 | builder.Sub(); 46 | builder.JumpIfZero(alternateLabel ?? end); 47 | builder.Jump(consequentLabel); 48 | break; 49 | } 50 | } 51 | 52 | builder.Mark(consequentLabel); 53 | Consequent.Build(builder); 54 | 55 | if (alternateLabel != null) 56 | { 57 | builder.Jump(end); 58 | builder.Mark(alternateLabel); 59 | Alternate!.Build(builder); 60 | } 61 | 62 | builder.Mark(end); 63 | } 64 | 65 | public override Statement Optimize() 66 | { 67 | var expression = Expression.Optimize(LanguageType.Boolean); 68 | var consequent = Consequent.Optimize(); 69 | var alternate = Alternate?.Optimize(); 70 | 71 | if (expression is IConstantValue { Value: bool value }) 72 | { 73 | return value ? consequent : alternate ?? new EmptyStatement(Range); 74 | } 75 | 76 | return new IfStatement(Range, expression, consequent, alternate); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/ImportStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record ImportStatement(SourceRange Range, ProgramStatement Program, Dictionary? Mappings = null) : ScopeStatement(Range) 4 | { 5 | public override void OnDeclare(YabalBuilder builder) 6 | { 7 | Block = builder.PushBlock(); 8 | Program.Declare(builder); 9 | builder.PopBlock(); 10 | } 11 | 12 | public override void OnInitialize(YabalBuilder builder) 13 | { 14 | Program.Initialize(builder); 15 | } 16 | 17 | public override void OnBuild(YabalBuilder builder) 18 | { 19 | Program.Build(builder); 20 | } 21 | 22 | public override Statement CloneStatement() 23 | { 24 | return new ImportStatement(Range, Program, Mappings) 25 | { 26 | Block = Block 27 | }; 28 | } 29 | 30 | public override Statement Optimize() 31 | { 32 | return new ImportStatement(Range, Program, Mappings) 33 | { 34 | Block = Block 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/LabelStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record LabelStatement(SourceRange Range, string Name) : Statement(Range) 4 | { 5 | public override void Build(YabalBuilder builder) 6 | { 7 | var label = builder.CreateLabel(); 8 | 9 | if (!builder.Block.Labels.TryAdd(Name, label)) 10 | { 11 | builder.AddError(ErrorLevel.Error, Range, ErrorMessages.DuplicateLabel(Name)); 12 | return; 13 | } 14 | 15 | builder.Mark(label); 16 | } 17 | 18 | public override Statement CloneStatement() => this; 19 | 20 | public override Statement Optimize() => this; 21 | } 22 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/NamespaceStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record NamespaceStatement(SourceRange Range, Namespace Namespace, BlockStatement Body) : ScopeStatement(Range) 4 | { 5 | public override void OnDeclare(YabalBuilder builder) 6 | { 7 | if (builder.Block.Namespace.Namespaces.Count > 0) 8 | { 9 | builder.Block.Namespace = new Namespace( 10 | builder.Block.Namespace.Namespaces.Concat(Namespace.Namespaces).ToArray() 11 | ); 12 | } 13 | else 14 | { 15 | builder.Block.Namespace = Namespace; 16 | } 17 | 18 | Body.Declare(builder); 19 | } 20 | 21 | public override void OnInitialize(YabalBuilder builder) 22 | { 23 | Body.Initialize(builder); 24 | } 25 | 26 | public override void OnBuild(YabalBuilder builder) 27 | { 28 | Body.Build(builder); 29 | } 30 | 31 | public override Statement CloneStatement() 32 | { 33 | return new NamespaceStatement(Range, Namespace, Body.CloneStatement()); 34 | } 35 | 36 | public override Statement Optimize() 37 | { 38 | return new NamespaceStatement(Range, Namespace, Body.Optimize()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/ProgramStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record ProgramStatement( 4 | SourceRange Range, 5 | List Statements 6 | ) : BlockStatement(Range, Statements, false) 7 | { 8 | public override ProgramStatement Optimize() 9 | { 10 | return new ProgramStatement(Range, Statements.Select(s => s.Optimize()).ToList()); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/ReturnStatement.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Exceptions; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public record ReturnStatement(SourceRange Range, Expression? Expression) : Statement(Range) 6 | { 7 | public LanguageType? ReturnType { get; set; } 8 | 9 | public override void Initialize(YabalBuilder builder) 10 | { 11 | Expression?.Initialize(builder); 12 | ReturnType = builder.ReturnType; 13 | 14 | if (ReturnType != null && Expression is ITypeExpression typeExpression) 15 | { 16 | typeExpression.Initialize(builder, ReturnType); 17 | } 18 | } 19 | 20 | public override void Build(YabalBuilder builder) 21 | { 22 | if (builder.Block.Return == null) 23 | { 24 | builder.AddError(ErrorLevel.Error, Range, ErrorMessages.ReturnOutsideFunction); 25 | } 26 | 27 | var returnType = builder.ReturnType ?? throw new InvalidCodeException("Cannot return outside of a function", Range); 28 | 29 | Expression?.BuildExpressionToPointer(builder, returnType, builder.ReturnValue); 30 | 31 | if (builder.Block.Return != null) 32 | { 33 | builder.Jump(builder.Block.Return); 34 | } 35 | } 36 | 37 | public override Statement CloneStatement() 38 | { 39 | return new ReturnStatement(Range, Expression?.CloneExpression()) 40 | { 41 | ReturnType = ReturnType 42 | }; 43 | } 44 | 45 | public override Statement Optimize() 46 | { 47 | return new ReturnStatement(Range, Expression?.Optimize(ReturnType)) 48 | { 49 | ReturnType = ReturnType 50 | }; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/StructDeclarationStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record StructDeclarationStatement(SourceRange Range, LanguageStruct Struct) : Statement(Range) 4 | { 5 | public override void Initialize(YabalBuilder builder) 6 | { 7 | } 8 | 9 | public override void Build(YabalBuilder builder) 10 | { 11 | } 12 | 13 | public override Statement CloneStatement() => this; 14 | 15 | public override Statement Optimize() => this; 16 | } 17 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/UsingStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public record UsingStatement(SourceRange Range, Namespace Namespace) : Statement(Range) 4 | { 5 | public override void Declare(YabalBuilder builder) 6 | { 7 | builder.Block.AddUsing(Namespace); 8 | } 9 | 10 | public override void Build(YabalBuilder builder) 11 | { 12 | } 13 | 14 | public override Statement CloneStatement() 15 | { 16 | return new UsingStatement(Range, Namespace); 17 | } 18 | 19 | public override Statement Optimize() 20 | { 21 | return this; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/VariableDeclarationStatement.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Exceptions; 2 | using Yabal.Visitor; 3 | 4 | namespace Yabal.Ast; 5 | 6 | public record VariableDeclarationStatement(SourceRange Range, Identifier Name, bool Constant, Expression? Value = null, LanguageType? Type = null) : Statement(Range) 7 | { 8 | public Variable Variable { get; private set; } = null!; 9 | 10 | public override void Initialize(YabalBuilder builder) 11 | { 12 | Value?.Initialize(builder); 13 | 14 | var type = Type ?? Value?.Type; 15 | 16 | if (type is null || type.StaticType == StaticType.Unknown) 17 | { 18 | throw new InvalidCodeException("Variable type is not specified", Name.Range); 19 | } 20 | 21 | Variable = builder.CreateVariable(Name, type, Value); 22 | 23 | if (Value is ITypeExpression typeExpression) 24 | { 25 | typeExpression.Initialize(builder, type); 26 | } 27 | } 28 | 29 | public override void Build(YabalBuilder builder) 30 | { 31 | if (Value == null || Variable.CanBeRemoved && !builder.Debug) 32 | { 33 | return; 34 | } 35 | 36 | if (Value is IBankSource createPointer) 37 | { 38 | createPointer.BuildExpression(builder, false, Type); 39 | builder.StoreA(Variable.Pointer); 40 | 41 | builder.SetA(createPointer.Bank); 42 | builder.StoreA(Variable.Pointer.Add(1)); 43 | } 44 | else 45 | { 46 | builder.SetValue(Variable.Pointer, Variable.Type, Value); 47 | } 48 | 49 | builder.AddVariableDebug(Name.Range, Variable.Type, Variable.Pointer); 50 | } 51 | 52 | public override Statement CloneStatement() 53 | { 54 | return new VariableDeclarationStatement(Range, Name, Constant, Value?.CloneExpression(), Type); 55 | } 56 | 57 | public override Statement Optimize() 58 | { 59 | return new VariableDeclarationStatement(Range, Name, Constant, Value?.Optimize(Type), Type) 60 | { 61 | Variable = Variable 62 | }; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/Statement/WhileStatement.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal.Ast; 4 | 5 | public record WhileStatement(SourceRange Range, Expression Expression, BlockStatement Body) : ScopeStatement(Range) 6 | { 7 | private InstructionLabel _nextLabel = null!; 8 | private InstructionLabel _bodyLabel = null!; 9 | private InstructionLabel _endLabel = null!; 10 | 11 | public override void OnDeclare(YabalBuilder builder) 12 | { 13 | Body.Declare(builder); 14 | } 15 | 16 | public override void OnInitialize(YabalBuilder builder) 17 | { 18 | _nextLabel = builder.CreateLabel(); 19 | _bodyLabel = builder.CreateLabel(); 20 | _endLabel = builder.CreateLabel(); 21 | 22 | Block.Continue = _nextLabel; 23 | Block.Break = _endLabel; 24 | 25 | Expression.Initialize(builder); 26 | Body.Initialize(builder); 27 | } 28 | 29 | public override void OnBuild(YabalBuilder builder) 30 | { 31 | var expression = Expression.Optimize(LanguageType.Boolean); 32 | 33 | builder.Mark(_nextLabel); 34 | 35 | if (expression is not IConstantValue {Value: true}) 36 | { 37 | expression.CreateComparison(builder, _endLabel, _bodyLabel); 38 | builder.Mark(_bodyLabel); 39 | } 40 | 41 | Body.Build(builder); 42 | builder.Jump(_nextLabel); 43 | builder.Mark(_endLabel); 44 | } 45 | 46 | public override Statement CloneStatement() 47 | { 48 | return new WhileStatement(Range, Expression.CloneExpression(), Body.CloneStatement()); 49 | } 50 | 51 | public override Statement Optimize() 52 | { 53 | return new WhileStatement(Range, Expression.Optimize(LanguageType.Boolean), Body.Optimize()) 54 | { 55 | Block = Block, 56 | _nextLabel = _nextLabel, 57 | _bodyLabel = _bodyLabel, 58 | _endLabel = _endLabel 59 | }; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/StaticType.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public enum StaticType 4 | { 5 | Integer, 6 | Boolean, 7 | Void, 8 | Pointer, 9 | Struct, 10 | Assembly, 11 | Reference, 12 | Unknown, 13 | Char, 14 | Function 15 | } 16 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Ast/UnaryOperator.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Ast; 2 | 3 | public enum UnaryOperator 4 | { 5 | Not, 6 | Negate, 7 | Minus 8 | } 9 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/CompileError.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal; 2 | 3 | public record CompileError(SourceRange Range, ErrorLevel Level, string Message); 4 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/ErrorLevel.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal; 2 | 3 | public enum ErrorLevel 4 | { 5 | Error, 6 | Warning, 7 | Debug 8 | } 9 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Exceptions/InvalidCodeException.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Exceptions; 2 | 3 | public class InvalidCodeException : Exception 4 | { 5 | public InvalidCodeException(string? message, SourceRange? range) : base(message) 6 | { 7 | Range = range; 8 | } 9 | 10 | public SourceRange? Range { get; } 11 | } 12 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/FileContent.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Ast; 2 | 3 | namespace Yabal; 4 | 5 | public interface IFileLoader 6 | { 7 | ValueTask LoadAsync(YabalBuilder builder, SourceRange range, string path, FileReader reader); 8 | } 9 | 10 | public record FileContent(int Offset, int[] Data); 11 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Loaders/ByteLoader.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Loaders; 2 | 3 | public class ByteLoader : IFileLoader 4 | { 5 | public static readonly IFileLoader Instance = new ByteLoader(); 6 | 7 | public async ValueTask LoadAsync(YabalBuilder builder, SourceRange range, string path, 8 | FileReader reader) 9 | { 10 | var (_, bytes) = await reader.ReadAllBytesAsync(range, path); 11 | var content = new int[bytes.Length / 2 + 1]; 12 | var i = 0; 13 | content[i++] = bytes.Length / 2; 14 | 15 | var memory = new byte[2]; 16 | 17 | for (var j = 0; j < bytes.Length; j += 2) 18 | { 19 | if (j + 1 < bytes.Length) 20 | { 21 | content[i++] = memory[0] << 8; 22 | } 23 | else 24 | { 25 | content[i++] = memory[0] << 8 | memory[1]; 26 | } 27 | } 28 | 29 | return new FileContent(1, content); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Pointer/AbsolutePointer.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal; 4 | 5 | public class AbsolutePointer : Pointer 6 | { 7 | public AbsolutePointer(int address, int bank) 8 | { 9 | Address = address; 10 | Bank = bank; 11 | } 12 | 13 | public override string Name => Address.ToString(); 14 | 15 | public override bool IsSmall => Address < InstructionReference.MaxData; 16 | 17 | public override int Bank { get; } 18 | 19 | public override int Address { get; } 20 | 21 | public override int Get(IReadOnlyDictionary mappings) 22 | { 23 | return Address; 24 | } 25 | 26 | public override string ToString() 27 | { 28 | return $"[{Address}:{Bank}]"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Pointer/Pointer.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal; 4 | 5 | public abstract class Pointer 6 | { 7 | public abstract string? Name { get; } 8 | 9 | public abstract bool IsSmall { get; } 10 | 11 | public abstract int Bank { get; } 12 | 13 | public abstract int Address { get; } 14 | 15 | public abstract int Get(IReadOnlyDictionary mappings); 16 | 17 | public void CopyTo(YabalBuilder builder, Pointer pointer, int offset) 18 | { 19 | builder.LoadA(this.Add(offset)); 20 | builder.SetComment("load value"); 21 | builder.StoreA(pointer.Add(offset)); 22 | builder.SetComment("store value"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Pointer/PointerExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal; 2 | 3 | public static class PointerExtensions 4 | { 5 | public static Pointer Add(this Pointer pointer, int offset) 6 | { 7 | return offset == 0 ? pointer : new PointerWithOffset(pointer, offset); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Pointer/PointerWithOffset.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | 3 | namespace Yabal; 4 | 5 | public class PointerWithOffset : Pointer 6 | { 7 | private readonly Pointer _pointer; 8 | private readonly int _offset; 9 | 10 | public PointerWithOffset(Pointer pointer, int offset) 11 | { 12 | _pointer = pointer; 13 | _offset = offset; 14 | } 15 | 16 | public override string Name => $"{_pointer.Name}+{_offset}"; 17 | 18 | public override bool IsSmall => _pointer.IsSmall; 19 | 20 | public override int Bank => _pointer.Bank; 21 | 22 | public override int Address => _pointer.Address + _offset; 23 | 24 | public override int Get(IReadOnlyDictionary mappings) 25 | { 26 | var value = _pointer.Get(mappings); 27 | 28 | return value + _offset; 29 | } 30 | 31 | public override string ToString() 32 | { 33 | return $"{_pointer} + {_offset}"; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/PointerCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using Yabal.Ast; 3 | using Yabal.Instructions; 4 | using Yabal.Visitor; 5 | 6 | namespace Yabal; 7 | 8 | public class PointerCollection : IEnumerable 9 | { 10 | private readonly Dictionary> _pointersBySize = new(); 11 | private readonly string _name; 12 | private int _counter; 13 | 14 | public PointerCollection(string name) 15 | { 16 | _name = name; 17 | } 18 | 19 | public int Count => _pointersBySize.Sum(i => i.Value.Count); 20 | 21 | public int Size => _pointersBySize.Sum(i => i.Value.Count * i.Key); 22 | 23 | public InstructionPointer Get(int index, int size) 24 | { 25 | if (!_pointersBySize.TryGetValue(size, out var pointers)) 26 | { 27 | pointers = new List(); 28 | _pointersBySize[size] = pointers; 29 | } 30 | 31 | if (index < pointers.Count) 32 | { 33 | return pointers[index]; 34 | } 35 | 36 | var pointer = new InstructionPointer($"[{_name}:{_counter++}]", size, true); 37 | pointers.Add(pointer); 38 | return pointer; 39 | } 40 | 41 | public InstructionPointer GetNext(int size) 42 | { 43 | var index = _pointersBySize.TryGetValue(size, out var pointers) ? pointers.Count : 0; 44 | return Get(index, size); 45 | } 46 | 47 | public InstructionPointer GetNext(BlockStack block, int size) 48 | { 49 | return Get(block.GetNextOffset(size, block.IsGlobal), size); 50 | } 51 | 52 | public InstructionPointer GetNext(BlockStack block, LanguageType type) 53 | { 54 | return GetNext(block, type.Size); 55 | } 56 | 57 | public IEnumerator GetEnumerator() 58 | { 59 | return _pointersBySize.SelectMany(i => i.Value).GetEnumerator(); 60 | } 61 | 62 | IEnumerator IEnumerable.GetEnumerator() 63 | { 64 | return GetEnumerator(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/SourceRange.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using Antlr4.Runtime; 3 | 4 | namespace Yabal; 5 | 6 | public readonly record struct SourceRange(Uri File, int StartLine, int StartColumn, int EndLine, int EndColumn, int Index, int Length) : IComparable 7 | { 8 | public static readonly SourceRange Zero = default; 9 | 10 | public static (int stopLine, int stopColumn) CalculateStop(int line, int column, string text) 11 | { 12 | var match = Regex.Matches(text, @"(\r\n|\r|\n)"); 13 | 14 | int stopLine; 15 | int stopColumn; 16 | 17 | if (match.Count > 0) 18 | { 19 | var lastMatch = match[^1]; 20 | stopLine = line + match.Count; 21 | stopColumn = column + text.Length - (lastMatch.Index + lastMatch.Length); 22 | } 23 | else 24 | { 25 | stopLine = line; 26 | stopColumn = column + text.Length; 27 | } 28 | 29 | return (stopLine, stopColumn); 30 | } 31 | 32 | public static SourceRange From(ParserRuleContext context, Uri file) 33 | { 34 | var startLine = context.Start.Line; 35 | var startColumn = context.Start.Column; 36 | var end = context.stop ?? context.start; 37 | var text = end.Text; 38 | if (text == "") text = string.Empty; 39 | var (endLine, endColumn) = CalculateStop(end.Line, end.Column, text); 40 | 41 | return new SourceRange( 42 | file, 43 | startLine, 44 | startColumn, 45 | endLine, 46 | endColumn, 47 | context.Start.StartIndex, 48 | (end.StartIndex - context.Start.StartIndex) + text.Length 49 | ); 50 | } 51 | 52 | public static SourceRange From(IToken token, Uri file) 53 | { 54 | var startLine = token.Line; 55 | var startColumn = token.Column; 56 | var text = token.Text; 57 | var (endLine, endColumn) = CalculateStop(token.Line, token.Column, text); 58 | 59 | if (text == "") 60 | { 61 | text = string.Empty; 62 | } 63 | 64 | return new SourceRange( 65 | file, 66 | startLine, 67 | startColumn, 68 | endLine, 69 | endColumn, 70 | token.StartIndex, 71 | text.Length 72 | ); 73 | } 74 | 75 | public bool IsInRange(int line, int column) 76 | { 77 | return line >= StartLine && column >= StartColumn && 78 | line <= EndLine && column <= EndColumn; 79 | } 80 | 81 | public static SourceRange Combine(IEnumerable ranges) 82 | { 83 | var array = ranges as IReadOnlyList ?? ranges.ToArray(); 84 | var start = array.MinBy(r => r.Index); 85 | var end = array.MaxBy(r => r.Index + r.Length); 86 | 87 | return new SourceRange( 88 | start.File, 89 | start.StartLine, 90 | start.StartColumn, 91 | end.EndLine, 92 | end.EndColumn, 93 | start.Index, 94 | end.Index + end.Length - start.Index); 95 | } 96 | 97 | public override string ToString() 98 | { 99 | return $"{StartLine}:{StartColumn} - {EndLine}:{EndColumn} (index: {Index}, length: {Length})"; 100 | } 101 | 102 | public int CompareTo(SourceRange other) 103 | { 104 | return Index.CompareTo(other.Index); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/TemporaryVariable.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Instructions; 2 | using Yabal.Visitor; 3 | 4 | namespace Yabal; 5 | 6 | public sealed class TemporaryVariable : IDisposable 7 | { 8 | public TemporaryVariable(InstructionPointer pointer, BlockStack block) 9 | { 10 | Pointer = pointer; 11 | Block = block; 12 | } 13 | 14 | public InstructionPointer Pointer { get; } 15 | 16 | public BlockStack Block { get; } 17 | 18 | public void Dispose() 19 | { 20 | Block.TemporaryVariablesStack.Push(this); 21 | } 22 | 23 | public static implicit operator InstructionPointer(TemporaryVariable variable) 24 | { 25 | return variable.Pointer; 26 | } 27 | 28 | public static implicit operator PointerOrData(TemporaryVariable variable) 29 | { 30 | return variable.Pointer; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Visitor/AsmVisitor.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Ast; 2 | 3 | namespace Yabal.Visitor; 4 | 5 | public class AsmArgumentVisitor : YabalParserBaseVisitor 6 | { 7 | private Uri _file; 8 | 9 | public AsmArgumentVisitor(Uri file) 10 | { 11 | _file = file; 12 | } 13 | 14 | public override AsmArgument VisitAsmInteger(YabalParser.AsmIntegerContext context) 15 | { 16 | return new AsmInteger(SourceRange.From(context, _file), YabalVisitor.ParseInt(context.GetText())); 17 | } 18 | 19 | public override AsmArgument VisitAsmAddress(YabalParser.AsmAddressContext context) 20 | { 21 | return new AsmVariable(SourceRange.From(context, _file), new Identifier(SourceRange.From(context, _file), context.asmIdentifier().GetText())); 22 | } 23 | 24 | public override AsmArgument VisitAsmLabelReference(YabalParser.AsmLabelReferenceContext context) 25 | { 26 | return new AsmLabel(SourceRange.From(context, _file), context.asmIdentifier().GetText()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Visitor/BlockCompileStack.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace Yabal.Visitor; 4 | 5 | public class BlockCompileStack 6 | { 7 | public BlockCompileStack(BlockCompileStack? parent = null) 8 | { 9 | Parent = parent; 10 | } 11 | 12 | public BlockCompileStack? Parent { get; } 13 | 14 | public Dictionary Variables { get; } = new(); 15 | 16 | public int Offset { get; set; } 17 | 18 | public bool TryGetVariable(string name, [NotNullWhen(true)] out Variable? variable) 19 | { 20 | if (Variables.TryGetValue(name, out variable)) 21 | { 22 | return true; 23 | } 24 | 25 | if (Parent != null) 26 | { 27 | return Parent.TryGetVariable(name, out variable); 28 | } 29 | 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/Visitor/Variable.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Ast; 2 | using Yabal.Instructions; 3 | 4 | namespace Yabal.Visitor; 5 | 6 | public interface IVariable 7 | { 8 | bool IsGlobal { get; } 9 | 10 | bool IsDirectReference { get; } 11 | 12 | Identifier Identifier { get; } 13 | 14 | Pointer Pointer { get; } 15 | 16 | LanguageType Type { get; } 17 | 18 | Expression? Initializer { get; } 19 | 20 | bool ReadOnly { get; } 21 | 22 | public bool Constant { get; set; } 23 | 24 | IEnumerable References { get; } 25 | 26 | void AddReference(Identifier identifierExpression); 27 | 28 | void AddUsage(); 29 | 30 | void MarkUsed(); 31 | } 32 | 33 | public record Variable( 34 | Identifier Identifier, 35 | InstructionPointer Pointer, 36 | LanguageType Type, 37 | Expression? Initializer = null, 38 | bool IsGlobal = false, 39 | bool IsDirectReference = false) 40 | : IVariable 41 | { 42 | Pointer IVariable.Pointer => Pointer; 43 | 44 | public bool ReadOnly => false; 45 | 46 | public bool Constant { get; set; } = true; 47 | 48 | public int Usages { get; set; } 49 | 50 | public bool HasBeenUsed { get; set; } 51 | 52 | public bool CanBeRemoved => HasBeenUsed && Usages == 0; 53 | 54 | public List References { get; } = new(); 55 | 56 | IEnumerable IVariable.References => References; 57 | 58 | void IVariable.AddReference(Identifier identifierExpression) 59 | { 60 | References.Add(identifierExpression); 61 | } 62 | 63 | void IVariable.AddUsage() 64 | { 65 | Usages++; 66 | } 67 | 68 | void IVariable.MarkUsed() 69 | { 70 | HasBeenUsed = true; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Yabal.Compiler/Yabal/YabalParser.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime; 2 | 3 | namespace Yabal; 4 | 5 | public partial class YabalParser 6 | { 7 | protected bool lineTerminatorAhead() 8 | { 9 | // Get the token ahead of the current index. 10 | var possibleIndexEosToken = CurrentToken.TokenIndex - 1; 11 | var ahead = _input.Get(possibleIndexEosToken); 12 | 13 | if (ahead.Channel != Lexer.Hidden) 14 | { 15 | // We're only interested in tokens on the Hidden channel. 16 | return false; 17 | } 18 | 19 | if (ahead.Type is LineTerminator or AsmLineTerminator) 20 | { 21 | // There is definitely a line terminator ahead. 22 | return true; 23 | } 24 | 25 | if (ahead.Type is WhiteSpaces or AsmWhiteSpaces) 26 | { 27 | // Get the token ahead of the current whitespaces. 28 | possibleIndexEosToken = CurrentToken.TokenIndex - 2; 29 | ahead = _input.Get(possibleIndexEosToken); 30 | } 31 | 32 | // Get the token's text and type. 33 | string text = ahead.Text; 34 | int type = ahead.Type; 35 | 36 | // Check if the token is, or contains a line terminator. 37 | return (type is MultiLineComment or AsmMultiLineComment && (text.Contains('\r') || text.Contains('\n'))) || 38 | (type is LineTerminator or AsmLineTerminator); 39 | } 40 | 41 | protected bool noNewLine() 42 | { 43 | // Get the token ahead of the current index. 44 | var possibleIndexEosToken = CurrentToken.TokenIndex - 1; 45 | var ahead = _input.Get(possibleIndexEosToken); 46 | 47 | while (ahead.Channel == Lexer.Hidden) 48 | { 49 | if (ahead.Type is LineTerminator or AsmLineTerminator) 50 | { 51 | // There is definitely a line terminator ahead. 52 | return false; 53 | } 54 | 55 | ahead = _input.Get(--possibleIndexEosToken); 56 | } 57 | 58 | return true; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Yabal.Core/Character.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal; 2 | 3 | public class Character 4 | { 5 | public static IReadOnlyDictionary CharToInt { get; } 6 | 7 | public static IReadOnlyDictionary IntToChar { get; } 8 | 9 | static Character() 10 | { 11 | CharToInt = new Dictionary 12 | { 13 | [' '] = 0, 14 | ['+'] = 3, 15 | ['-'] = 4, 16 | ['*'] = 5, 17 | ['/'] = 6, 18 | ['_'] = 8, 19 | ['<'] = 9, 20 | ['>'] = 10, 21 | ['|'] = 11, 22 | ['A'] = 13, 23 | ['a'] = 13, 24 | ['B'] = 14, 25 | ['b'] = 14, 26 | ['C'] = 15, 27 | ['c'] = 15, 28 | ['D'] = 16, 29 | ['d'] = 16, 30 | ['E'] = 17, 31 | ['e'] = 17, 32 | ['F'] = 18, 33 | ['f'] = 18, 34 | ['G'] = 19, 35 | ['g'] = 19, 36 | ['H'] = 20, 37 | ['h'] = 20, 38 | ['I'] = 21, 39 | ['i'] = 21, 40 | ['J'] = 22, 41 | ['j'] = 22, 42 | ['K'] = 23, 43 | ['k'] = 23, 44 | ['L'] = 24, 45 | ['l'] = 24, 46 | ['M'] = 25, 47 | ['m'] = 25, 48 | ['N'] = 26, 49 | ['n'] = 26, 50 | ['O'] = 27, 51 | ['o'] = 27, 52 | ['P'] = 28, 53 | ['p'] = 28, 54 | ['Q'] = 29, 55 | ['q'] = 29, 56 | ['R'] = 30, 57 | ['r'] = 30, 58 | ['S'] = 31, 59 | ['s'] = 31, 60 | ['T'] = 32, 61 | ['t'] = 32, 62 | ['U'] = 33, 63 | ['u'] = 33, 64 | ['V'] = 34, 65 | ['v'] = 34, 66 | ['W'] = 35, 67 | ['w'] = 35, 68 | ['X'] = 36, 69 | ['x'] = 36, 70 | ['Y'] = 37, 71 | ['y'] = 37, 72 | ['Z'] = 38, 73 | ['z'] = 38, 74 | ['0'] = 39, 75 | ['1'] = 40, 76 | ['2'] = 41, 77 | ['3'] = 42, 78 | ['4'] = 43, 79 | ['5'] = 44, 80 | ['6'] = 45, 81 | ['7'] = 46, 82 | ['8'] = 47, 83 | ['9'] = 48, 84 | ['?'] = 49, 85 | ['!'] = 50, 86 | ['#'] = 51, 87 | ['$'] = 52, 88 | ['%'] = 53, 89 | ['.'] = 54, 90 | [','] = 55, 91 | [':'] = 56, 92 | [';'] = 57, 93 | ['('] = 58, 94 | [')'] = 59, 95 | ['['] = 60, 96 | [']'] = 61, 97 | ['{'] = 62, 98 | ['}'] = 63, 99 | ['"'] = 64, 100 | ['\''] = 65, 101 | ['^'] = 66, 102 | ['='] = 68 103 | }; 104 | 105 | IntToChar = CharToInt 106 | .GroupBy(kv => kv.Value) 107 | .ToDictionary(g => g.Key, g => g.First().Key); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Yabal.Core/DebugOffset.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal; 2 | 3 | 4 | public static class DebugOffset 5 | { 6 | public const int Flush = 0; 7 | public const int Line = 1; 8 | public const int Column = 2; 9 | public const int Size = 3; 10 | public const int Value = 4; 11 | } -------------------------------------------------------------------------------- /src/Yabal.Core/Either.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace Yabal; 4 | 5 | public readonly struct Either : IEquatable> 6 | where TLeft : notnull 7 | where TRight : notnull 8 | { 9 | private Either(TLeft left) 10 | { 11 | Left = left; 12 | Right = default; 13 | IsRight = false; 14 | } 15 | 16 | private Either(TRight right) 17 | { 18 | Left = default; 19 | Right = right; 20 | IsRight = true; 21 | } 22 | 23 | [MemberNotNullWhen(true, nameof(Right))] 24 | [MemberNotNullWhen(false, nameof(Left))] 25 | public bool IsRight { get; } 26 | 27 | public TRight? Right { get; } 28 | 29 | [MemberNotNullWhen(true, nameof(Left))] 30 | [MemberNotNullWhen(false, nameof(Right))] 31 | public bool IsLeft => !IsRight; 32 | 33 | public TLeft? Left { get; } 34 | 35 | public static implicit operator Either(TLeft left) => new(left); 36 | public static implicit operator Either(TRight right) => new(right); 37 | 38 | public override string? ToString() 39 | { 40 | return IsRight ? Right.ToString() : Left.ToString(); 41 | } 42 | 43 | public bool Equals(Either other) 44 | { 45 | return IsRight == other.IsRight && EqualityComparer.Default.Equals(Right, other.Right) && EqualityComparer.Default.Equals(Left, other.Left); 46 | } 47 | 48 | public override bool Equals(object? obj) 49 | { 50 | return obj is Either other && Equals(other); 51 | } 52 | 53 | public override int GetHashCode() 54 | { 55 | unchecked 56 | { 57 | var hashCode = IsRight.GetHashCode(); 58 | hashCode = (hashCode * 397) ^ (Right is null ? 0 : EqualityComparer.Default.GetHashCode(Right)); 59 | hashCode = (hashCode * 397) ^ (Left is null ? 0 : EqualityComparer.Default.GetHashCode(Left)); 60 | return hashCode; 61 | } 62 | } 63 | 64 | public static bool operator ==(Either left, Either right) 65 | { 66 | return left.Equals(right); 67 | } 68 | 69 | public static bool operator !=(Either left, Either right) 70 | { 71 | return !left.Equals(right); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Yabal.Core/HexFile.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Globalization; 3 | 4 | namespace Yabal; 5 | 6 | [SuppressMessage("ReSharper", "UseIndexFromEndExpression")] 7 | [SuppressMessage("ReSharper", "ReplaceSliceWithRangeIndexer")] 8 | public static class HexFile 9 | { 10 | public static void LoadFile(string path, int[] data, int offset = 0) 11 | { 12 | Load(File.ReadAllText(path), data, offset); 13 | } 14 | 15 | public static void Load(string str, int[] data, int offset = 0) 16 | { 17 | var span = str.AsSpan(); 18 | var validated = false; 19 | 20 | foreach (var rawLine in span.Split('\n')) 21 | { 22 | var line = rawLine; 23 | 24 | if (line.Length == 0) 25 | { 26 | continue; 27 | } 28 | 29 | if (line[line.Length - 1] == '\r') 30 | { 31 | line = line.Slice(0, line.Length - 1); 32 | } 33 | 34 | if (!validated) 35 | { 36 | validated = ValidateHeader(line); 37 | continue; 38 | } 39 | 40 | var start = line.IndexOf(' '); 41 | 42 | if (start == -1) 43 | { 44 | return; 45 | } 46 | 47 | for (var i = start + 1; i < line.Length;) 48 | { 49 | var value = line.Slice(i); 50 | var end = value.IndexOf(' '); 51 | 52 | if (end == -1) 53 | { 54 | end = value.Length; 55 | } 56 | else 57 | { 58 | end += 1; 59 | } 60 | 61 | i += end; 62 | 63 | #if NETSTANDARD2_0 64 | var intValue = value.Slice(0, end).ToString(); 65 | #else 66 | var intValue = value[..end]; 67 | #endif 68 | 69 | if (int.TryParse(intValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var instruction)) 70 | { 71 | data[offset++] = instruction; 72 | } 73 | } 74 | } 75 | } 76 | 77 | private static bool ValidateHeader(ReadOnlySpan line) 78 | { 79 | line = line.Trim(); 80 | 81 | if (line.Length == 0) 82 | { 83 | return false; 84 | } 85 | 86 | if (line.SequenceEqual("v3.0 hex words addressed".AsSpan())) 87 | { 88 | return true; 89 | } 90 | 91 | throw new FileLoadException("Invalid file format"); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Yabal.Core/IProgram.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal; 2 | 3 | public interface IProgram 4 | { 5 | void CopyTo(int[] array); 6 | } 7 | -------------------------------------------------------------------------------- /src/Yabal.Core/Instructions/InstructionReference.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Instructions; 2 | 3 | public readonly struct InstructionReference 4 | { 5 | public const int MaxId = 0b111111; 6 | public const int MaxData = 0b1111111111; 7 | 8 | public readonly int Id; 9 | public readonly int Data; 10 | public readonly int Raw; 11 | 12 | public InstructionReference(int raw) 13 | { 14 | Raw = raw; 15 | Id = raw >> 11; 16 | Data = raw & MaxData; 17 | } 18 | 19 | public InstructionReference(int id, int data) 20 | { 21 | Id = id; 22 | Data = data; 23 | Raw = ((id & MaxId) << 11) | (data & MaxData); 24 | } 25 | 26 | public Instruction Instruction => Instruction.Default[Id]; 27 | 28 | public override string ToString() 29 | { 30 | return ToString(true); 31 | } 32 | 33 | public string ToString(bool withData) 34 | { 35 | var instructions = Instruction.Default; 36 | 37 | if (Id >= instructions.Count) 38 | { 39 | return Raw.ToString(); 40 | } 41 | 42 | var instruction = instructions[Id]; 43 | 44 | if (!withData || !instruction.MicroInstructions.Any(i => i.IsIR)) 45 | { 46 | return instruction.Name; 47 | } 48 | 49 | return $"{instruction.Name} {Data}"; 50 | } 51 | 52 | public static InstructionReference Create(int id, int data = 0) 53 | { 54 | return new InstructionReference(id, data); 55 | } 56 | 57 | public static implicit operator int(InstructionReference value) => value.Raw; 58 | 59 | public InstructionReference WithData(int value) 60 | { 61 | return new InstructionReference(Id, value); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Yabal.Core/Yabal.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | enable 6 | enable 7 | Yabal 8 | preview 9 | embedded 10 | 11 | 12 | 13 | true 14 | 15 | 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Yabal.Desktop/Config/AddressConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | using Yabal.Devices; 5 | using Yabal.Devices; 6 | 7 | namespace Yabal; 8 | 9 | public class AddressConverter : JsonConverter
10 | { 11 | public override Address Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 12 | { 13 | if (reader.TokenType == JsonTokenType.String) 14 | { 15 | var stringValue = reader.GetString()!.AsSpan(); 16 | 17 | if (!stringValue.Contains(':')) 18 | { 19 | return new Address(0, IntJsonConverter.ParseInt(stringValue)); 20 | } 21 | 22 | var left = stringValue.Slice(0, stringValue.IndexOf(':')); 23 | var right = stringValue.Slice(stringValue.IndexOf(':') + 1); 24 | 25 | return new Address( 26 | IntJsonConverter.ParseInt(left), 27 | IntJsonConverter.ParseInt(right) 28 | ); 29 | } 30 | 31 | if (reader.TokenType == JsonTokenType.StartArray) 32 | { 33 | reader.Read(); 34 | 35 | var left = IntJsonConverter.ParseInt(ref reader); 36 | 37 | reader.Read(); 38 | 39 | if (reader.TokenType == JsonTokenType.EndArray) 40 | { 41 | return new Address(0, left); 42 | } 43 | 44 | var right = IntJsonConverter.ParseInt(ref reader); 45 | 46 | reader.Read(); 47 | 48 | if (reader.TokenType != JsonTokenType.EndArray) 49 | { 50 | throw new JsonException(); 51 | } 52 | 53 | return new Address(left, right); 54 | } 55 | 56 | if (reader.TokenType == JsonTokenType.Number) 57 | { 58 | return new Address(0, reader.GetInt32()); 59 | } 60 | 61 | throw new JsonException(); 62 | } 63 | 64 | public override void Write(Utf8JsonWriter writer, Address value, JsonSerializerOptions options) 65 | { 66 | writer.WriteStringValue($"{value.Bank}:0x{value.Offset:X4}"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Yabal.Desktop/Config/ConfigContext.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Serialization; 3 | using Yabal; 4 | 5 | namespace Yabal; 6 | 7 | [JsonSerializable(typeof(Config))] 8 | internal partial class ConfigContext : JsonSerializerContext 9 | { 10 | public static Config Load(string? directory) 11 | { 12 | Config? config = null; 13 | 14 | var path = "config.jsonc"; 15 | 16 | if (directory != null) 17 | { 18 | path = Path.Combine(directory, path); 19 | } 20 | 21 | if (File.Exists(path)) 22 | { 23 | var json = File.ReadAllText(path); 24 | 25 | var context = new ConfigContext( 26 | new JsonSerializerOptions 27 | { 28 | ReadCommentHandling = JsonCommentHandling.Skip, 29 | AllowTrailingCommas = true, 30 | Converters = 31 | { 32 | new IntJsonConverter(), 33 | new AddressConverter() 34 | } 35 | } 36 | ); 37 | 38 | try 39 | { 40 | config = JsonSerializer.Deserialize(json, context.Config); 41 | } 42 | catch(Exception e) 43 | { 44 | Console.WriteLine($"Failed to parse config file: {path}: {e.Message}"); 45 | config = null; 46 | } 47 | } 48 | 49 | return config ?? new Config(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Yabal.Desktop/Config/IntJsonConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | 5 | namespace Yabal; 6 | 7 | public class IntJsonConverter : JsonConverter 8 | { 9 | public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 10 | { 11 | return ParseInt(ref reader); 12 | } 13 | 14 | public static int ParseInt(ref Utf8JsonReader reader) 15 | { 16 | if (reader.TokenType == JsonTokenType.String) 17 | { 18 | var span = reader.GetString()!.AsSpan(); 19 | return ParseInt(span); 20 | } 21 | 22 | if (reader.TokenType == JsonTokenType.Number) 23 | { 24 | return reader.GetInt32(); 25 | } 26 | 27 | throw new JsonException(); 28 | } 29 | 30 | public static int ParseInt(ReadOnlySpan span) 31 | { 32 | if (!span.Contains('_')) 33 | { 34 | return ParseIntCore(span); 35 | } 36 | 37 | Span value = stackalloc char[span.Length]; 38 | 39 | var offset = 0; 40 | foreach (var c in span) 41 | { 42 | if (c != '_') 43 | { 44 | value[offset++] = c; 45 | } 46 | } 47 | 48 | return ParseIntCore(value); 49 | } 50 | 51 | private static int ParseIntCore(ReadOnlySpan span) 52 | { 53 | if (span.Length > 2 && span[0] == '0' && (span[1] is 'X' or 'x')) 54 | { 55 | return int.Parse(span[2..], NumberStyles.HexNumber); 56 | } 57 | 58 | if (span.Length > 2 && span[0] == '0' && (span[1] is 'B' or 'b')) 59 | { 60 | return Convert.ToInt32(span[2..].ToString(), 2); 61 | } 62 | 63 | return int.Parse(span); 64 | } 65 | 66 | public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options) 67 | { 68 | writer.WriteNumberValue(value); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Yabal.Desktop/Devices/Keyboard.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Devices; 2 | 3 | public static class Keyboard 4 | { 5 | public static readonly IReadOnlyDictionary Table = new Dictionary 6 | { 7 | // Special characters 8 | [44] = 0, // space -> blank 9 | [58] = 1, // f1 -> smaller solid square 10 | [59] = 2, // f2 -> full solid square 11 | [87] = 3, // num+ -> + 12 | [86] = 4, // num- -> - 13 | [85] = 5, // num* -> * 14 | [84] = 6, // num/ -> / 15 | [60] = 7, // f3 -> full hollow square 16 | [45] = 8, // _ -> _ 17 | [80] = 9, // l-arr -> < 18 | [79] = 10, // r-arr -> > 19 | [82] = 71, // u-arr -> u-arr 20 | [81] = 72, // d-arr -> d-arr 21 | [49] = 11, // | -> vertical line | 22 | [66] = 12, // f9 -> horizontal line -- 23 | 24 | // Letters 25 | [4] = 13, // a -> a 26 | [5] = 14, // b -> b 27 | [6] = 15, // c -> c 28 | [7] = 16, // d -> d 29 | [8] = 17, // e -> e 30 | [9] = 18, // f -> f 31 | [10] = 19, // g -> g 32 | [11] = 20, // h -> h 33 | [12] = 21, // i -> i 34 | [13] = 22, // j -> j 35 | [14] = 23, // k -> k 36 | [15] = 24, // l -> l 37 | [16] = 25, // m -> m 38 | [17] = 26, // n -> n 39 | [18] = 27, // o -> o 40 | [19] = 28, // p -> p 41 | [20] = 29, // q -> q 42 | [21] = 30, // r -> r 43 | [22] = 31, // s -> s 44 | [23] = 32, // t -> t 45 | [24] = 33, // u -> u 46 | [25] = 34, // v -> v 47 | [26] = 35, // w -> w 48 | [27] = 36, // x -> x 49 | [28] = 37, // y -> y 50 | [29] = 38, // z -> z 51 | 52 | // Numbers 53 | [39] = 39, // 0 -> 0 54 | [30] = 40, // 1 -> 1 55 | [31] = 41, // 2 -> 2 56 | [32] = 42, // 3 -> 3 57 | [33] = 43, // 4 -> 4 58 | [34] = 44, // 5 -> 5 59 | [35] = 45, // 6 -> 6 60 | [36] = 46, // 7 -> 7 61 | [37] = 47, // 8 -> 8 62 | [38] = 48, // 9 -> 9 63 | 64 | [42] = 70, // backspace -> backspace 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /src/Yabal.Desktop/Utils/AliasAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Utils; 2 | 3 | public class AliasAttribute : Attribute 4 | { 5 | public AliasAttribute(params string[] aliases) 6 | { 7 | Aliases = aliases; 8 | } 9 | 10 | public string[] Aliases { get; } 11 | } 12 | -------------------------------------------------------------------------------- /src/Yabal.Desktop/Utils/EnumHelper.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine.Parsing; 2 | using System.Reflection; 3 | 4 | namespace Yabal.Utils; 5 | 6 | public static class EnumHelper 7 | { 8 | public static IEnumerable GetAttributes(this Enum enumVal) where T : Attribute 9 | { 10 | var type = enumVal.GetType(); 11 | var memInfo = type.GetMember(enumVal.ToString()); 12 | return memInfo[0].GetCustomAttributes(false); 13 | } 14 | 15 | public static List? ParseEnum(ArgumentResult result) where T : struct, Enum 16 | { 17 | if (result.Tokens.Count == 0) 18 | { 19 | return null; 20 | } 21 | 22 | var names = new Dictionary(StringComparer.OrdinalIgnoreCase); 23 | 24 | foreach (var value in Enum.GetValues()) 25 | { 26 | names[value.ToString()] = value; 27 | 28 | foreach (var alias in value.GetAttributes().SelectMany(i => i.Aliases)) 29 | { 30 | names[alias] = value; 31 | } 32 | } 33 | 34 | var values = new List(); 35 | var splitChars = new[] {',', ' '}; 36 | 37 | foreach (var token in result.Tokens) 38 | { 39 | T outputFormat; 40 | 41 | var tokenValue = token.Value; 42 | 43 | if (tokenValue.IndexOfAny(splitChars) != -1) 44 | { 45 | foreach (var value in tokenValue.Split(splitChars)) 46 | { 47 | if (names.TryGetValue(value, out outputFormat)) 48 | { 49 | values.Add(outputFormat); 50 | } 51 | else 52 | { 53 | result.ErrorMessage = $"Unknown value '{value}'"; 54 | return null; 55 | } 56 | } 57 | 58 | continue; 59 | } 60 | 61 | if (names.TryGetValue(tokenValue, out outputFormat)) 62 | { 63 | values.Add(outputFormat); 64 | continue; 65 | } 66 | 67 | foreach (var c in tokenValue) 68 | { 69 | if (names.TryGetValue(c.ToString(), out outputFormat)) 70 | { 71 | values.Add(outputFormat); 72 | } 73 | else 74 | { 75 | result.ErrorMessage = $"Unknown value '{tokenValue}'"; 76 | return null; 77 | } 78 | } 79 | } 80 | 81 | return values; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Yabal.Desktop/Utils/OutputFormat.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Utils; 2 | 3 | public enum OutputFormat 4 | { 5 | [Alias("a", "asm")] Assembly, 6 | [Alias("c", "asmc")] AssemblyWithComments, 7 | [Alias("h", "aexe")] AstroExecutable, 8 | [Alias("l", "hex")] Logisim 9 | } 10 | -------------------------------------------------------------------------------- /src/Yabal.Desktop/Yabal.Desktop.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /src/Yabal.Desktop/config.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "Cpu": { 3 | // Duration of one cycle in ticks. 4 | // Lower values will result in faster execution. 5 | "CycleDuration": 300, 6 | 7 | // Amount of instructions that should be executed per cycle. 8 | // Higher values will result in faster execution, since the CPU doesn't have to fetch the registers from the heap every time. 9 | "InstructionsPerCycle": 100 10 | }, 11 | 12 | "Program": { 13 | "Path": "program_machine_code", 14 | "Size": "0xEF6E" 15 | }, 16 | 17 | "Screen": { 18 | // Note: changing the resolution will also resize the memory allocation of the screen and character device. 19 | "Width": 108, 20 | "Height": 108, 21 | "Scale": 9 22 | }, 23 | 24 | "Memory": { 25 | // Max amount of memory in bytes. 26 | "Size": "0xFFFF", 27 | "Banks": 10, 28 | 29 | // Mapping of device in memory. 30 | "Devices": { 31 | "Program": [0, "0x0000"], 32 | "Character": [1, "0xD12A"], 33 | "Keyboard": [1, "0xD0FC"], 34 | "Mouse": [1, "0xD0FD"], 35 | "Screen": [1, "0xD26E"], 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Yabal.Desktop/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YabalLang/compiler/659a278a29007ec65f00449a63f5fa09e95c663d/src/Yabal.Desktop/icon.ico -------------------------------------------------------------------------------- /src/Yabal.Desktop/native/win-x64/SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YabalLang/compiler/659a278a29007ec65f00449a63f5fa09e95c663d/src/Yabal.Desktop/native/win-x64/SDL2.dll -------------------------------------------------------------------------------- /src/Yabal.Emulator/Config/Config.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal; 2 | 3 | public class ProgramConfig 4 | { 5 | public string Path { get; set; } = "program_machine_code"; 6 | 7 | public int Size { get; set; } = 0xEF6E; 8 | } 9 | 10 | public class Config 11 | { 12 | public CpuConfig Cpu { get; set; } = new(); 13 | 14 | public ProgramConfig Program { get; set; } = new(); 15 | 16 | public ScreenConfig Screen { get; set; } = new(); 17 | 18 | public MemoryConfig Memory { get; set; } = new(); 19 | } 20 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Config/CpuConfig.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal; 2 | 3 | public class CpuConfig 4 | { 5 | public int CycleDuration { get; set; } = 5; 6 | 7 | public int InstructionsPerCycle { get; set; } = 100; 8 | } 9 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Config/MemoryConfig.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal; 2 | 3 | public class MemoryConfig 4 | { 5 | public int Size { get; set; } = 0xFFFF; 6 | 7 | public int Banks { get; set; } = 10; 8 | 9 | public MemoryDeviceConfig Devices { get; set; } = new(); 10 | } 11 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Config/MemoryDeviceConfig.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Devices; 2 | 3 | namespace Yabal; 4 | 5 | public class MemoryDeviceConfig 6 | { 7 | public Address Screen { get; set; } = new(1, 0xD26E); 8 | 9 | public Address Character { get; set; } = new(1, 0xD12A); 10 | 11 | public Address Program { get; set; } = new(0, 0x0000); 12 | 13 | public Address Keyboard { get; set; } = new(1, 0xD0FC); 14 | 15 | public Address Mouse { get; set; } = new(1, 0xD0FD); 16 | } 17 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Config/ScreenConfig.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal; 2 | 3 | public class ScreenConfig 4 | { 5 | public int Scale { get; set; } = 4; 6 | 7 | public int Width { get; set; } = 108; 8 | 9 | public int Height { get; set; } = 108; 10 | } 11 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Devices/Address.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Devices; 2 | 3 | public readonly record struct Address(int Bank, int Offset); 4 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Devices/CpuContext.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Devices; 2 | 3 | public readonly record struct CpuContext(int A, int B, int C, int Bus, int MemoryIndex, bool FlagA, bool FlagB, int ProgramCounter, int Bank) 4 | { 5 | public void Save(BinaryWriter writer) 6 | { 7 | writer.Write(A); 8 | writer.Write(B); 9 | writer.Write(C); 10 | writer.Write(Bus); 11 | writer.Write(MemoryIndex); 12 | writer.Write(FlagA); 13 | writer.Write(FlagB); 14 | writer.Write(ProgramCounter); 15 | writer.Write(Bank); 16 | } 17 | 18 | public static CpuContext Load(BinaryReader reader) 19 | { 20 | return new CpuContext 21 | ( 22 | reader.ReadInt32(), 23 | reader.ReadInt32(), 24 | reader.ReadInt32(), 25 | reader.ReadInt32(), 26 | reader.ReadInt32(), 27 | reader.ReadBoolean(), 28 | reader.ReadBoolean(), 29 | reader.ReadInt32(), 30 | reader.ReadInt32() 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Devices/Memory/DebugDevice.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Devices; 2 | 3 | public sealed class DebugDevice : MemoryDevice 4 | where THandler : Handler 5 | { 6 | private readonly THandler _handler; 7 | private readonly int[] _buffer = new int[16]; 8 | private int _line; 9 | private int _column; 10 | private int _size; 11 | 12 | public DebugDevice(int bank, int address, THandler handler) 13 | : base(address, length: 16) 14 | { 15 | _handler = handler; 16 | Bank = bank; 17 | } 18 | 19 | public int Bank { get; set; } 20 | 21 | public override void Write(int address, int value) 22 | { 23 | switch (address) 24 | { 25 | case DebugOffset.Flush: 26 | _handler.ShowVariable(_line, _column, _buffer.AsSpan(0, _size)); 27 | break; 28 | case DebugOffset.Line: 29 | _line = value; 30 | break; 31 | case DebugOffset.Column: 32 | _column = value; 33 | break; 34 | case DebugOffset.Size: 35 | _size = value; 36 | break; 37 | default: 38 | _buffer[address - DebugOffset.Value] = value; 39 | break; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Devices/Memory/MemoryDevice.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Devices; 2 | 3 | public abstract class MemoryDevice 4 | { 5 | protected MemoryDevice(int address, int length) 6 | { 7 | Length = length; 8 | Address = address; 9 | } 10 | 11 | public int Address { get; } 12 | 13 | public int Length { get; } 14 | 15 | public virtual void Initialize(Span span, bool isState) 16 | { 17 | } 18 | 19 | public virtual void Write(int address, int value) 20 | { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Devices/Memory/Screen.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Devices; 2 | 3 | public sealed class Screen : MemoryDevice 4 | where THandler : Handler 5 | { 6 | private readonly THandler _handler; 7 | private readonly int[] _pixels; 8 | private readonly int[] _overlay; 9 | 10 | public Screen(int bank, int address, THandler handler, int width, int height) 11 | : base(address, width * height) 12 | { 13 | _handler = handler; 14 | Bank = bank; 15 | Width = width; 16 | Height = height; 17 | _pixels = new int[width * height]; 18 | _overlay = new int[width * height]; 19 | } 20 | 21 | public int Bank { get; } 22 | 23 | public int Width { get; } 24 | 25 | public int Height { get; } 26 | 27 | private void UpdatePixel(int address) 28 | { 29 | var color = _overlay[address]; 30 | 31 | if (color == 0) 32 | { 33 | color = _pixels[address]; 34 | } 35 | 36 | _handler.SetPixel(address, color); 37 | } 38 | 39 | public override void Initialize(Span span, bool isState) 40 | { 41 | for (var i = 0; i < span.Length; i++) 42 | { 43 | _handler.SetPixel(i, span[i]); 44 | } 45 | } 46 | 47 | public override void Write(int address, int value) 48 | { 49 | if (_pixels[address] == value) 50 | { 51 | return; 52 | } 53 | 54 | _pixels[address] = value; 55 | UpdatePixel(address); 56 | } 57 | 58 | public void WriteOverlay(int address, int value) 59 | { 60 | if (address < 0 || address >= _overlay.Length) 61 | { 62 | return; 63 | } 64 | 65 | if (_overlay[address] == value) 66 | { 67 | return; 68 | } 69 | 70 | _overlay[address] = value; 71 | UpdatePixel(address); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Devices/ScreenColor.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Devices; 2 | 3 | public readonly record struct ScreenColor(int Value) 4 | { 5 | public static readonly ScreenColor White = new(0xFFFF); 6 | public static readonly ScreenColor Black = new(0x0000); 7 | 8 | public int R => ((Value >> 10) & 0b11111) * 8; 9 | 10 | public int G => ((Value >> 5) & 0b11111) * 8; 11 | 12 | public int B => (Value & 0b11111) * 8; 13 | 14 | public int A => 255; 15 | 16 | public int ARGB => (R << 24 | G << 16 | B << 8 | A); 17 | 18 | public static implicit operator ScreenColor(int value) => new(value); 19 | } 20 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Handler.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Devices; 2 | 3 | namespace Yabal; 4 | 5 | public abstract class Handler 6 | { 7 | public abstract void SetPixel(int address, ScreenColor color); 8 | 9 | public abstract void LogSpeed(int steps, float value); 10 | 11 | public abstract void FlushScreen(); 12 | 13 | public abstract void Halt(); 14 | 15 | public abstract void ShowVariable(int line, int offset, Span value); 16 | } 17 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Yabal.Emulator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Yabal 9 | preview 10 | embedded 11 | true 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Yabal.Emulator/Yabal.Emulator.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True 6 | True 7 | True -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Document.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Yabal.Instructions; 6 | using OmniSharp.Extensions.LanguageServer.Protocol; 7 | using OmniSharp.Extensions.LanguageServer.Protocol.Document; 8 | using OmniSharp.Extensions.LanguageServer.Protocol.Models; 9 | using OmniSharp.Extensions.LanguageServer.Protocol.Server; 10 | using Serilog; 11 | using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; 12 | 13 | namespace Yabal.LanguageServer; 14 | 15 | public class Document(DocumentUri uri, ILanguageServerFacade languageServer, TextDocumentContainer documentContainer) 16 | { 17 | private readonly TextDocumentContainer _documentContainer = documentContainer; 18 | private readonly string _uriString = uri.ToString(); 19 | 20 | public YabalBuilder Builder { get; set; } 21 | 22 | public DocumentUri Uri { get; } = uri; 23 | 24 | public int? Version { get; set; } 25 | 26 | public string Text { get; set; } 27 | 28 | public async Task UpdateAsync(int? version, string text) 29 | { 30 | if (Version >= version) 31 | { 32 | return; 33 | } 34 | 35 | Version = version; 36 | Text = text; 37 | 38 | try 39 | { 40 | var builder = new YabalBuilder(); 41 | await builder.CompileCodeAsync(text); 42 | Builder = builder; 43 | 44 | var diagnostics = new List(); 45 | 46 | foreach (var (range, level, message) in Builder.Errors.SelectMany(i => i.Value)) 47 | { 48 | diagnostics.Add(new Diagnostic 49 | { 50 | Message = message, 51 | Range = new Range(new Position(range.StartLine - 1, range.StartColumn), new Position(range.EndLine - 1, range.EndColumn)), 52 | Severity = level switch 53 | { 54 | ErrorLevel.Error => DiagnosticSeverity.Error, 55 | ErrorLevel.Warning => DiagnosticSeverity.Warning, 56 | _ => DiagnosticSeverity.Information 57 | }, 58 | }); 59 | } 60 | 61 | builder.Build(); 62 | 63 | languageServer.TextDocument.PublishDiagnostics(new PublishDiagnosticsParams 64 | { 65 | Uri = Uri, 66 | Diagnostics = diagnostics 67 | }); 68 | } 69 | catch (Exception e) 70 | { 71 | Log.Error(e, "Error compiling code"); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Extensions/RangeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using OmniSharp.Extensions.LanguageServer.Protocol.Models; 4 | using Yabal.Ast; 5 | using Yabal.Visitor; 6 | 7 | namespace Yabal.LanguageServer; 8 | 9 | public static class RangeExtensions 10 | { 11 | public static Range ToRange(this SourceRange sourceRange) 12 | { 13 | return new Range( 14 | sourceRange.StartLine - 1, 15 | sourceRange.StartColumn, 16 | sourceRange.EndLine - 1, 17 | sourceRange.EndColumn 18 | ); 19 | } 20 | 21 | public static bool IsInRange(this SourceRange sourceRange, Position position) 22 | { 23 | return sourceRange.IsInRange(position.Line + 1, position.Character); 24 | } 25 | 26 | public static (Identifier?, Variable?) Find(this IEnumerable variables, Position position) 27 | { 28 | foreach (var variable in variables) 29 | { 30 | if (variable.Identifier.Range.IsInRange(position)) 31 | { 32 | return (variable.Identifier, variable); 33 | } 34 | 35 | foreach (var identifier in variable.References) 36 | { 37 | if (identifier.Range.IsInRange(position)) 38 | { 39 | return (identifier, variable); 40 | } 41 | } 42 | } 43 | 44 | return default; 45 | } 46 | 47 | public static (Identifier?, Function?) Find(this IEnumerable functions, Position position) 48 | { 49 | foreach (var function in functions) 50 | { 51 | if (function.Name is not FunctionIdentifier { Identifier: {} name }) 52 | { 53 | continue; 54 | } 55 | 56 | if (name.Range.IsInRange(position)) 57 | { 58 | return (name, function); 59 | } 60 | 61 | foreach (var identifier in function.References) 62 | { 63 | if (identifier.Range.IsInRange(position)) 64 | { 65 | return (identifier, function); 66 | } 67 | } 68 | } 69 | 70 | return default; 71 | } 72 | 73 | public static (Identifier?, IVariable?) Find(this YabalBuilder builder, Position position) 74 | { 75 | (var identifier, IVariable? variable) = builder.Variables.Find(position); 76 | 77 | if (identifier != null && variable != null) 78 | { 79 | return (identifier, variable); 80 | } 81 | 82 | (identifier, variable) = builder.Functions.Find(position); 83 | 84 | return (identifier, variable); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Handlers/DefinitionHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; 5 | using OmniSharp.Extensions.LanguageServer.Protocol.Document; 6 | using OmniSharp.Extensions.LanguageServer.Protocol.Models; 7 | 8 | namespace Yabal.LanguageServer.Handlers; 9 | 10 | public class DefinitionHandler(TextDocumentContainer documentContainer) : IDefinitionHandler 11 | { 12 | public Task Handle(DefinitionParams request, CancellationToken cancellationToken) 13 | { 14 | if (!documentContainer.Documents.TryGetValue(request.TextDocument.Uri, out var document)) 15 | { 16 | return Task.FromResult(null); 17 | } 18 | 19 | var (_, variable) = document.Builder.Find(request.Position); 20 | 21 | if (variable == null) 22 | { 23 | return Task.FromResult(null); 24 | } 25 | 26 | return Task.FromResult(new LocationOrLocationLinks(new LocationOrLocationLink(new Location 27 | { 28 | Uri = request.TextDocument.Uri, 29 | Range = variable.Identifier.Range.ToRange() 30 | }))); 31 | } 32 | 33 | public DefinitionRegistrationOptions GetRegistrationOptions(DefinitionCapability capability, 34 | ClientCapabilities clientCapabilities) 35 | { 36 | return new DefinitionRegistrationOptions 37 | { 38 | DocumentSelector = TextDocumentSelector.ForLanguage("yabal") 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Handlers/HighlightHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; 6 | using OmniSharp.Extensions.LanguageServer.Protocol.Document; 7 | using OmniSharp.Extensions.LanguageServer.Protocol.Models; 8 | 9 | namespace Yabal.LanguageServer.Handlers; 10 | 11 | public class HighlightHandler(TextDocumentContainer documentContainer) : IDocumentHighlightHandler 12 | { 13 | public Task Handle(DocumentHighlightParams request, CancellationToken cancellationToken) 14 | { 15 | var items = new List(); 16 | 17 | if (documentContainer.Documents.TryGetValue(request.TextDocument.Uri, out var document)) 18 | { 19 | AddHighlights(request, document, items); 20 | } 21 | 22 | return Task.FromResult(items); 23 | } 24 | 25 | private static void AddHighlights(TextDocumentPositionParams request, Document document, List items) 26 | { 27 | var (_, variable) = document.Builder.Find(request.Position); 28 | 29 | if (variable == null) return; 30 | 31 | items.Add(new DocumentHighlight 32 | { 33 | Kind = DocumentHighlightKind.Write, 34 | Range = variable.Identifier.Range.ToRange() 35 | }); 36 | 37 | items.AddRange(variable.References.Select(i => new DocumentHighlight 38 | { 39 | Kind = DocumentHighlightKind.Read, 40 | Range = i.Range.ToRange() 41 | })); 42 | } 43 | 44 | public DocumentHighlightRegistrationOptions GetRegistrationOptions(DocumentHighlightCapability capability, 45 | ClientCapabilities clientCapabilities) 46 | { 47 | return new DocumentHighlightRegistrationOptions 48 | { 49 | DocumentSelector = TextDocumentSelector.ForLanguage("yabal") 50 | }; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Handlers/HoverHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; 5 | using OmniSharp.Extensions.LanguageServer.Protocol.Document; 6 | using OmniSharp.Extensions.LanguageServer.Protocol.Models; 7 | using Yabal.Ast; 8 | using Yabal.Instructions; 9 | 10 | namespace Yabal.LanguageServer.Handlers; 11 | 12 | public class HoverHandler(TextDocumentContainer documentContainer) : IHoverHandler 13 | { 14 | public Task Handle(HoverParams request, CancellationToken cancellationToken) 15 | { 16 | if (!documentContainer.Documents.TryGetValue(request.TextDocument.Uri, out var document)) 17 | { 18 | return Task.FromResult(null); 19 | } 20 | 21 | var (identifier, variable) = document.Builder.Find(request.Position); 22 | 23 | if (identifier == null || variable == null) 24 | { 25 | return Task.FromResult(null); 26 | } 27 | 28 | var sb = new StringBuilder(); 29 | var size = ((variable.Initializer as IConstantValue)?.Value as IAddress)?.Length ?? variable.Type.Size; 30 | 31 | sb.AppendLine($"Type: {variable.Type} "); 32 | sb.AppendLine($"Size: {size} "); 33 | 34 | if (variable.Pointer is not InstructionLabel) 35 | { 36 | sb.AppendLine($"Variable address: {variable.Pointer.Address} "); 37 | } 38 | 39 | if (variable.Initializer is IPointerSource { Pointer: {} pointer }) 40 | { 41 | sb.AppendLine($"Pointer address: {pointer.Address} "); 42 | sb.AppendLine($"Pointer bank: {pointer.Bank} "); 43 | } 44 | else if (variable.Initializer is IConstantValue {Value: { } value}) 45 | { 46 | sb.AppendLine($"Value: {value} "); 47 | } 48 | 49 | return Task.FromResult(new Hover 50 | { 51 | Contents = new MarkedStringsOrMarkupContent(new MarkupContent 52 | { 53 | Kind = MarkupKind.Markdown, 54 | Value = sb.ToString() 55 | }), 56 | Range = identifier.Range.ToRange() 57 | }); 58 | } 59 | 60 | public HoverRegistrationOptions GetRegistrationOptions(HoverCapability capability, ClientCapabilities clientCapabilities) 61 | { 62 | return new HoverRegistrationOptions 63 | { 64 | DocumentSelector = TextDocumentSelector.ForLanguage("yabal") 65 | }; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Handlers/RenameHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using OmniSharp.Extensions.LanguageServer.Protocol; 7 | using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; 8 | using OmniSharp.Extensions.LanguageServer.Protocol.Document; 9 | using OmniSharp.Extensions.LanguageServer.Protocol.Models; 10 | 11 | namespace Yabal.LanguageServer.Handlers; 12 | 13 | public class RenameHandler(TextDocumentContainer documentContainer) : IRenameHandler 14 | { 15 | public Task Handle(RenameParams request, CancellationToken cancellationToken) 16 | { 17 | if (!documentContainer.Documents.TryGetValue(request.TextDocument.Uri, out var document)) 18 | { 19 | return Task.FromResult(null); 20 | } 21 | 22 | var (_, variable) = document.Builder.Find(request.Position); 23 | 24 | if (variable == null) 25 | { 26 | return Task.FromResult(null); 27 | } 28 | 29 | return Task.FromResult(new WorkspaceEdit 30 | { 31 | Changes = new Dictionary> 32 | { 33 | [request.TextDocument.Uri] = 34 | [ 35 | ..variable.References 36 | .OrderByDescending(i => i.Range) 37 | .DistinctBy(i => i.Range.Index) 38 | .Select(i => new TextEdit 39 | { 40 | Range = i.Range.ToRange(), 41 | NewText = request.NewName 42 | }), 43 | new TextEdit 44 | { 45 | Range = variable.Identifier.Range.ToRange(), 46 | NewText = request.NewName 47 | } 48 | ] 49 | } 50 | }); 51 | } 52 | 53 | public RenameRegistrationOptions GetRegistrationOptions(RenameCapability capability, ClientCapabilities clientCapabilities) 54 | { 55 | return new RenameRegistrationOptions 56 | { 57 | DocumentSelector = TextDocumentSelector.ForLanguage("yabal") 58 | }; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Handlers/TextDocumentSyncHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using MediatR; 5 | using OmniSharp.Extensions.LanguageServer.Protocol; 6 | using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; 7 | using OmniSharp.Extensions.LanguageServer.Protocol.Document; 8 | using OmniSharp.Extensions.LanguageServer.Protocol.Models; 9 | using OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities; 10 | 11 | namespace Yabal.LanguageServer.Handlers; 12 | 13 | internal class TextDocumentHandler(TextDocumentContainer documentContainer) : TextDocumentSyncHandlerBase 14 | { 15 | public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri) 16 | { 17 | return new TextDocumentAttributes(uri, "yabal"); 18 | } 19 | 20 | public override async Task Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken) 21 | { 22 | await documentContainer.Update( 23 | request.TextDocument.Uri, 24 | request.TextDocument.Version, 25 | request.TextDocument.Text 26 | ); 27 | 28 | return Unit.Value; 29 | } 30 | 31 | public override async Task Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken) 32 | { 33 | var text = request.ContentChanges.FirstOrDefault()?.Text; 34 | 35 | if (text == null) 36 | { 37 | return Unit.Value; 38 | } 39 | 40 | await documentContainer.Update( 41 | request.TextDocument.Uri, 42 | request.TextDocument.Version, 43 | text 44 | ); 45 | 46 | return Unit.Value; 47 | } 48 | 49 | public override Task Handle(DidSaveTextDocumentParams request, CancellationToken cancellationToken) 50 | { 51 | return Unit.Task; 52 | } 53 | 54 | public override Task Handle(DidCloseTextDocumentParams request, CancellationToken cancellationToken) 55 | { 56 | return Unit.Task; 57 | } 58 | 59 | protected override TextDocumentSyncRegistrationOptions CreateRegistrationOptions( 60 | TextSynchronizationCapability capability, 61 | ClientCapabilities clientCapabilities) 62 | { 63 | return new TextDocumentSyncRegistrationOptions 64 | { 65 | Change = TextDocumentSyncKind.Full, 66 | DocumentSelector = TextDocumentSelector.ForLanguage("yabal"), 67 | Save = new SaveOptions { IncludeText = true } 68 | }; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Logging/LanguageServerSink.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using OmniSharp.Extensions.LanguageServer.Protocol.Server; 3 | using Serilog; 4 | using Serilog.Configuration; 5 | using Serilog.Core; 6 | using Serilog.Events; 7 | 8 | namespace Yabal.LanguageServer.Logging; 9 | 10 | public class LanguageServerSink(IFormatProvider? formatProvider, ILanguageServerFacade server) 11 | : ILogEventSink 12 | { 13 | public void Emit(LogEvent logEvent) 14 | { 15 | var message = logEvent.RenderMessage(formatProvider); 16 | server.SendNotification("yabal/log", message); 17 | } 18 | } 19 | 20 | public static class LanguageServerSinkExtensions 21 | { 22 | public static LoggerConfiguration LanguageServer( 23 | this LoggerSinkConfiguration loggerConfiguration, 24 | ILanguageServerFacade server, 25 | IFormatProvider? formatProvider = null) 26 | { 27 | return loggerConfiguration.Sink(new LanguageServerSink(formatProvider, server)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; 4 | using OmniSharp.Extensions.LanguageServer.Server; 5 | using Serilog; 6 | using Yabal.LanguageServer; 7 | using Yabal.LanguageServer.Handlers; 8 | using Yabal.LanguageServer.Logging; 9 | 10 | // System.Diagnostics.Debugger.Launch(); 11 | 12 | var server = await LanguageServer.From(options => 13 | { 14 | options 15 | .WithCommandLineCommunicationChannel(args) 16 | .WithHandler() 17 | .WithHandler() 18 | .WithHandler() 19 | .WithHandler() 20 | .WithHandler() 21 | .WithServices(services => 22 | { 23 | services.AddSingleton(); 24 | }) 25 | .OnInitialize((server, request, _) => 26 | { 27 | Log.Logger = new LoggerConfiguration() 28 | .MinimumLevel.Information() 29 | .WriteTo.LanguageServer(server) 30 | .CreateLogger(); 31 | 32 | if (request.Capabilities?.TextDocument != null) 33 | { 34 | request.Capabilities.TextDocument.Rename = new RenameCapability 35 | { 36 | PrepareSupport = true, 37 | DynamicRegistration = true 38 | }; 39 | 40 | request.Capabilities.TextDocument.DocumentHighlight = new DocumentHighlightCapability(); 41 | } 42 | 43 | return Task.CompletedTask; 44 | }); 45 | }); 46 | 47 | await server.WaitForExit; 48 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/TextDocumentContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Diagnostics; 3 | using System.Threading.Tasks; 4 | using OmniSharp.Extensions.LanguageServer.Protocol; 5 | using OmniSharp.Extensions.LanguageServer.Protocol.Server; 6 | 7 | namespace Yabal.LanguageServer; 8 | 9 | public class TextDocumentContainer(ILanguageServerFacade server) 10 | { 11 | public ILanguageServerFacade Server { get; } = server; 12 | 13 | public ConcurrentDictionary Documents { get; } = new(); 14 | 15 | public Document Get(DocumentUri uri) 16 | { 17 | return Documents.AddOrUpdate( 18 | uri, 19 | u => new Document(u, Server, this), 20 | (_, document) => document 21 | ); 22 | } 23 | 24 | public async Task Update(DocumentUri uri, int? version, string text) 25 | { 26 | var document = Get(uri); 27 | 28 | await document.UpdateAsync(version, text); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Yabal.LanguageServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Yabal.LanguageServer/Yabal.LanguageServer.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /src/Yabal.Loaders.Font/Yabal.Loaders.Font.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | Yabal.Loaders 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Yabal.Loaders.Image/ImageLoader.cs: -------------------------------------------------------------------------------- 1 | using SixLabors.ImageSharp; 2 | using SixLabors.ImageSharp.PixelFormats; 3 | 4 | namespace Yabal.Loaders; 5 | 6 | public class ImageLoader : IFileLoader 7 | { 8 | public static readonly IFileLoader Instance = new ImageLoader(); 9 | 10 | public async ValueTask LoadAsync(YabalBuilder builder, SourceRange range, string path, 11 | FileReader reader) 12 | { 13 | var (_, bytes) = await reader.ReadAllBytesAsync(range, path); 14 | using var image = Image.Load(bytes); 15 | 16 | var width = (byte)image.Width; 17 | var height = (byte)image.Height; 18 | 19 | var content = new int[width * height + 1]; 20 | var i = 0; 21 | content[i++] = (width << 8) | height; 22 | 23 | Write(image, content, i, height, width); 24 | 25 | return new FileContent(1, content); 26 | } 27 | 28 | private static void Write(Image image, int[] content, int i, byte height, byte width) 29 | { 30 | for (var y = 0; y < height; y++) 31 | { 32 | for (var x = 0; x < width; x++) 33 | { 34 | var pixel = image[x, y]; 35 | var a = pixel.A > 0 ? 1 : 0; 36 | var value = (a << 15) | (pixel.R / 8 << 10) | (pixel.G / 8 << 5) | (pixel.B / 8); 37 | 38 | content[i++] = value; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Yabal.Loaders.Image/Yabal.Loaders.Image.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | Yabal.Loaders 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Yabal.SourceGenerator/EmbeddedResources.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Yabal.SourceGenerator; 4 | public static class EmbeddedResource 5 | { 6 | private static readonly string? BaseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 7 | 8 | public static string GetContent(string relativePath) 9 | { 10 | if (BaseDir != null) 11 | { 12 | var filePath = Path.Combine(BaseDir, Path.GetFileName(relativePath)); 13 | 14 | if (File.Exists(filePath)) 15 | { 16 | return File.ReadAllText(filePath); 17 | } 18 | } 19 | 20 | var baseName = Assembly.GetExecutingAssembly().GetName().Name; 21 | var resourceName = relativePath 22 | .TrimStart('.') 23 | .Replace(Path.DirectorySeparatorChar, '.') 24 | .Replace(Path.AltDirectorySeparatorChar, '.'); 25 | 26 | var manifestResourceName = Assembly.GetExecutingAssembly() 27 | .GetManifestResourceNames().FirstOrDefault(x => x.EndsWith(resourceName)); 28 | 29 | if (string.IsNullOrEmpty(manifestResourceName)) 30 | { 31 | throw new InvalidOperationException($"Did not find required resource ending in '{resourceName}' in assembly '{baseName}'."); 32 | } 33 | 34 | using var stream = Assembly.GetExecutingAssembly() 35 | .GetManifestResourceStream(manifestResourceName); 36 | 37 | if (stream == null) 38 | { 39 | throw new InvalidOperationException($"Did not find required resource '{manifestResourceName}' in assembly '{baseName}'."); 40 | } 41 | 42 | using var reader = new StreamReader(stream); 43 | return reader.ReadToEnd(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Yabal.SourceGenerator/InstructionGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Yabal.Instructions; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.Text; 5 | using Scriban; 6 | 7 | namespace Yabal.SourceGenerator; 8 | 9 | public record InstructionStepModel( 10 | int Id, 11 | MicroInstruction MicroInstruction, 12 | bool HasFlags = false, 13 | bool FlagA = false, 14 | bool FlagB = false 15 | ); 16 | 17 | public class InstructionModel 18 | { 19 | public InstructionModel(Instruction instruction) 20 | { 21 | Id = instruction.Id; 22 | Name = instruction.Name; 23 | IsSimple = true; 24 | 25 | var stepCount = instruction.MicroInstructions.Length / 4; 26 | 27 | for (var i = 2; i < stepCount; i++) 28 | { 29 | var microInstructions = instruction.MicroInstructions 30 | .Skip(i * 4) 31 | .Take(4) 32 | .ToArray(); 33 | 34 | if (microInstructions.Distinct().Count() == 1) 35 | { 36 | var microInstruction = microInstructions[0]; 37 | 38 | if (microInstruction.Value == 0) 39 | { 40 | continue; 41 | } 42 | 43 | if (microInstruction.IsEI) 44 | { 45 | break; 46 | } 47 | 48 | Steps.Add(new InstructionStepModel(i, microInstruction)); 49 | } 50 | else 51 | { 52 | for (var j = 0; j < microInstructions.Length; j++) 53 | { 54 | var microInstruction = microInstructions[j]; 55 | 56 | if (microInstruction == null) 57 | { 58 | continue; 59 | } 60 | 61 | Steps.Add(new InstructionStepModel( 62 | i, 63 | microInstruction, 64 | true, 65 | j is 0b10 or 0b11, 66 | j is 0b01 or 0b11 67 | )); 68 | } 69 | } 70 | } 71 | } 72 | 73 | public int Id { get; } 74 | 75 | public string Name { get; } 76 | 77 | public bool IsSimple { get; set; } 78 | 79 | public List Steps { get; } = new(); 80 | } 81 | 82 | public record Model( 83 | IReadOnlyList Instructions 84 | ); 85 | 86 | [Generator] 87 | public class InstructionGenerator : ISourceGenerator 88 | { 89 | public void Initialize(GeneratorInitializationContext context) 90 | { 91 | } 92 | 93 | public void Execute(GeneratorExecutionContext context) 94 | { 95 | const string file = "template.scriban"; 96 | 97 | var model = new Model( 98 | Instruction.Default.Select(i => new InstructionModel(i)) 99 | .ToList() 100 | ); 101 | var template = Template.Parse(EmbeddedResource.GetContent(file), file); 102 | var output = template.Render(model, member => member.Name); 103 | 104 | context.AddSource("CpuInstructions", SourceText.From(output, Encoding.UTF8)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Yabal.SourceGenerator/Yabal.SourceGenerator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 10 6 | false 7 | enable 8 | enable 9 | true 10 | true 11 | 12 | 13 | 14 | 15 | 16 | Core\ 17 | Core\Character.cs 18 | 19 | 20 | Core\ 21 | Core\Either.cs 22 | 23 | 24 | Core\ 25 | Core\Extensions\SpanExtensions.cs 26 | 27 | 28 | Core\ 29 | Core\HexFile.cs 30 | 31 | 32 | Core\ 33 | Core\Instructions\Instruction.cs 34 | 35 | 36 | Core\ 37 | Core\Instructions\InstructionReference.cs 38 | 39 | 40 | Core\ 41 | Core\Instructions\MicroInstruction.cs 42 | 43 | 44 | Core\ 45 | Core\IProgram.cs 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | $(GetTargetPathDependsOn);GetDependencyTargetPaths 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/Yabal.Wasm/Interop.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Yabal.Devices; 4 | using Exception = System.Exception; 5 | 6 | namespace Yabal.Browser; 7 | 8 | public static class Interop 9 | { 10 | private static Cpu? _cpu; 11 | 12 | [UnmanagedCallersOnly(EntryPoint = "Compile")] 13 | public static unsafe void Compile(byte* bytes, int byteLength) 14 | { 15 | _cpu?.Halt(); 16 | _cpu = null; 17 | 18 | try 19 | { 20 | var code = Encoding.UTF8.GetString(bytes, byteLength); 21 | int[] data; 22 | 23 | using (var context = new YabalContext()) 24 | { 25 | var builder = new YabalBuilder(context) 26 | { 27 | Debug = true 28 | }; 29 | 30 | builder.CompileCodeWithoutFiles(code); 31 | 32 | var program = builder.Build(); 33 | 34 | foreach (var (range, errors) in builder.Errors) 35 | { 36 | Console.WriteLine(range); 37 | 38 | foreach (var error in errors) 39 | { 40 | Console.WriteLine($" - {error.Level} {error.Message}"); 41 | } 42 | } 43 | 44 | data = program.ToArray(); 45 | } 46 | 47 | _cpu = CpuBuilder.Create() 48 | .WithMemory(0, data) 49 | .WithScreen() 50 | .WithCharacter() 51 | .WithDebug() 52 | .Create(); 53 | 54 | Start(); 55 | } 56 | catch (Exception e) 57 | { 58 | Console.WriteLine("Failed to compile: " + e); 59 | } 60 | } 61 | 62 | [UnmanagedCallersOnly(EntryPoint = "Step")] 63 | public static int Step(int amount) 64 | { 65 | if (_cpu is not {} cpu) 66 | { 67 | return 0; 68 | } 69 | 70 | try 71 | { 72 | cpu.Step(amount); 73 | return cpu.ProgramCounter; 74 | } 75 | catch (Exception e) 76 | { 77 | Console.WriteLine("Failed to step: " + e); 78 | cpu.Halt(); 79 | return -1; 80 | } 81 | } 82 | 83 | [DllImport("NativeLib")] 84 | public static extern unsafe void UpdateScreen(int* screen); 85 | 86 | [DllImport("NativeLib")] 87 | public static extern unsafe void ShowVariable(int line, int offset, int size, int* screen); 88 | 89 | [DllImport("NativeLib")] 90 | public static extern unsafe void Halt(); 91 | 92 | [DllImport("NativeLib")] 93 | public static extern unsafe void Start(); 94 | } 95 | -------------------------------------------------------------------------------- /src/Yabal.Wasm/WasmHandler.cs: -------------------------------------------------------------------------------- 1 | using Yabal.Devices; 2 | 3 | namespace Yabal.Browser; 4 | 5 | public class WasmHandler : Handler 6 | { 7 | private readonly byte[] _screen = new byte[108 * 108 * 4]; 8 | 9 | public override void SetPixel(int address, ScreenColor color) 10 | { 11 | var index = address * 4; 12 | _screen[index + 0] = (byte)color.R; 13 | _screen[index + 1] = (byte)color.G; 14 | _screen[index + 2] = (byte)color.B; 15 | _screen[index + 3] = (byte)color.A; 16 | } 17 | 18 | public override void LogSpeed(int steps, float value) 19 | { 20 | 21 | } 22 | 23 | public override unsafe void FlushScreen() 24 | { 25 | fixed (byte* screen = _screen) 26 | { 27 | Interop.UpdateScreen((int*)screen); 28 | } 29 | } 30 | 31 | public override void Halt() 32 | { 33 | FlushScreen(); 34 | Interop.Halt(); 35 | } 36 | 37 | public override unsafe void ShowVariable(int line, int offset, Span value) 38 | { 39 | fixed (int* screen = value) 40 | { 41 | Interop.ShowVariable(line, offset, value.Length, screen); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Yabal.Wasm/Yabal.Wasm.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | true 9 | browser-wasm 10 | Shared 11 | false 12 | true 13 | wasm 14 | AnyCPU 15 | true 16 | true 17 | true 18 | true 19 | -s EXPORTED_RUNTIME_METHODS=cwrap 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Yabal.Wasm/build.cmd: -------------------------------------------------------------------------------- 1 | set EMSDK=C:\Sources\emsdk 2 | 3 | touch Interop.cs 4 | dotnet publish -c Release 5 | %EMSDK%/upstream/bin/wasm-opt -Oz -o bin/Release/net8.0/browser-wasm/native/Yabal.wasm bin/Release/net8.0/browser-wasm/native/Yabal.Wasm.wasm 6 | 7 | xcopy /Y /Q bin\Release\net8.0\browser-wasm\native\Yabal.wasm C:\Sources\yabal.dev\public\runtime\Yabal.Wasm.wasm 8 | xcopy /Y /Q bin\Release\net8.0\browser-wasm\native\Yabal.Wasm.js C:\Sources\yabal.dev\public\runtime\Yabal.Wasm.js -------------------------------------------------------------------------------- /tests/Yabal.SourceGenerator.Tests/GenerateTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | using VerifyTests; 5 | using VerifyXunit; 6 | 7 | namespace Yabal.SourceGenerator.Tests; 8 | 9 | [UsesVerify] 10 | public class GenerateTest 11 | { 12 | [Fact] 13 | public Task GenerateInstructions() 14 | { 15 | VerifySourceGenerators.Initialize(); 16 | 17 | var compilation = CSharpCompilation.Create("name"); 18 | var generator = new InstructionGenerator(); 19 | 20 | GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); 21 | 22 | driver = driver.RunGenerators(compilation); 23 | 24 | return Verifier.Verify(driver); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Yabal.SourceGenerator.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | -------------------------------------------------------------------------------- /tests/Yabal.SourceGenerator.Tests/Yabal.SourceGenerator.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net472 5 | false 6 | preview 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | runtime; build; native; contentfiles; analyzers; buildtransitive 25 | all 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/Yabal.Tests/InstructionBuilderTest.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute; 2 | using Yabal.Devices; 3 | using Yabal.Instructions; 4 | using Xunit.Abstractions; 5 | using Yabal; 6 | 7 | namespace Yabal.Tests; 8 | 9 | public class InstructionBuilderTest 10 | { 11 | private readonly ITestOutputHelper _output; 12 | 13 | public InstructionBuilderTest(ITestOutputHelper output) 14 | { 15 | _output = output; 16 | } 17 | 18 | private global::Yabal.Devices.Cpu Create(InstructionBuilder builder) 19 | { 20 | _output.WriteLine("Instructions:"); 21 | _output.WriteLine(builder.ToString()); 22 | 23 | var mock = Substitute.For(); 24 | var cpu = CpuBuilder.Create(mock) 25 | .WithMemory() 26 | .WithProgram(builder) 27 | .Create(); 28 | 29 | return cpu; 30 | } 31 | 32 | [Theory] 33 | [InlineData("ADD", 2, 2, 4)] 34 | [InlineData("SUB", 2, 2, 0)] 35 | [InlineData("MULT", 2, 2, 4)] 36 | [InlineData("DIV", 2, 2, 1)] 37 | [InlineData("NOT", 0b10, 0, ~0b10 & ushort.MaxValue)] 38 | [InlineData("AND", 0b10, 0b11, 0b10)] 39 | [InlineData("OR", 0b10, 0b11, 0b11)] 40 | [InlineData("BSL", 0b1, 1, 0b10)] 41 | [InlineData("BSR", 0b10, 1, 0b1)] 42 | public void Binary(string instruction, int a, int b, int expected) 43 | { 44 | var builder = new InstructionBuilder(); 45 | builder.SetA(a); 46 | builder.SetB(b); 47 | builder.Emit(instruction); 48 | 49 | var cpu = Create(builder); 50 | cpu.Run(); 51 | 52 | Assert.Equal((expected, b, 0), (cpu.A, cpu.B, cpu.C)); 53 | } 54 | 55 | [Fact] 56 | public void JMP() 57 | { 58 | var builder = new InstructionBuilder(); 59 | builder.CreateLabel(out var label); 60 | builder.Jump(label); 61 | builder.SetA(10); 62 | builder.Mark(label); 63 | builder.SetB(10); 64 | 65 | var cpu = Create(builder); 66 | cpu.Run(); 67 | 68 | Assert.Equal((0, 10, 0), (cpu.A, cpu.B, cpu.C)); 69 | } 70 | 71 | [Fact] 72 | public void LDLGE() 73 | { 74 | var builder = new InstructionBuilder(); 75 | builder.CreateLabel("END", out var end); 76 | builder.CreatePointer(out var data); 77 | builder.LoadA_Large(data); 78 | builder.Jump(end); 79 | builder.Mark(data); 80 | builder.EmitRaw(int.MaxValue); 81 | builder.Mark(end); 82 | 83 | var cpu = Create(builder); 84 | cpu.Run(); 85 | 86 | Assert.Equal((int.MaxValue, 0, 0), (cpu.A, cpu.B, cpu.C)); 87 | } 88 | 89 | [Fact] 90 | public void JREG() 91 | { 92 | var builder = new InstructionBuilder(); 93 | builder.CreateLabel(out var label); 94 | builder.SetA(label); 95 | builder.JumpToA(); 96 | builder.SetB(10); 97 | builder.Mark(label); 98 | 99 | var cpu = Create(builder); 100 | cpu.Run(); 101 | 102 | Assert.Equal((label.Address, 0, 0), (cpu.A, cpu.B, cpu.C)); 103 | } 104 | 105 | [Fact] 106 | public void Pointer() 107 | { 108 | var builder = new InstructionBuilder(); 109 | builder.EmitRaw(0); 110 | builder.CreatePointer(out var pointerA); 111 | builder.EmitRaw(0); 112 | builder.CreatePointer(out var pointerB); 113 | builder.Build(); 114 | 115 | Assert.NotEqual(pointerA, pointerB); 116 | Assert.Equal(0, pointerA.Address); 117 | Assert.Equal(1, pointerB.Address); 118 | } 119 | 120 | [Fact] 121 | public void BNK() 122 | { 123 | var builder = new InstructionBuilder(); 124 | builder.SetBank(1); 125 | 126 | var cpu = Create(builder); 127 | cpu.Run(); 128 | 129 | Assert.Equal(1, cpu.Bank); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /tests/Yabal.Tests/InstructionDefinitionTest.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Yabal.Instructions; 3 | 4 | namespace Yabal.Tests; 5 | 6 | [UsesVerify] 7 | public class InstructionDefinitionTest 8 | { 9 | [Fact] 10 | public Task Parse() 11 | { 12 | var definitions = Instruction.Parse(new[] 13 | { 14 | "fetch( 0=cr,aw & 1=rm,iw,ce & 2=ei", // Fetch 15 | "ain( 2=aw,ir & 3=wa,rm & 4=ei", // LoadA 16 | }); 17 | 18 | var settings = new VerifySettings(); 19 | settings.IgnoreMember(e => e.Value); 20 | settings.IgnoreInstance(b => !b); 21 | return Verify(definitions, settings); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Yabal.Tests/SpanTest.cs: -------------------------------------------------------------------------------- 1 | namespace Yabal.Tests; 2 | 3 | public class SpanTest 4 | { 5 | [Fact] 6 | public void True() 7 | { 8 | Span flags = new bool?[] {null, true}; 9 | 10 | Assert.True(flags.HasFlag(0b01)); 11 | } 12 | 13 | [Fact] 14 | public void False() 15 | { 16 | Span flags = new bool?[] {null, false}; 17 | 18 | Assert.False(flags.HasFlag(0b01)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Yabal.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | -------------------------------------------------------------------------------- /tests/Yabal.Tests/Yabal.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | false 8 | preview 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | all 19 | 20 | 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | all 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /vscode/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "name": "Launch Client", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /vscode/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.insertSpaces": false, 3 | "typescript.tsc.autoDetect": "off", 4 | "typescript.preferences.quoteStyle": "single" 5 | } -------------------------------------------------------------------------------- /vscode/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | src/** 3 | server/** 4 | .gitignore 5 | package-lock.json 6 | tsconfig.json 7 | rollup.config.js -------------------------------------------------------------------------------- /vscode/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-present Gerard Smit 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. -------------------------------------------------------------------------------- /vscode/images/icon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YabalLang/compiler/659a278a29007ec65f00449a63f5fa09e95c663d/vscode/images/icon.jpeg -------------------------------------------------------------------------------- /vscode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "vscode-yabal", 4 | "displayName": "Yabal", 5 | "icon": "images/icon.jpeg", 6 | "description": "Language server support for Yabal", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/GerardSmit/Astro8.git" 10 | }, 11 | "license": "MIT", 12 | "version": "0.0.2", 13 | "publisher": "GerardSmit", 14 | "author": "GerardSmit", 15 | "keywords": [ 16 | "yabal", 17 | "astro8" 18 | ], 19 | "scripts": { 20 | "build": "dotnet publish ../src/Yabal.LanguageServer/Yabal.LanguageServer.csproj -o extension/bin && rollup -c", 21 | "watch": "rollup -c -w --env=dev" 22 | }, 23 | "devDependencies": { 24 | "@rollup/plugin-commonjs": "^22.0.2", 25 | "@rollup/plugin-node-resolve": "^14.0.1", 26 | "@rollup/plugin-replace": "^4.0.0", 27 | "@rollup/plugin-typescript": "^8.2.5", 28 | "@types/node": "^18.7.16", 29 | "@types/vscode": "^1.1.53", 30 | "rollup": "^2.56.3", 31 | "tslib": "^2.4.0", 32 | "typescript": "^4.8.3" 33 | }, 34 | "main": "./extension/index", 35 | "engines": { 36 | "vscode": "^1.52.0" 37 | }, 38 | "activationEvents": [ 39 | "onLanguage:yabal" 40 | ], 41 | "capabilities": { 42 | "completionProvider": { 43 | "resolveProvider": "true", 44 | "triggerCharacters": [ 45 | "\"" 46 | ] 47 | } 48 | }, 49 | "contributes": { 50 | "languages": [ 51 | { 52 | "id": "yabal", 53 | "aliases": [ 54 | "Yabal" 55 | ], 56 | "extensions": [ 57 | ".yabal" 58 | ] 59 | } 60 | ], 61 | "grammars": [ 62 | { 63 | "language": "yabal", 64 | "scopeName": "source.yabal", 65 | "path": "./syntaxes/yabal.tmLanguage.json" 66 | } 67 | ] 68 | }, 69 | "dependencies": { 70 | "vscode-jsonrpc": "^8.0.2", 71 | "vscode-languageclient": "^8.0.2" 72 | }, 73 | "extensionDependencies": [ 74 | "ms-dotnettools.vscode-dotnet-runtime" 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /vscode/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import typescript from '@rollup/plugin-typescript'; 4 | import replace from '@rollup/plugin-replace'; 5 | import * as path from 'path'; 6 | 7 | const production = !process.env.ROLLUP_WATCH 8 | 9 | export default [{ 10 | input: 'src/extension.ts', 11 | output: { 12 | file: 'extension/index.js', 13 | format: 'cjs', 14 | external: [ 15 | 'vscode' 16 | ] 17 | }, 18 | plugins: [ 19 | replace({ 20 | 'process.env.NODE_ENV': JSON.stringify(production ? 'production' : 'dev'), 21 | 'process.env.SERVER_PATH': JSON.stringify(path.join(__dirname, '..', 'src', 'Yabal.LanguageServer', 'bin', 'Debug', 'net8.0', 'Yabal.LanguageServer.exe')) 22 | }), 23 | typescript(), 24 | nodeResolve(), 25 | commonjs() 26 | ] 27 | }] -------------------------------------------------------------------------------- /vscode/src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as path from 'path'; 3 | import { LanguageClient, TransportKind } from 'vscode-languageclient/node'; 4 | import { Trace } from 'vscode-jsonrpc'; 5 | import { acquireDotNet } from './acquireDotNet'; 6 | 7 | export async function activate(context: vscode.ExtensionContext) { 8 | const output = vscode.window.createOutputChannel('Yabal'); 9 | 10 | let command, args; 11 | 12 | if (process.env.NODE_ENV === 'dev') { 13 | command = process.env.SERVER_PATH; 14 | args = []; 15 | } else { 16 | command = await acquireDotNet('8.0', 'GerardSmit.vscode-yabal'); 17 | args = [path.join(__dirname, 'bin', 'Yabal.LanguageServer.dll')]; 18 | } 19 | 20 | const transport = TransportKind.pipe 21 | const document = { scheme: 'file', language: 'html' }; 22 | 23 | const client = new LanguageClient( 24 | 'yabalLanguageServer', 25 | 'yabal', 26 | { 27 | run : { command, transport, args }, 28 | debug: { command, transport, args } 29 | }, 30 | { 31 | documentSelector: [ document ], 32 | synchronize: { 33 | configurationSection: 'yabalLanguageServer', 34 | fileEvents: vscode.workspace.createFileSystemWatcher('**/*.yabal') 35 | } 36 | }, 37 | false 38 | ); 39 | 40 | client.registerProposedFeatures(); 41 | 42 | context.subscriptions.push(client.onNotification('yabal/log', function(data) { 43 | output.appendLine(data); 44 | })) 45 | 46 | await client.start(); 47 | } -------------------------------------------------------------------------------- /vscode/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "lib": [ 5 | "es2020" 6 | ], 7 | "moduleResolution": "node", 8 | "sourceMap": true 9 | }, 10 | "exclude": [ 11 | "node_modules" 12 | ] 13 | } --------------------------------------------------------------------------------