├── .clang-format ├── .devcontainer ├── devcontainer.json ├── greeting.fish └── setup.bash ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── CMakeLists.txt ├── LICENSE ├── README.md ├── img ├── error.png └── pretty.png ├── mpl ├── task1 │ ├── sample011.mpl │ ├── sample014.mpl │ ├── sample11.mpl │ ├── sample11p.mpl │ ├── sample11pp.mpl │ ├── sample12.mpl │ ├── sample13.mpl │ ├── sample14.mpl │ ├── sample14p.mpl │ ├── sample15.mpl │ ├── sample15a.mpl │ ├── sample16.mpl │ ├── sample17.mpl │ ├── sample18.mpl │ └── sample19p.mpl ├── task2 │ ├── sample021.mpl │ ├── sample022.mpl │ ├── sample023.mpl │ ├── sample024.mpl │ ├── sample025.mpl │ ├── sample026.mpl │ ├── sample02a.mpl │ ├── sample21.mpl │ ├── sample22.mpl │ ├── sample23.mpl │ ├── sample24.mpl │ ├── sample25.mpl │ ├── sample25t.mpl │ ├── sample26.mpl │ ├── sample27.mpl │ ├── sample28p.mpl │ ├── sample29p.mpl │ └── sample2a.mpl └── task3 │ ├── sample032p.mpl │ ├── sample31p.mpl │ ├── sample33p.mpl │ ├── sample34.mpl │ └── sample35.mpl └── src ├── array.c ├── array.h ├── canvas.c ├── canvas.h ├── checker.c ├── codegen_casl2.c ├── codegen_llvm.c ├── compiler.h ├── context.c ├── context.h ├── context_fwd.h ├── lexer.c ├── main.c ├── map.c ├── map.h ├── mppl_syntax.c ├── mppl_syntax.h ├── mppl_syntax_ext.c ├── mppl_syntax_ext.h ├── parser.c ├── pretty_printer.c ├── report.c ├── report.h ├── resolver.c ├── source.c ├── source.h ├── syntax_kind.c ├── syntax_kind.h ├── syntax_tree.c ├── syntax_tree.h ├── tasks.c ├── terminal.h ├── utility.c └── utility.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Webkit 2 | IndentWidth: 2 3 | ContinuationIndentWidth: 2 4 | PointerAlignment: Right 5 | AlignConsecutiveAssignments: Consecutive 6 | AlignConsecutiveDeclarations: Consecutive 7 | AlignConsecutiveMacros: Consecutive 8 | SpaceAfterCStyleCast: true 9 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "KIT LPP", 3 | "image": "omzn/kit-lpp:v2", 4 | "onCreateCommand": "bash .devcontainer/setup.bash", 5 | "remoteUser": "vscode", 6 | "features": { 7 | "ghcr.io/meaningful-ooo/devcontainer-features/fish:1": {}, 8 | "ghcr.io/devcontainers/features/common-utils:2": { 9 | "installZsh": false, 10 | "configureZshAsDefaultShell": false, 11 | "installOhMyZsh": false, 12 | "installOhMyZshConfig": false, 13 | "username": "vscode", 14 | "userUid": "automatic", 15 | "userGid": "automatic" 16 | }, 17 | "ghcr.io/devcontainers/features/git:1": { 18 | "version": "2.43.0", 19 | "ppa": false 20 | }, 21 | "ghcr.io/devcontainers-community/features/llvm:3": { 22 | "version": "latest" 23 | } 24 | }, 25 | "customizations": { 26 | "vscode": { 27 | "extensions": [ 28 | "ms-vscode.cmake-tools", 29 | "ms-vscode.cpptools", 30 | "llvm-vs-code-extensions.vscode-clangd", 31 | "eamodio.gitlens", 32 | "AnsonYeung.pascal-language-basics" 33 | ], 34 | "settings": { 35 | "[c]": { 36 | "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd", 37 | "editor.formatOnSave": true, 38 | "editor.formatOnType": true, 39 | "editor.indentSize": 2 40 | }, 41 | "clangd.path": "clangd", 42 | "cmake.buildTask": true, 43 | "cmake.configureOnOpen": true, 44 | "C_Cpp.intelliSenseEngine": "disabled", 45 | "files.associations": { 46 | "*.h": "c", 47 | "*.mpl": "pascal", 48 | "*.csl": "casl2" 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.devcontainer/greeting.fish: -------------------------------------------------------------------------------- 1 | function fish_greeting 2 | cat /etc/motd 3 | cat /etc/issue 4 | end 5 | -------------------------------------------------------------------------------- /.devcontainer/setup.bash: -------------------------------------------------------------------------------- 1 | cp .devcontainer/greeting.fish $HOME/.config/fish/conf.d/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .cache 3 | 4 | *.csl 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "(gdb) Launch", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/build/mpplc", 9 | "args": [ 10 | "./mpl/task1/sample19p.mpl" 11 | ], 12 | "stopAtEntry": false, 13 | "cwd": "${workspaceFolder}", 14 | "environment": [], 15 | "externalConsole": false, 16 | "MIMode": "gdb", 17 | "miDebuggerPath": "gdb", 18 | "setupCommands": [ 19 | { 20 | "description": "Enable pretty-printing for gdb", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | }, 24 | { 25 | "description": "Set Disassembly Flavor to Intel", 26 | "text": "-gdb-set disassembly-flavor intel", 27 | "ignoreFailures": true 28 | } 29 | ] 30 | }, 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cmake", 6 | "label": "CMake: build", 7 | "command": "build", 8 | "targets": [ 9 | "mpplc", 10 | ], 11 | "group": "build", 12 | "problemMatcher": [ "$gcc" ], 13 | "detail": "CMake build task", 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(KIT_LPP) 3 | 4 | add_executable(mpplc) 5 | 6 | file(GLOB src src/*.c) 7 | target_sources(mpplc 8 | PRIVATE ${src}) 9 | 10 | set_target_properties(mpplc 11 | PROPERTIES 12 | C_STANDARD 90 13 | C_EXTENSIONS OFF) 14 | 15 | set(gnu_like "$") 16 | set(gnu_like_debug "$>") 17 | target_compile_options(mpplc 18 | PRIVATE 19 | "$<${gnu_like}:-pedantic-errors;-Wall;-Wextra>" 20 | "$<${gnu_like_debug}:-fsanitize=address,leak,undefined;-fno-sanitize-recover;-fstack-protector>") 21 | target_link_options(mpplc 22 | PRIVATE 23 | "$<${gnu_like_debug}:-fsanitize=address,leak,undefined>") 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 言語処理プログラミング実装 2 | 3 | このリポジトリは作者が2021年に受講した京都工芸繊維大学情報工学課程の[言語処理プログラミング](https://www.syllabus.kit.ac.jp/archive/?c=detail&pk=1157&schedule_code=12221202&yr=2021&sk=99&sn=%E8%A8%80%E8%AA%9E%E5%87%A6%E7%90%86%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)の成果物にアレンジを加えたものを置いているリポジトリです。 4 | 5 | ## ビルド方法 6 | 7 | ```bash 8 | git clone https://github.com/shouth/LanguageProcessing 9 | cd LanguageProcessing 10 | cmake -S . -B build 11 | cmake --build ./build # 最終成果物として ./build/mpplc が生成されます 12 | ``` 13 | 14 | ## 機能 15 | 16 | ``` 17 | $ ./build/mpplc --help 18 | Usage: ./build/mpplc [OPTIONS] INPUT 19 | Options: 20 | --dump-syntax Dump syntax tree 21 | --pretty-print Pretty print the input file 22 | --syntax-only Check syntax only 23 | --emit-llvm Emit LLVM IR 24 | --emit-casl2 Emit CASL2 25 | --help Print this help message 26 | ``` 27 | 28 | ### エラー出力 29 | 30 | カッコいい(主観)エラーメッセージが出力されます。下の画像のは極端な例だけど。 31 | 実装は[report.c](https://github.com/shouth/LanguageProcessing/blob/main/src/report.c)にはいってます。 32 | 33 | フォーマット自体は[Ariadne](https://github.com/zesterer/ariadne)のものを参考…というかほぼそのまま使ってます。実装は何も見ずに一から自分でやった。 34 | 35 | ![エラーメッセージの画像](./img/error.png) 36 | 37 | ### プリティプリント 38 | 39 | プリティプリントができます。しかも色付き。 40 | 41 | ![プリティプリントの出力](./img/pretty.png) 42 | 43 | ### LLVM IR 44 | 45 | 講義ではコンパイル先の言語としてCASL2で出力を行いますが、このコンパイラにはLLVM IRを出力する機能もついてます。 46 | 47 | ```bash 48 | ./build/mpplc --emit-llvm [mpplファイル] 49 | # mpplファイルと同じディレクトリにLLVM IRのファイルができる 50 | ``` 51 | 52 | こんな感じのファイルが出力されます。 53 | 54 | ```llvm 55 | declare {i16, i1} @llvm.sadd.with.overflow.i16(i16, i16) 56 | declare {i16, i1} @llvm.ssub.with.overflow.i16(i16, i16) 57 | declare {i16, i1} @llvm.smul.with.overflow.i16(i16, i16) 58 | 59 | declare i32 @getchar() 60 | declare i32 @printf(ptr, ...) 61 | declare i32 @scanf(ptr, ...) 62 | declare void @exit(i32) 63 | 64 | @x = common global i16 0 65 | @high = common global i16 0 66 | @low = common global i16 0 67 | @mid = common global i16 0 68 | @can = common global i16 0 69 | 70 | define i32 @main() { 71 | call i32 @printf(ptr @.str0) 72 | store i8 0, ptr @x 73 | call i32 @scanf(ptr @.format.integer, ptr @x) 74 | call i32 @scanf(ptr @.format.line) 75 | call i32 @getchar() 76 | %.t2 = load i16, ptr @x 77 | %.t3 = add i16 0, 0 78 | %.t1 = icmp slt i16 %.t2, %.t3 79 | br i1 %.t1, label %l2, label %l3 80 | 81 | l2: 82 | call i32 @printf(ptr @.str1) 83 | br label %l1 84 | 85 | l3: 86 | %.t4 = add i16 0, 0 87 | store i16 %.t4, ptr @low 88 | %.t5 = add i16 0, 181 89 | store i16 %.t5, ptr @high 90 | br label %l4 91 | 92 | ; 以下省略 93 | ``` 94 | 95 | 生成されたLLVM IRのファイルをclangでコンパイルすればネイティブのプログラムが作れます。target tripleを埋め込んでないのでコンパイル時に警告が出てしまうのはご愛敬ということで。 96 | 97 | ```bash 98 | clang [上のコマンドで生成されたLLVM IRファイル] 99 | ``` 100 | 101 | ## 内部実装 102 | 103 | マルチパスのコンパイラです。 104 | 105 | 1. 構文木の生成([parser.c](https://github.com/shouth/LanguageProcessing/blob/main/src/parser.c)) 106 | 2. 名前解決([resolver.c](https://github.com/shouth/LanguageProcessing/blob/main/src/resolver.c)) 107 | 3. 文法チェック([checker.c](https://github.com/shouth/LanguageProcessing/blob/main/src/resolver.c)) 108 | 4. コード生成([codegen_casl2.c](https://github.com/shouth/LanguageProcessing/blob/main/src/codegen_casl2.c), [codegen_llvm.c](https://github.com/shouth/LanguageProcessing/blob/main/src/codegen_llvm.c)) 109 | 110 | の順に処理が進みます。講義ではワンパスで実装する形で進行していきますが、私は講義を受けていた当時もマルチパスで実装していました。 111 | 112 | あとコンパイルオプションに `-std=c89 -pedantic-errors` を指定するという縛りを設けています。 113 | 114 | ### データ構造 115 | 116 | 可変長配列([array.c](https://github.com/shouth/LanguageProcessing/blob/main/src/array.c))とハッシュテーブル([map.c](https://github.com/shouth/LanguageProcessing/blob/main/src/map.c))を自作してます。可変長配列の方はまあ…特に面白味もないありふれた感じの実装です。 117 | 118 | ハッシュテーブルの実装では[hopscotch hashing](https://en.wikipedia.org/wiki/Hopscotch_hashing)というアルゴリズムを使っています。ハッシュテーブルのアルゴリズムには大きく分けてチェイン法とオープンアドレス法の二つがありますが、このhopscotch hashingはチェイン法とオープンアドレス法をいい感じに統合したアルゴリズムになっています。ハッシュテーブルの充填率が高くなっても性能が落ちないこともあり、作者はこのアルゴリズムが好きです。 119 | 120 | ハッシュテーブルは要素のハッシュ値をもとに格納する位置を定めるため、格納する位置の衝突を避けるためにも、ハッシュ値はできるだけ偏りなくばらけてくれると嬉しいです。そこでハッシュ値を計算するハッシュアルゴリズムとして[FNV-1a](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function)を使用しています。[実装が簡単](https://github.com/shouth/LanguageProcessing/blob/e3d85656d52308ffc2013ce097b4423c6adeb290/src/utility.c#L28-L38)なのになんかええ感じに値がばらけてくれているっぽいのでなんかこう…嬉しい感じです。雪崩効果っていうらしい。 121 | 122 | ```c 123 | #define FNV1A_INIT 0x811C9DC5ul 124 | 125 | unsigned long fnv1a(unsigned long hash, const void *ptr, unsigned long len) 126 | { 127 | const unsigned char *data = ptr; 128 | const unsigned char *end = data + len; 129 | const unsigned long prime = 0x01000193ul; 130 | 131 | for (; data < end; ++data) { 132 | hash = (hash ^ *data) * prime; 133 | } 134 | return 0xFFFFFFFFul & hash; 135 | } 136 | ``` 137 | 138 | 高速なハッシュアルゴリズムとしては[xxHash](https://xxhash.com)があります。ハッシュアルゴリズムを選定するときにこちらも考慮に一応いれてみましたが、実装が込み入っていて自分の実装に取り込むのはかなり手間がかかりそうだったのでやめておきました。他にも読んだうえで取り込んだらライセンスとかの問題もあるし。 139 | 140 | FNV-1aとxxHashそれぞれに対して手元で軽くベンチマークを取ってみたのですが、ハッシュ値の計算対象となるデータが極めて小さい場合はFNV-1aの方が速いっぽいです。逆に大きい場合はxxHashの圧勝でした。まあベンチマークのやり方が悪かった可能性もありますが、FNV-1aはハッシュテーブル向けのアルゴリズムであるように思います。 141 | 142 | ### 構文木 143 | 144 | Red Green Treeという[roslyn](https://github.com/dotnet/roslyn)で発明されたらしきものを使ってます。より厳密にはRed Green Treeが[rust-analyzer](https://github.com/rust-lang/rust-analyzer)で改良されたあとに[biome](https://github.com/biomejs/biome)でさらに改良されたものと似たような感じのものを導入してみました。rust-analyzerのドキュメントに[解説](https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/syntax.md)があるのでよろしければ参照ください。 145 | 146 | Red Green Tree(の改良版)は 147 | 148 | 1. トークンと文法の構造を、親から子の向きにリンクする形で保持する木 149 | 2. トークンのファイル内での位置を保持し、子から親の向きのリンクを提供する木 150 | 3. 文法ごとに異なる型を提供する木 151 | 152 | の三層からなる木構造で構成されています。メモリ上に保持されるのは基本的に1の木構造のみで、2と3の木構造は必要に応じてその場で生成されたり消去されたりします。このような構造とすることで、シンタックスシュガーを脱糖したりなど木構造そのものに変更を与えたい時には1の木構造にパッチを当て、またリンターのルールの検査を行いたい場合には2の木構造を利用して上下方向に木構造をトラバースし、文法ごとに異なる処理を行いたい場合には3の木構造で型毎に異なる処理を実装するというように柔軟な運用ができる利点があるようです。 153 | 154 | 実際に導入してみての所感としては、エラーメッセージを出力する際の位置情報の取り扱いがとても簡単にできて嬉しい印象がありました。一方、少なからずCでコンパイラを実装する場合には、必要に応じて木構造を生成したり消去したりする部分のコードの記述が煩雑になってしまったり、3の木構造で各文法に型を持たせても利点が実感しにくいような印象もあります。もう少し実装方法について考えてみても良かったかもしれません。 155 | 156 | ### エラーメッセージ 157 | 158 | 気合で実装しました。仮想ターミナルっぽいもの([canvas.c](https://github.com/shouth/LanguageProcessing/blob/main/src/canvas.c))を用意して、その上に出力していく([report.c](https://github.com/shouth/LanguageProcessing/blob/main/src/report.c))方法を取りました。ガチで気合で実装したのでアルゴリズムもへったくれもないです。めちゃくちゃ大変だった… 159 | 160 | メッセージ出力を[box-drawing character](https://en.wikipedia.org/wiki/Box-drawing_character)を用いてリッチにしている都合上、簡易的にUTF-8を処理する仕組みが導入してあります。 161 | 162 | ### コード生成(CASL2) 163 | 164 | レジスタ割り付けをちょっとだけ最適化してスタックへの値の出し入れが少なくなるようにしてあります。地味に実装が難しかった… 165 | 166 | ### コード生成(LLVM) 167 | 168 | 就活が嫌すぎて現実逃避の一環でできてしまった代物。[CFG](https://en.wikipedia.org/wiki/Control-flow_graph)の理解があったこともあり二日ぐらいで実装できてしまった。すごいことをしてるっぽく見える部分ですが実際は指定のフォーマットに従ってコードを吐いていくだけなのでCASL2の生成よりも圧倒的に楽です。レジスタ割り付けとかも考える必要ないし。 169 | -------------------------------------------------------------------------------- /img/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shouth/LanguageProcessing/65a990b246c4329d8008a4dd82c3e19405ba57d8/img/error.png -------------------------------------------------------------------------------- /img/pretty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shouth/LanguageProcessing/65a990b246c4329d8008a4dd82c3e19405ba57d8/img/pretty.png -------------------------------------------------------------------------------- /mpl/task1/sample011.mpl: -------------------------------------------------------------------------------- 1 | NAME program var array of begin end if then else procedure return 2 | call break 3 | while do not or div and char integer boolean readln 4 | writeln true false NUMBER STRING + - * = <> 5 | < <= > >= ( ) [ ] := . 6 | , : ; 7 | 1 2 3 4 5 6 7 8 9 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 8 | 'string' '''s''t''r''i''n''g''''''s''' /* string */ 9 | read write const 10 | a=b<=c:=-d; 11 | 12 | 13 | break 14 | -------------------------------------------------------------------------------- /mpl/task1/sample014.mpl: -------------------------------------------------------------------------------- 1 | program sample14; 2 | /* Calculator program */ 3 | /* c n : clear & set n */ 4 | /* + n : add n */ 5 | /* - n : subtract n */ 6 | /* * n : multiply n */ 7 | /* / n : divide n */ 8 | /* o : off */ 9 | var com :char; 10 | x, y : integer; 11 | endflag : boolean; 12 | begin 13 | writeln(' *** Calculator -- h for help ***'); 14 | x := 0; 15 | endflag := false; 16 | while not endflag do begin 17 | writeln(' Please input command :'); 18 | readln(com, y); 19 | if (com = 'c') or (com = 'C') then begin 20 | x := y; 21 | end 22 | else if com = '+' then begin 23 | x := x + y; 24 | end 25 | else if com = '-' then begin 26 | x := x - y; 27 | end 28 | else if com = '*' then begin 29 | x := x * y; 30 | end 31 | else if com = '/' then begin 32 | x := x / y; 33 | end 34 | else if (com = 'o') or (com = 'O') then begin 35 | endflag := true 36 | end 37 | else begin 38 | writeln; 39 | writeln('Calculator Usage:'); 40 | writeln(' c number : clear & set it'); 41 | writeln(' + number : add it'); 42 | writeln(' - number : subtract it'); 43 | writeln(' * number : multiply it'); 44 | writeln(' / number : divide it'); 45 | writeln(' o : off(terminate execution)'); 46 | writeln 47 | end; 48 | if endflag then 49 | writeln('Final Result =', x) 50 | else 51 | writeln('Temporary Result =', x) 52 | end 53 | end. 54 | 55 | -------------------------------------------------------------------------------- /mpl/task1/sample11.mpl: -------------------------------------------------------------------------------- 1 | program sample11; 2 | var n, sum, data : integer; 3 | begin 4 | writeln('input the number of data'); 5 | readln(n); 6 | sum := 0; 7 | while n > 0 do begin 8 | readln(data); 9 | sum := sum + data; 10 | n := n - 1 11 | end; 12 | writeln('Sum of data = ', sum) 13 | end. 14 | -------------------------------------------------------------------------------- /mpl/task1/sample11p.mpl: -------------------------------------------------------------------------------- 1 | program sample11p; 2 | var n : integer; 3 | procedure kazuyomikomi; 4 | begin 5 | writeln('input the number of data'); 6 | readln(n) 7 | end; 8 | var sum : integer; 9 | procedure wakakidasi; 10 | begin 11 | writeln('Sum of data = ', sum) 12 | end; 13 | var data : integer; 14 | procedure goukei; 15 | begin 16 | sum := 0; 17 | while n > 0 do begin 18 | readln(data); 19 | sum := sum + data; 20 | n := n - 1 21 | end 22 | end; 23 | begin 24 | call kazuyomikomi; 25 | call goukei; 26 | call wakakidasi 27 | end. 28 | -------------------------------------------------------------------------------- /mpl/task1/sample11pp.mpl: -------------------------------------------------------------------------------- 1 | program sample11pp; 2 | procedure kazuyomikomi(n : integer); 3 | begin 4 | writeln('input the number of data'); 5 | readln(n) 6 | end; 7 | var sum : integer; 8 | procedure wakakidasi; 9 | begin 10 | writeln('Sum of data = ', sum) 11 | end; 12 | var data : integer; 13 | procedure goukei(n, s : integer); 14 | var data : integer; 15 | begin 16 | s := 0; 17 | while n > 0 do begin 18 | readln(data); 19 | s := s + data; 20 | n := n - 1 21 | end 22 | end; 23 | var n : integer; 24 | begin 25 | call kazuyomikomi(n); 26 | call goukei(n * 2, sum); 27 | call wakakidasi 28 | end. 29 | -------------------------------------------------------------------------------- /mpl/task1/sample12.mpl: -------------------------------------------------------------------------------- 1 | /* The shortest Min-Passy Program : sample12 */ 2 | program S;begin end. 3 | -------------------------------------------------------------------------------- /mpl/task1/sample13.mpl: -------------------------------------------------------------------------------- 1 | program sample13; { square root } 2 | var x, high, low, mid, can : integer; 3 | begin 4 | writeln('Input x for calculating root x'); 5 | readln(x); 6 | if x < 0 then begin 7 | writeln('can not calculate a root of negative number') 8 | end 9 | else begin 10 | low := 0; 11 | high := 181; 12 | while (high - low) >= 2 do begin 13 | mid := (high + low) div 2; 14 | can := mid * mid; 15 | if x < can then high := mid 16 | else if can < x then low := mid 17 | else begin 18 | high := mid; 19 | low := mid 20 | end 21 | end; 22 | if high = low then writeln('root ', x, ' = ', low) 23 | else if (high * high - x) > (x - low * low) then 24 | writeln('root ', x, ' = ', low) 25 | else writeln('root ', x, ' = ', high) 26 | end 27 | end. 28 | 29 | -------------------------------------------------------------------------------- /mpl/task1/sample14.mpl: -------------------------------------------------------------------------------- 1 | program sample14; 2 | /* Calculator program */ 3 | /* c n : clear & set n */ 4 | /* + n : add n */ 5 | /* - n : subtract n */ 6 | /* * n : multiply n */ 7 | /* / n : divide n */ 8 | /* o : off */ 9 | var com :char; 10 | x, y : integer; 11 | endflag : boolean; 12 | begin 13 | writeln(' *** Calculator -- h for help ***'); 14 | x := 0; 15 | endflag := false; 16 | while not endflag do begin 17 | writeln(' Please input command :'); 18 | readln(com, y); 19 | if (com = 'c') or (com = 'C') then begin 20 | x := y; 21 | end 22 | else if com = '+' then begin 23 | x := x + y; 24 | end 25 | else if com = '-' then begin 26 | x := x - y; 27 | end 28 | else if com = '*' then begin 29 | x := x * y; 30 | end 31 | else if com = '/' then begin 32 | x := x div y; 33 | end 34 | else if (com = 'o') or (com = 'O') then begin 35 | endflag := true 36 | end 37 | else begin 38 | writeln; 39 | writeln('Calculator Usage:'); 40 | writeln(' c number : clear & set it'); 41 | writeln(' + number : add it'); 42 | writeln(' - number : subtract it'); 43 | writeln(' * number : multiply it'); 44 | writeln(' / number : divide it'); 45 | writeln(' o : off(terminate execution)'); 46 | writeln 47 | end; 48 | if endflag then 49 | writeln('Final Result =', x) 50 | else 51 | writeln('Temporary Result =', x) 52 | end 53 | end. 54 | -------------------------------------------------------------------------------- /mpl/task1/sample14p.mpl: -------------------------------------------------------------------------------- 1 | program sample14; 2 | /* Calculator program */ 3 | /* c n : clear & set n */ 4 | /* + n : add n */ 5 | /* - n : subtract n */ 6 | /* * n : multiply n */ 7 | /* / n : divide n */ 8 | /* o : off */ 9 | var x, y : integer; 10 | procedure sum; 11 | begin 12 | x := x + y 13 | end; 14 | procedure sub; 15 | begin 16 | x := x - y 17 | end; 18 | procedure mult; 19 | begin 20 | x := x * y 21 | end; 22 | procedure divide; 23 | begin 24 | x := x div y 25 | end; 26 | var com :char; 27 | endflag : boolean; 28 | begin 29 | writeln(' *** Calculator -- h for help ***'); 30 | x := 0; 31 | endflag := false; 32 | while not endflag do begin 33 | writeln(' Please input command :'); 34 | readln(com, y); 35 | if (com = 'c') or (com = 'C') then x := y 36 | else if com = '+' then call sum 37 | else if com = '-' then call sub 38 | else if com = '*' then call mult 39 | else if com = '/' then call divide 40 | else if (com = 'o') or (com = 'O') then endflag := true 41 | else begin 42 | writeln; 43 | writeln('Calculator Usage:'); 44 | writeln(' c number : clear & set it'); 45 | writeln(' + number : add it'); 46 | writeln(' - number : subtract it'); 47 | writeln(' * number : multiply it'); 48 | writeln(' / number : divide it'); 49 | writeln(' o : off(terminate execution)'); 50 | writeln 51 | end; 52 | if endflag then writeln('Final Result =', x) 53 | else writeln('Temporary Result =', x) 54 | end 55 | end. 56 | -------------------------------------------------------------------------------- /mpl/task1/sample15.mpl: -------------------------------------------------------------------------------- 1 | program numberlist; { sample15 } 2 | var e, n: integer; 3 | begin 4 | writeln('Number list'); 5 | writeln(' n ', ' 2n ', ' n**2 ', ' n**3 ', ' 2**n '); 6 | n := 0; 7 | e := 1; 8 | while n < 15 do begin 9 | writeln(n:6, ' ', 2*n:6, ' ', n*n:6, ' ', n*n*n:6, ' ', e:6); 10 | e := e * 2; 11 | n := n + 1; 12 | end 13 | end. 14 | 15 | -------------------------------------------------------------------------------- /mpl/task1/sample15a.mpl: -------------------------------------------------------------------------------- 1 | program numberlist; { sample15a } 2 | var e, n: integer; 3 | begin 4 | writeln('Number list'); 5 | writeln(' n ', ' 2n ', ' n**2 ', ' n**3 ', ' 2**n '); 6 | n := 0; e := 1; 7 | while true do begin 8 | writeln(n:6, ' ', 2*n:6, ' ', n*n:6, ' ', n*n*n:6, ' ', e:6); n := n + 1; 9 | if n>=16 then break; 10 | e := e * 2 11 | end 12 | end. 13 | 14 | -------------------------------------------------------------------------------- /mpl/task1/sample16.mpl: -------------------------------------------------------------------------------- 1 | program sample16; /* prime numbers */ 2 | var furui : array[20000] of boolean; 3 | i, j : integer; 4 | begin 5 | i := 2; 6 | while i < 20000 do begin 7 | furui[i] := true; 8 | i := i + 1 9 | end; 10 | furui[0] := false; 11 | furui[1] := false; 12 | i := 2; 13 | while i < 20000 do begin 14 | if furui[i] then begin 15 | writeln(i, ' is a prime number'); 16 | j := i; 17 | if i < 16384 then 18 | while j < 20000 do begin 19 | furui[j] := false; 20 | j := j + i 21 | end 22 | end; 23 | i := i + 1 24 | end 25 | end. 26 | -------------------------------------------------------------------------------- /mpl/task1/sample17.mpl: -------------------------------------------------------------------------------- 1 | program GCMLCM; /* sample17 */ 2 | var m, n, a, b, r : integer; 3 | gcm, lcm : integer; 4 | begin 5 | writeln('Input two integers'); 6 | readln(m, n); 7 | a := m; 8 | b := n; 9 | while b <> 0 do begin 10 | r := a - (a div b) * b; 11 | a := b; 12 | b := r 13 | end; 14 | gcm := a; 15 | lcm := (m div gcm) * n; 16 | writeln('GCM = ', gcm, ' LCM = ', lcm) 17 | end. 18 | 19 | -------------------------------------------------------------------------------- /mpl/task1/sample18.mpl: -------------------------------------------------------------------------------- 1 | program primefactor; { program18 } 2 | var factor : array[200] of integer; 3 | i, n, can : integer; 4 | begin 5 | i := 0; 6 | while i < 200 do begin 7 | factor[i] := 0; 8 | i := i + 1 9 | end; 10 | writeln('Input positive integer'); 11 | readln(n); 12 | can := 2; 13 | while (can <= n div 2) and (can < 200) do begin 14 | if (n - (n div can) * can) <> 0 then can := can + 1 15 | else begin 16 | factor[can] := factor[can] + 1; 17 | n := n div can 18 | end 19 | end; 20 | i := 0; 21 | if n < 200 then begin 22 | factor[n] := factor[n]+1; 23 | n := 1 24 | end; 25 | while i < 200 do begin 26 | if factor[i] <> 0 then begin 27 | writeln(' ', i, ' ** ', factor[i]); 28 | end; 29 | i := i + 1 30 | end; 31 | if n > 1 then writeln(' ', n, ' ** ', 1) 32 | end. 33 | -------------------------------------------------------------------------------- /mpl/task1/sample19p.mpl: -------------------------------------------------------------------------------- 1 | program sample19; 2 | /* Calculator program */ 3 | /* c n : clear & set n */ 4 | /* + n : add n */ 5 | /* - n : subtract n */ 6 | /* * n : multiply n */ 7 | /* / n : divide by n */ 8 | /* o : off */ 9 | procedure gcmlcm(m, n, gc, lc : integer); 10 | /* gc := GCM(m,n), lc := LCM(m,n) */ 11 | /* m and n are not changed */ 12 | var a,b,r : integer; 13 | begin 14 | a := m; 15 | b := n; 16 | while b <> 0 do begin 17 | r := a - (a div b) * b; 18 | a := b; 19 | b := r 20 | end; 21 | gc := a; 22 | lc := (m div gc) * n 23 | end; 24 | procedure abs(a, b : integer); 25 | begin 26 | if a < 0 then b := -a else b := a 27 | end; 28 | procedure gcm(a, b, gc : integer); 29 | var lc, aa, bb : integer; 30 | begin 31 | if (a = 0) or (b = 0) then gc := 1 32 | else begin 33 | call abs(a,aa); call abs(b,bb); 34 | call gcmlcm(aa, bb, gc, lc) 35 | end 36 | end; 37 | procedure lcm(a, b, lc : integer); 38 | var gc, aa, bb : integer; 39 | begin 40 | if (a = 0) or (b = 0) then lc := 1 41 | else begin 42 | call abs(a,aa); call abs(b,bb); 43 | call gcmlcm(aa, bb, gc, lc) 44 | end 45 | end; 46 | procedure reduce(a1, a2 : integer); 47 | var gc : integer; 48 | begin 49 | if a1 = 0 then begin a2 := 1; return end; 50 | if a2 = 0 then begin a1 := 1; return end; 51 | if a2 < 0 then begin a2 := -a2; a1 := -a1 end; 52 | call gcm(a1, a2, gc); 53 | a1 := a1 div gc; 54 | a2 := a2 div gc 55 | end; 56 | 57 | procedure sum(x1, x2, y1, y2 : integer); 58 | var lc, y11 : integer; 59 | begin 60 | call lcm(x2, y2, lc); 61 | x1 := x1 * (lc div x2); 62 | y11 := y1 * (lc div y2); 63 | x1 := x1 + y11; 64 | x2 := lc; 65 | call reduce(x1, x2) 66 | end; 67 | procedure sub(x1, x2, y1, y2 : integer); 68 | var lc, y11 : integer; 69 | begin 70 | call sum(x1, x2, -y1, y2) 71 | end; 72 | procedure mult(x1, x2, y1, y2 : integer); 73 | var gc, y11, y22 : integer; 74 | begin 75 | call gcm(x1, y2, gc); 76 | x1 := x1 div gc; 77 | y22 := y2 div gc; 78 | call gcm(x2, y1, gc); 79 | x2 := x2 div gc; 80 | y11 := y1 div gc; 81 | x1 := x1 * y11; 82 | x2 := x2 * y22; 83 | call reduce(x1, x2) 84 | end; 85 | procedure divide(x1, x2, y1, y2 : integer); 86 | begin 87 | call mult(x1, x2, y2, y1) 88 | end; 89 | 90 | procedure printfinal(a, b : integer); 91 | begin 92 | if a = 0 then writeln('Final Result =', a) 93 | else if b = 1 then writeln('Final Result =', a) 94 | else writeln('Final Result =', a, '/', b) 95 | end; 96 | procedure printtemp(a, b : integer); 97 | begin 98 | if a = 0 then writeln('Temporary Result =', a) 99 | else if b = 1 then writeln('Temporary Result =', a) 100 | else writeln('Temporary Result =', a, '/', b) 101 | end; 102 | 103 | var x1, x2, y1,y2 : integer; 104 | var com :char; 105 | endflag : boolean; 106 | begin 107 | writeln(' *** Calculator -- h for help ***'); 108 | x1 := 0; x2 :=1; 109 | endflag := false; 110 | while not endflag do begin 111 | writeln(' Please input command :'); 112 | readln(com, y1); y2 := 1; 113 | if (com = 'c') or (com = 'C') then begin x1 := y1; x2 := y2 end 114 | else if com = '+' then call sum(x1, x2, y1, y2) 115 | else if com = '-' then call sub(x1, x2, y1, y2) 116 | else if com = '*' then call mult(x1, x2, y1, y2) 117 | else if com = '/' then 118 | if y1 = 0 then writeln('*** 0-divide error ***') 119 | else call divide(x1, x2, y1, y2) 120 | else if (com = 'o') or (com = 'O') then endflag := true 121 | else begin 122 | writeln; 123 | writeln('Calculator Usage:'); 124 | writeln(' c number : clear & set it'); 125 | writeln(' + number : add it'); 126 | writeln(' - number : subtract it'); 127 | writeln(' * number : multiply it'); 128 | writeln(' / number : divide by it'); 129 | writeln(' o : off(terminate execution)'); 130 | writeln 131 | end; 132 | if endflag then call printfinal(x1, x2) 133 | else call printtemp(x1, x2) 134 | end 135 | end. 136 | -------------------------------------------------------------------------------- /mpl/task2/sample021.mpl: -------------------------------------------------------------------------------- 1 | program assigninteger {sample21} 2 | var n: integer; 3 | begin 4 | n := 1 5 | end. 6 | -------------------------------------------------------------------------------- /mpl/task2/sample022.mpl: -------------------------------------------------------------------------------- 1 | program assignboolean; {sample22} 2 | var x y : boolean; 3 | begin 4 | x := true; 5 | y := false; 6 | end. 7 | -------------------------------------------------------------------------------- /mpl/task2/sample023.mpl: -------------------------------------------------------------------------------- 1 | program assignchar; {sample23} 2 | var x,y : char;; 3 | begin 4 | x := 'X'; 5 | y := 'Y' 6 | end. 7 | -------------------------------------------------------------------------------- /mpl/task2/sample024.mpl: -------------------------------------------------------------------------------- 1 | program Write; {sample24} 2 | begin; 3 | writeln('It's OK?') 4 | end. 5 | -------------------------------------------------------------------------------- /mpl/task2/sample025.mpl: -------------------------------------------------------------------------------- 1 | program ifst; {sample25} 2 | var ch : char; 3 | begin 4 | readln(ch); 5 | if ch = 'a' then writeln('It is ''a'' '); 6 | else writeln('It is not ''a'' ') 7 | end. 8 | -------------------------------------------------------------------------------- /mpl/task2/sample026.mpl: -------------------------------------------------------------------------------- 1 | program whilest; {sample26} 2 | var n, i, sum : integer; 3 | begin 4 | readln(n); 5 | i := n; 6 | sum := 0; 7 | while (i > 00) do begin 8 | sum := sum + i; 9 | i := i - 1 10 | end; 11 | writeln('Summention of 1 - ', n, ' is ', sum) 12 | end. 13 | -------------------------------------------------------------------------------- /mpl/task2/sample02a.mpl: -------------------------------------------------------------------------------- 1 | program TeaBreak; 2 | begin 3 | ;;;;;;;;;; 4 | writeln 5 | ( 6 | 'Say "BREAK!!" ' 7 | ) 8 | ; 9 | ;;;;;;;;;; 10 | break 11 | end. -------------------------------------------------------------------------------- /mpl/task2/sample21.mpl: -------------------------------------------------------------------------------- 1 | program assigninteger; {sample21} 2 | var n:integer;begin n:=1 end. 3 | -------------------------------------------------------------------------------- /mpl/task2/sample22.mpl: -------------------------------------------------------------------------------- 1 | program assignboolean; {sample22} 2 | var 3 | x,y 4 | : 5 | boolean; 6 | 7 | 8 | begin 9 | x := true; 10 | y := false; 11 | end. 12 | -------------------------------------------------------------------------------- /mpl/task2/sample23.mpl: -------------------------------------------------------------------------------- 1 | program assignchar; {sample23} 2 | var x,y : char; 3 | begin 4 | x := 'X';y := 'Y' end. 5 | -------------------------------------------------------------------------------- /mpl/task2/sample24.mpl: -------------------------------------------------------------------------------- 1 | program Write; {sample24}begin 2 | writeln ( 'It''s OK?' )end. 3 | -------------------------------------------------------------------------------- /mpl/task2/sample25.mpl: -------------------------------------------------------------------------------- 1 | program ifst; 2 | {sample25} 3 | var 4 | ch 5 | : 6 | char; 7 | begin readln(ch); 8 | if ch = 'a' then writeln('It is ''a'' ')else writeln('It is not ''a'' ') 9 | end. 10 | -------------------------------------------------------------------------------- /mpl/task2/sample25t.mpl: -------------------------------------------------------------------------------- 1 | program IfstTC; {sample25t} 2 | var 3 | ch:char;int:integer;boolx,booly:boolean; 4 | begin 5 | boolx:=true;booly:=false;ch:='a';int:=66; 6 | write(integer(ch));write(integer(int));write(integer(boolx),integer(booly)); 7 | writeln; 8 | writeln(char(ch),char(int));writeln(boolean(ch),integer(int),integer(boolx), 9 | integer(booly));end. 10 | -------------------------------------------------------------------------------- /mpl/task2/sample26.mpl: -------------------------------------------------------------------------------- 1 | program whilest; {sample26} 2 | var 3 | n, 4 | i, 5 | sum 6 | : integer; 7 | begin 8 | readln(n);i:=n;sum:=0; 9 | while i>0 do begin 10 | sum := sum 11 | + i;i:=i-1 12 | end;writeln('Summention of 1 - ', n, ' is ', sum) 13 | end. 14 | -------------------------------------------------------------------------------- /mpl/task2/sample27.mpl: -------------------------------------------------------------------------------- 1 | program nwhilest; {sample27} 2 | var i, j, k : integer; 3 | begin 4 | i := 1; 5 | while i <10 do begin 6 | j := 1; 7 | while j < 10 do begin 8 | k := 1; 9 | while k < 10 do begin 10 | if (k div 2)*2=k then begin k := k+1 end 11 | else begin k:=k+1end 12 | end; 13 | j := j+1; 14 | end; 15 | i:=i+1 16 | end; 17 | writeln ('All End')end. 18 | -------------------------------------------------------------------------------- /mpl/task2/sample28p.mpl: -------------------------------------------------------------------------------- 1 | program sample28p; 2 | procedure p;begin writeln('Hello!') end; 3 | procedure q;begin writeln('Everyone!') end; 4 | begin call p; call q end. 5 | -------------------------------------------------------------------------------- /mpl/task2/sample29p.mpl: -------------------------------------------------------------------------------- 1 | program sample29; 2 | /* Calculator program */ 3 | /* c n : clear & set n */ 4 | /* + n : add n */ 5 | /* - n : subtract n */ 6 | /* * n : multiply n */ 7 | /* / n : divide by n */ 8 | /* o : off */ 9 | var unused1: integer; 10 | UnusedArrayForTest: array[200] of char; 11 | procedure gcmlcm(m, n, gc, lc : integer); 12 | /* gc := GCM(m,n), lc := LCM(m,n) */ 13 | /* m and n are not changed */ 14 | var a,b,r : integer; 15 | begin 16 | a := m; 17 | b := n; 18 | while b <> 0 do begin 19 | r := a - (a div b) * b; 20 | a := b; 21 | b := r 22 | end; 23 | gc := a; 24 | lc := (m div gc) * n 25 | end; 26 | procedure abs(a, b : integer); 27 | begin 28 | if a < 0 then b := -a else b := a 29 | end; 30 | procedure gcm(a, b, gc : integer); 31 | var lc, aa, bb : integer; 32 | begin 33 | if (a = 0) or (b = 0) then gc := 1 34 | else begin 35 | call abs(a,aa); call abs(b,bb); 36 | call gcmlcm(aa, bb, gc, lc) 37 | end 38 | end; 39 | procedure lcm(a, b, lc : integer); 40 | var gc, aa, bb : integer; 41 | begin 42 | if (a = 0) or (b = 0) then lc := 1 43 | else begin 44 | call abs(a,aa); call abs(b,bb); 45 | call gcmlcm(aa, bb, gc, lc) 46 | end 47 | end; 48 | var unusedchar : char; 49 | procedure reduce(a1, a2 : integer); 50 | var gc:integer; 51 | begin 52 | if a1 = 0 then begin a2 := 1; return end; 53 | if a2 = 0 then begin a1 := 1; return end; 54 | if a2 < 0 then begin a1 := -a1; a2 := - a2 end; 55 | call gcm(a1, a2, gc); 56 | a1 := a1 div gc; 57 | a2 := a2 div gc 58 | end; 59 | 60 | procedure sum(x1, x2, y1, y2 : integer); 61 | var lc, y11 : integer; 62 | begin 63 | call lcm(x2, y2, lc); 64 | x1 := x1 * (lc div x2); 65 | y11 := y1 * (lc div y2); 66 | x1 := x1 + y11; 67 | x2 := lc; 68 | call reduce(x1, x2) 69 | end; 70 | procedure sub(x1, x2, y1, y2 : integer); 71 | var lc, y11 : integer; 72 | begin 73 | call sum(x1, x2, -y1, y2) 74 | end; 75 | procedure mult(x1, x2, y1, y2 : integer); 76 | var gc,y22,y11 : integer; 77 | begin 78 | call gcm(x1, y2, gc); 79 | x1 := x1 div gc; 80 | y22 := y2 div gc; 81 | call gcm(x2, y1, gc); 82 | x2 := x2 div gc; 83 | y11 := y1 div gc; 84 | x1 := x1 * y11; 85 | x2 := x2 * y22; 86 | call reduce(x1, x2) 87 | end; 88 | procedure divide(x1, x2, y1, y2 : integer); 89 | begin 90 | call mult(x1, x2, y2, y1) 91 | end; 92 | var unusedarray : array[100] of char; 93 | procedure printfinal(a, b : integer); 94 | begin 95 | if a = 0 then writeln('Final Result =', a) 96 | else if b = 1 then writeln('Final Result =', a) 97 | else writeln('Final Result =', a, '/', b) 98 | end; 99 | procedure printtemp(a, b : integer); 100 | begin 101 | if a = 0 then writeln('Temporary Result =', a) 102 | else if b = 1 then writeln('Temporary Result =', a) 103 | else writeln('Temporary Result =', a, '/', b) 104 | end; 105 | 106 | var x1, x2, y1,y2 : integer; 107 | var com :char; 108 | endflag : boolean; 109 | begin 110 | writeln(' *** Calculator -- h for help ***'); 111 | x1 := 0; x2 :=1; 112 | endflag := false; 113 | while not endflag do begin 114 | writeln(' Please input command :'); 115 | readln(com, y1); y2 := 1; 116 | if (com = 'c') or (com = 'C') then begin x1 := y1; x2 := y2 end 117 | else if com = '+' then call sum(x1, x2, y1, y2) 118 | else if com = '-' then call sub(x1, x2, y1, y2) 119 | else if com = '*' then call mult(x1, x2, y1, y2) 120 | else if com = '/' then call divide(x1, x2, y1, y2) 121 | else if (com = 'o') or (com = 'O') then endflag := true 122 | else begin 123 | writeln; 124 | writeln('Calculator Usage:'); 125 | writeln(' c number : clear & set it'); 126 | writeln(' + number : add it'); 127 | writeln(' - number : subtract it'); 128 | writeln(' * number : multiply it'); 129 | writeln(' / number : divide by it'); 130 | writeln(' o : off(terminate execution)'); 131 | writeln 132 | end; 133 | if endflag then call printfinal(x1, x2) 134 | else call printtemp(x1, x2) 135 | end 136 | end. 137 | -------------------------------------------------------------------------------- /mpl/task2/sample2a.mpl: -------------------------------------------------------------------------------- 1 | program change; /* sample2a.mpl */ { compute change program. What is change? } 2 | { no 5, 50, 500, & 5000 yen used } 3 | var n, count : integer; 4 | begin 5 | writeln('please input change'); readln(n); count := 0; 6 | while n>0 do begin 7 | while n>=10 do begin 8 | while n>= 100 do begin 9 | while n>=1000 do begin 10 | while n >=10000 do begin 11 | count := count +1; n := n-10000; 12 | end; 13 | if count >0 then writeln('10000 yen : ', count); 14 | count := 0; 15 | if n < 1000 then break; 16 | count := count +1; n := n-1000; 17 | end; 18 | if count >0 then writeln(' 1000 yen : ', count); 19 | count := 0; 20 | if n < 100 then break; 21 | count := count +1; n := n-100; 22 | end; 23 | if count >0 then writeln(' 100 yen : ', count); 24 | count := 0; 25 | if n < 10 then break; 26 | count := count +1; n := n-10; 27 | end; 28 | 29 | 30 | 31 | 32 | if count >0 then writeln(' 10 yen : ', count); 33 | count := 0; 34 | if n < 1 then break; 35 | count := count +1; n := n-1; 36 | end; 37 | if count > 0 then writeln(' 1 yen : ', count); 38 | /* if n !=0 then break; */ 39 | end. -------------------------------------------------------------------------------- /mpl/task3/sample032p.mpl: -------------------------------------------------------------------------------- 1 | program rcall; 2 | procedure f; begin call f end; 3 | begin call f end. 4 | -------------------------------------------------------------------------------- /mpl/task3/sample31p.mpl: -------------------------------------------------------------------------------- 1 | program sample31p; 2 | var a : integer; 3 | procedure p(a : char); 4 | begin 5 | writeln('proc of p'); 6 | a := 'a' 7 | end; 8 | var b : char; 9 | procedure q(b:integer); 10 | var a : boolean; 11 | q : integer; 12 | begin 13 | writeln('proc of q'); 14 | readln(q); 15 | a := b = q; 16 | if a then writeln('true') else writeln('false') 17 | end; 18 | var c : integer; 19 | begin 20 | a := 1; b := 'b'; 21 | call p(b); 22 | call q(a);call q(2*a+1) 23 | end. 24 | -------------------------------------------------------------------------------- /mpl/task3/sample33p.mpl: -------------------------------------------------------------------------------- 1 | program sample33p; 2 | var a,x : integer; 3 | procedure p(a : char); 4 | begin 5 | readln(x); 6 | writeln('proc of p',x); 7 | if x=1 then a := 'a'; 8 | if x > 2 then a := 'b' else a := 'c'; 9 | while x > 0 do x := x - 1 10 | end; 11 | var b : char; 12 | procedure q(b:integer); 13 | var a : boolean; 14 | q : integer; 15 | begin 16 | writeln('proc of q'); 17 | readln(q); 18 | a := b = q; 19 | if a then writeln('true') else writeln('false') 20 | end; 21 | var c : integer; 22 | begin 23 | a := 1; b := 'b'; 24 | call p(b); 25 | call q(a) 26 | end. 27 | -------------------------------------------------------------------------------- /mpl/task3/sample34.mpl: -------------------------------------------------------------------------------- 1 | program sample34; 2 | var num : integer; ch : char; 3 | 4 | begin 5 | writeln('input the number of data and data character (readln)'); 6 | readln(num); readln(ch); 7 | writeln; 8 | while num > 0 do begin 9 | write(ch); 10 | num := num - 1 11 | end; 12 | writeln; 13 | writeln('input the number of data and data character (read)'); 14 | read(num); read(ch); 15 | writeln; 16 | while num > 0 do begin 17 | write(ch); 18 | num := num - 1 19 | end; 20 | writeln 21 | end. 22 | -------------------------------------------------------------------------------- /mpl/task3/sample35.mpl: -------------------------------------------------------------------------------- 1 | program typeconv; 2 | var i : integer; b : boolean; c : char; 3 | begin 4 | i := integer(false); 5 | while i <= integer(true) do begin 6 | writeln( boolean(i), ' : ', i); 7 | i := i + 1; 8 | end; 9 | writeln; 10 | i := integer(' '); 11 | while i < 127 do begin 12 | if i div 16 * 16 = i then writeln; 13 | write(char(i), ' '); 14 | i := i + 1; 15 | end; 16 | writeln 17 | end. 18 | -------------------------------------------------------------------------------- /src/array.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "array.h" 23 | 24 | struct Array { 25 | void *data; 26 | unsigned long size; 27 | unsigned long count; 28 | unsigned long capacity; 29 | }; 30 | 31 | Array *array_new(unsigned long size) 32 | { 33 | return array_new_with_capacity(size, 0); 34 | } 35 | 36 | Array *array_new_with_capacity(unsigned long size, unsigned long capacity) 37 | { 38 | Array *array = malloc(sizeof(Array)); 39 | array->data = NULL; 40 | array->size = size; 41 | array->count = 0; 42 | array->capacity = 0; 43 | array_reserve(array, capacity); 44 | return array; 45 | } 46 | 47 | void array_free(Array *array) 48 | { 49 | if (array) { 50 | free(array->data); 51 | free(array); 52 | } 53 | } 54 | 55 | unsigned long array_count(const Array *array) 56 | { 57 | return array->count; 58 | } 59 | 60 | unsigned long array_capacity(const Array *array) 61 | { 62 | return array->capacity; 63 | } 64 | 65 | void *array_data(const Array *array) 66 | { 67 | return array->data; 68 | } 69 | 70 | void *array_at(const Array *array, unsigned long index) 71 | { 72 | return (char *) array->data + array->size * index; 73 | } 74 | 75 | void *array_front(const Array *array) 76 | { 77 | return array_at(array, 0); 78 | } 79 | 80 | void *array_back(const Array *array) 81 | { 82 | return array_at(array, array->count - 1); 83 | } 84 | 85 | void array_reserve(Array *array, unsigned long capacity) 86 | { 87 | if (array->capacity < capacity) { 88 | void *new = realloc(array->data, array->size * capacity); 89 | if (!new) { 90 | fprintf(stderr, "Internal Error: Failed to allocate memory\n"); 91 | exit(EXIT_FAILURE); 92 | } 93 | array->data = new; 94 | array->capacity = capacity; 95 | } 96 | } 97 | 98 | void array_fit(Array *array) 99 | { 100 | void *new = realloc(array->data, array->size * array->count); 101 | if (new) { 102 | array->data = new; 103 | array->capacity = array->count; 104 | } 105 | } 106 | 107 | void array_push(Array *array, void *value) 108 | { 109 | array_push_count(array, value, 1); 110 | } 111 | 112 | void array_push_count(Array *array, void *value, unsigned long count) 113 | { 114 | if (array_count(array) + count > array_capacity(array)) { 115 | unsigned long capacity = array_capacity(array) ? array_capacity(array) : 1; 116 | while (array_count(array) + count > capacity) { 117 | capacity *= 2; 118 | } 119 | array_reserve(array, capacity); 120 | } 121 | memcpy(array_at(array, array_count(array)), value, array->size * count); 122 | array->count += count; 123 | } 124 | 125 | void array_pop(Array *array) 126 | { 127 | array_pop_count(array, 1); 128 | } 129 | 130 | void array_pop_count(Array *array, unsigned long count) 131 | { 132 | if (array->count > count) { 133 | array->count -= count; 134 | } else { 135 | array->count = 0; 136 | } 137 | } 138 | 139 | void array_clear(Array *array) 140 | { 141 | array->count = 0; 142 | } 143 | 144 | void *array_steal(Array *array) 145 | { 146 | void *result = array->data; 147 | array->data = NULL; 148 | array->capacity = 0; 149 | array->count = 0; 150 | array_free(array); 151 | return result; 152 | } 153 | -------------------------------------------------------------------------------- /src/array.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef VECTOR_H 18 | #define VECTOR_H 19 | 20 | typedef struct Array Array; 21 | 22 | Array *array_new(unsigned long size); 23 | Array *array_new_with_capacity(unsigned long size, unsigned long capacity); 24 | void array_free(Array *array); 25 | 26 | unsigned long array_count(const Array *array); 27 | unsigned long array_capacity(const Array *array); 28 | void *array_data(const Array *array); 29 | void *array_at(const Array *array, unsigned long index); 30 | void *array_front(const Array *array); 31 | void *array_back(const Array *array); 32 | void array_reserve(Array *array, unsigned long capacity); 33 | void array_fit(Array *array); 34 | void array_push(Array *array, void *value); 35 | void array_push_count(Array *array, void *value, unsigned long count); 36 | void array_pop(Array *array); 37 | void array_pop_count(Array *array, unsigned long count); 38 | void array_clear(Array *array); 39 | void *array_steal(Array *array); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/canvas.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "array.h" 23 | #include "canvas.h" 24 | #include "terminal.h" 25 | #include "utility.h" 26 | 27 | typedef struct CanvasStyle CanvasStyle; 28 | 29 | struct CanvasStyle { 30 | signed char intensity; 31 | signed char italic; 32 | signed char underline; 33 | unsigned long fg; 34 | unsigned long bg; 35 | }; 36 | 37 | struct CanvasCell { 38 | char character[4]; 39 | int size; 40 | CanvasStyle style; 41 | }; 42 | 43 | struct Canvas { 44 | Array *lines; 45 | unsigned long current_line; 46 | unsigned long current_column; 47 | CanvasStyle style; 48 | }; 49 | 50 | Canvas *canvas_new(void) 51 | { 52 | Canvas *canvas = malloc(sizeof(Canvas)); 53 | Array *line = array_new(sizeof(CanvasCell)); 54 | canvas->lines = array_new(sizeof(Array *)); 55 | array_push(canvas->lines, &line); 56 | canvas->current_line = 0; 57 | canvas->current_column = 0; 58 | canvas->style.intensity = 0; 59 | canvas->style.italic = 0; 60 | canvas->style.underline = 0; 61 | canvas->style.fg = 0; 62 | canvas->style.bg = 0; 63 | return canvas; 64 | } 65 | 66 | void canvas_free(Canvas *canvas) 67 | { 68 | if (canvas) { 69 | unsigned long i; 70 | for (i = 0; i < array_count(canvas->lines); ++i) { 71 | Array **line = array_at(canvas->lines, i); 72 | array_free(*line); 73 | } 74 | array_free(canvas->lines); 75 | free(canvas); 76 | } 77 | } 78 | 79 | #define BUFFER_SIZE 1024 80 | 81 | void canvas_next_line(Canvas *canvas) 82 | { 83 | ++canvas->current_line; 84 | canvas->current_column = 0; 85 | if (canvas->current_line >= array_count(canvas->lines)) { 86 | Array *line = array_new(sizeof(CanvasCell)); 87 | array_push(canvas->lines, &line); 88 | } 89 | } 90 | 91 | void canvas_style(Canvas *canvas, int attr, ...) 92 | { 93 | switch (attr) { 94 | case TERM_RESET: 95 | canvas->style.intensity = 0; 96 | canvas->style.italic = 0; 97 | canvas->style.underline = 0; 98 | canvas->style.fg = 0; 99 | canvas->style.bg = 0; 100 | break; 101 | 102 | case TERM_BOLD: 103 | canvas->style.intensity = 1; 104 | break; 105 | 106 | case TERM_FAINT: 107 | canvas->style.intensity = -1; 108 | break; 109 | 110 | case TERM_ITALIC: 111 | canvas->style.italic = 1; 112 | break; 113 | 114 | case TERM_UNDERLINE: 115 | canvas->style.underline = 1; 116 | break; 117 | 118 | case TERM_FG_BLACK: 119 | case TERM_FG_RED: 120 | case TERM_FG_GREEN: 121 | case TERM_FG_YELLOW: 122 | case TERM_FG_BLUE: 123 | case TERM_FG_MAGENTA: 124 | case TERM_FG_CYAN: 125 | case TERM_FG_WHITE: 126 | case TERM_FG_BRIGHT_BLACK: 127 | case TERM_FG_BRIGHT_RED: 128 | case TERM_FG_BRIGHT_GREEN: 129 | case TERM_FG_BRIGHT_YELLOW: 130 | case TERM_FG_BRIGHT_BLUE: 131 | case TERM_FG_BRIGHT_MAGENTA: 132 | case TERM_FG_BRIGHT_CYAN: 133 | case TERM_FG_BRIGHT_WHITE: 134 | canvas->style.fg = attr; 135 | break; 136 | 137 | case TERM_FG_SELECT: { 138 | va_list args; 139 | va_start(args, attr); 140 | switch (va_arg(args, int)) { 141 | case TERM_COLOR_RGB: { 142 | unsigned long r = va_arg(args, int); 143 | unsigned long g = va_arg(args, int); 144 | unsigned long b = va_arg(args, int); 145 | canvas->style.fg = (TERM_COLOR_RGB << 24) | (r << 16) | (g << 8) | b; 146 | break; 147 | } 148 | case TERM_COLOR_256: { 149 | unsigned long index = va_arg(args, int); 150 | canvas->style.fg = (TERM_COLOR_256 << 24) | index; 151 | break; 152 | } 153 | } 154 | va_end(args); 155 | break; 156 | } 157 | 158 | case TERM_BG_BLACK: 159 | case TERM_BG_RED: 160 | case TERM_BG_GREEN: 161 | case TERM_BG_YELLOW: 162 | case TERM_BG_BLUE: 163 | case TERM_BG_MAGENTA: 164 | case TERM_BG_CYAN: 165 | case TERM_BG_WHITE: 166 | case TERM_BG_BRIGHT_BLACK: 167 | case TERM_BG_BRIGHT_RED: 168 | case TERM_BG_BRIGHT_GREEN: 169 | case TERM_BG_BRIGHT_YELLOW: 170 | case TERM_BG_BRIGHT_BLUE: 171 | case TERM_BG_BRIGHT_MAGENTA: 172 | case TERM_BG_BRIGHT_CYAN: 173 | case TERM_BG_BRIGHT_WHITE: 174 | canvas->style.bg = attr; 175 | break; 176 | 177 | case TERM_BG_SELECT: { 178 | va_list args; 179 | va_start(args, attr); 180 | switch (va_arg(args, int)) { 181 | case TERM_COLOR_RGB: { 182 | unsigned long r = va_arg(args, int); 183 | unsigned long g = va_arg(args, int); 184 | unsigned long b = va_arg(args, int); 185 | canvas->style.bg = (TERM_COLOR_RGB << 24) | (r << 16) | (g << 8) | b; 186 | break; 187 | } 188 | case TERM_COLOR_256: { 189 | unsigned long index = va_arg(args, int); 190 | canvas->style.bg = (TERM_COLOR_256 << 24) | index; 191 | break; 192 | } 193 | } 194 | va_end(args); 195 | break; 196 | } 197 | } 198 | } 199 | 200 | void canvas_write(Canvas *canvas, const char *format, ...) 201 | { 202 | va_list args; 203 | Array **line = array_at(canvas->lines, canvas->current_line); 204 | FILE *file = tmpfile(); 205 | long index = 0; 206 | char buffer[BUFFER_SIZE + 1]; 207 | 208 | va_start(args, format); 209 | vfprintf(file, format, args); 210 | va_end(args); 211 | rewind(file); 212 | while (fgets(buffer + index, BUFFER_SIZE - index, file)) { 213 | while (buffer[index]) { 214 | long size = utf8_len(buffer + index, BUFFER_SIZE - index); 215 | if (size < 0) { 216 | int remain = strlen(buffer + index); 217 | memmove(buffer, buffer + index, remain); 218 | index = remain; 219 | break; 220 | } 221 | 222 | { 223 | unsigned long initial_line_width = array_count(*line); 224 | CanvasCell cell; 225 | cell.style = canvas->style; 226 | cell.size = size; 227 | memcpy(cell.character, buffer + index, size); 228 | if (canvas->current_column < initial_line_width) { 229 | memcpy(array_at(*line, canvas->current_column), &cell, sizeof(CanvasCell)); 230 | } else { 231 | array_push(*line, &cell); 232 | } 233 | ++canvas->current_column; 234 | index += size; 235 | } 236 | } 237 | } 238 | fclose(file); 239 | } 240 | 241 | unsigned long canvas_line(const Canvas *canvas) 242 | { 243 | return canvas->current_line; 244 | } 245 | 246 | unsigned long canvas_column(const Canvas *canvas) 247 | { 248 | return canvas->current_column; 249 | } 250 | 251 | void canvas_seek(Canvas *canvas, unsigned long line, unsigned long column) 252 | { 253 | Array **last_line; 254 | canvas->current_line = line; 255 | canvas->current_column = column; 256 | 257 | while (canvas->current_line >= array_count(canvas->lines)) { 258 | Array *line = array_new(sizeof(CanvasCell)); 259 | array_push(canvas->lines, &line); 260 | } 261 | 262 | last_line = array_at(canvas->lines, canvas->current_line); 263 | while (canvas->current_column >= array_count(*last_line)) { 264 | CanvasCell cell; 265 | cell.style = canvas->style; 266 | cell.size = 1; 267 | strcpy(cell.character, " "); 268 | array_push(*last_line, &cell); 269 | } 270 | } 271 | 272 | void canvas_print(Canvas *canvas, FILE *stream) 273 | { 274 | unsigned long line, column; 275 | for (line = 0; line < array_count(canvas->lines); ++line) { 276 | Array **line_array = array_at(canvas->lines, line); 277 | for (column = 0; column < array_count(*line_array); ++column) { 278 | CanvasCell *cell = array_at(*line_array, column); 279 | int fg_format = cell->style.fg >> 24; 280 | int bg_format = cell->style.bg >> 24; 281 | if (cell->style.intensity > 0) { 282 | fprintf(stream, "\033[1m"); 283 | } else if (cell->style.intensity < 0) { 284 | fprintf(stream, "\033[2m"); 285 | } 286 | if (cell->style.italic) { 287 | fprintf(stream, "\033[3m"); 288 | } 289 | if (cell->style.underline) { 290 | fprintf(stream, "\033[4m"); 291 | } 292 | switch (fg_format) { 293 | case 0: 294 | if (cell->style.fg) { 295 | fprintf(stream, "\033[%lum", cell->style.fg); 296 | } 297 | break; 298 | case TERM_COLOR_RGB: 299 | fprintf(stream, "\033[38;2;%lu;%lu;%lum", 300 | (cell->style.fg >> 16) & 0xFF, (cell->style.fg >> 8) & 0xFF, cell->style.fg & 0xFF); 301 | break; 302 | case TERM_COLOR_256: 303 | fprintf(stream, "\033[38;5;%lum", cell->style.fg & 0xFF); 304 | break; 305 | } 306 | switch (bg_format) { 307 | case 0: 308 | if (cell->style.bg) { 309 | fprintf(stream, "\033[%lum", cell->style.bg); 310 | } 311 | break; 312 | case TERM_COLOR_RGB: 313 | fprintf(stream, "\033[48;2;%lu;%lu;%lum", 314 | (cell->style.bg >> 16) & 0xFF, (cell->style.bg >> 8) & 0xFF, cell->style.bg & 0xFF); 315 | break; 316 | case TERM_COLOR_256: 317 | fprintf(stream, "\033[48;5;%lum", cell->style.bg & 0xFFFFFF); 318 | break; 319 | } 320 | fprintf(stream, "%.*s", (int) cell->size, cell->character); 321 | fprintf(stream, "\033[0m"); 322 | } 323 | if (line + 1 < array_count(canvas->lines)) { 324 | fprintf(stream, "\n"); 325 | } 326 | } 327 | fflush(stream); 328 | } 329 | -------------------------------------------------------------------------------- /src/canvas.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef CANVAS_H 18 | #define CANVAS_H 19 | 20 | #include 21 | 22 | typedef struct Canvas Canvas; 23 | typedef struct CanvasCell CanvasCell; 24 | 25 | Canvas *canvas_new(void); 26 | void canvas_free(Canvas *canvas); 27 | void canvas_next_line(Canvas *canvas); 28 | void canvas_style(Canvas *canvas, int attr, ...); 29 | void canvas_write(Canvas *canvas, const char *format, ...); 30 | unsigned long canvas_line(const Canvas *canvas); 31 | unsigned long canvas_column(const Canvas *canvas); 32 | void canvas_seek(Canvas *canvas, unsigned long line, unsigned long column); 33 | void canvas_print(Canvas *canvas, FILE *stream); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/compiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef COMPILER_H 18 | #define COMPILER_H 19 | 20 | #include "context_fwd.h" 21 | #include "mppl_syntax.h" 22 | #include "source.h" 23 | #include "syntax_kind.h" 24 | 25 | typedef struct LexedToken LexedToken; 26 | 27 | typedef enum { 28 | LEX_OK, 29 | LEX_EOF, 30 | LEX_ERROR_STRAY_CHAR, 31 | LEX_ERROR_NONGRAPHIC_CHAR, 32 | LEX_ERROR_UNTERMINATED_STRING, 33 | LEX_ERROR_UNTERMINATED_COMMENT, 34 | LEX_ERROR_TOO_BIG_NUMBER 35 | } LexStatus; 36 | 37 | struct LexedToken { 38 | SyntaxKind kind; 39 | unsigned long offset; 40 | unsigned long length; 41 | }; 42 | 43 | LexStatus mpplc_lex(const Source *source, unsigned long offset, LexedToken *token); 44 | 45 | int mpplc_parse(const Source *source, Ctx *ctx, MpplProgram **syntax); 46 | 47 | int mpplc_resolve(const Source *source, const MpplProgram *syntax, Ctx *ctx); 48 | 49 | int mpplc_check(const Source *source, const MpplProgram *syntax, Ctx *ctx); 50 | 51 | int mpplc_codegen_casl2(const Source *source, const MpplProgram *syntax, Ctx *ctx); 52 | 53 | int mpplc_codegen_llvm_ir(const Source *source, const MpplProgram *syntax, Ctx *ctx); 54 | 55 | void mpplc_dump_syntax(const MpplProgram *syntax); 56 | 57 | typedef struct PrinterOption PrinterOption; 58 | 59 | struct PrinterOption { 60 | struct { 61 | int enabled; 62 | unsigned long foreground; 63 | unsigned long program; 64 | unsigned long keyword; 65 | unsigned long operator; 66 | unsigned long procedure; 67 | unsigned long parameter; 68 | unsigned long string; 69 | unsigned long literal; 70 | } color; 71 | }; 72 | 73 | void mpplc_pretty_print(const MpplProgram *syntax, const PrinterOption *option); 74 | 75 | int mpplc_task1(int argc, const char **argv); 76 | int mpplc_task2(int argc, const char **argv); 77 | int mpplc_task3(int argc, const char **argv); 78 | int mpplc_task4(int argc, const char **argv); 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /src/context.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "array.h" 23 | #include "context.h" 24 | #include "context_fwd.h" 25 | #include "map.h" 26 | #include "string.h" 27 | #include "syntax_tree.h" 28 | #include "utility.h" 29 | 30 | struct String { 31 | const char *data; 32 | unsigned long length; 33 | }; 34 | 35 | struct TypeList { 36 | const Type **types; 37 | unsigned long length; 38 | }; 39 | 40 | struct Type { 41 | TypeKind kind; 42 | }; 43 | 44 | struct ArrayType { 45 | TypeKind kind; 46 | const Type *base; 47 | unsigned long length; 48 | }; 49 | 50 | struct ProcType { 51 | TypeKind kind; 52 | const TypeList *params; 53 | }; 54 | 55 | struct Def { 56 | DefKind kind; 57 | const String *name; 58 | const SyntaxTree *syntax; 59 | }; 60 | 61 | struct Ctx { 62 | Map *strings; 63 | Map *type_lists; 64 | Map *types; 65 | Array *defs; 66 | Map *resolved; 67 | Map *syntax_type; 68 | }; 69 | 70 | static const TypeList CTX_TYPE_LIST_EMPTY = { NULL, 0 }; 71 | 72 | static const Type CTX_TYPE_BOOLEAN = { TYPE_BOOLEAN }; 73 | static const Type CTX_TYPE_CHAR = { TYPE_CHAR }; 74 | static const Type CTX_TYPE_INTEGER = { TYPE_INTEGER }; 75 | static const Type CTX_TYPE_STRING = { TYPE_STRING }; 76 | 77 | static unsigned long string_hash(const void *value) 78 | { 79 | const String *x = value; 80 | return fnv1a(FNV1A_INIT, x->data, x->length); 81 | } 82 | 83 | static int string_equal(const void *left, const void *right) 84 | { 85 | const String *l = left; 86 | const String *r = right; 87 | return l->length == r->length && memcmp(l->data, r->data, l->length) == 0; 88 | } 89 | 90 | static unsigned long type_list_hash_core(unsigned long hash, const TypeList *list); 91 | static unsigned long type_hash_core(unsigned long hash, const Type *type); 92 | 93 | static unsigned long type_list_hash_core(unsigned long hash, const TypeList *list) 94 | { 95 | unsigned long i; 96 | for (i = 0; i < list->length; i++) { 97 | hash = type_hash_core(hash, list->types[i]); 98 | } 99 | return hash; 100 | } 101 | 102 | static unsigned long type_hash_core(unsigned long hash, const Type *type) 103 | { 104 | hash = fnv1a(hash, &type->kind, sizeof(type->kind)); 105 | switch (type->kind) { 106 | case TYPE_ARRAY: { 107 | const ArrayType *array = (const ArrayType *) type; 108 | hash = type_hash_core(hash, array->base); 109 | hash = fnv1a(hash, &array->length, sizeof(array->length)); 110 | return hash; 111 | } 112 | case TYPE_PROC: { 113 | const ProcType *proc = (const ProcType *) type; 114 | hash = type_list_hash_core(hash, proc->params); 115 | return hash; 116 | } 117 | default: 118 | return hash; 119 | } 120 | } 121 | 122 | static unsigned long type_list_hash(const void *value) 123 | { 124 | return type_list_hash_core(FNV1A_INIT, value); 125 | } 126 | 127 | static int type_list_equal(const void *left, const void *right) 128 | { 129 | const TypeList *l = left; 130 | const TypeList *r = right; 131 | 132 | if (l->length != r->length) { 133 | return 0; 134 | } else if (l->length == 0) { 135 | return 1; 136 | } else { 137 | return !memcmp(l->types, r->types, sizeof(Type *) * l->length); 138 | } 139 | } 140 | 141 | static unsigned long type_hash(const void *value) 142 | { 143 | return type_hash_core(FNV1A_INIT, value); 144 | } 145 | 146 | static int type_equal(const void *left, const void *right) 147 | { 148 | const Type *l = left; 149 | const Type *r = right; 150 | if (l->kind != r->kind) { 151 | return 0; 152 | } 153 | switch (l->kind) { 154 | case TYPE_ARRAY: { 155 | const ArrayType *larray = (const ArrayType *) l; 156 | const ArrayType *rarray = (const ArrayType *) r; 157 | return larray->base == rarray->base && larray->length == rarray->length; 158 | } 159 | case TYPE_PROC: { 160 | const ProcType *lproc = (const ProcType *) l; 161 | const ProcType *rproc = (const ProcType *) r; 162 | return type_list_equal(lproc->params, rproc->params); 163 | } 164 | default: 165 | return 1; 166 | } 167 | } 168 | 169 | Ctx *ctx_new(void) 170 | { 171 | Ctx *ctx = xmalloc(sizeof(Ctx)); 172 | ctx->strings = map_new(&string_hash, &string_equal); 173 | ctx->type_lists = map_new(&type_list_hash, &type_list_equal); 174 | ctx->types = map_new(&type_hash, &type_equal); 175 | ctx->defs = array_new(sizeof(Def *)); 176 | ctx->resolved = map_new(NULL, NULL); 177 | ctx->syntax_type = map_new(NULL, NULL); 178 | 179 | { 180 | MapIndex index; 181 | 182 | map_entry(ctx->type_lists, (void *) &CTX_TYPE_LIST_EMPTY, &index); 183 | map_update(ctx->type_lists, &index, (void *) &CTX_TYPE_LIST_EMPTY, NULL); 184 | 185 | map_entry(ctx->types, (void *) &CTX_TYPE_BOOLEAN, &index); 186 | map_update(ctx->types, &index, (void *) &CTX_TYPE_BOOLEAN, NULL); 187 | 188 | map_entry(ctx->types, (void *) &CTX_TYPE_CHAR, &index); 189 | map_update(ctx->types, &index, (void *) &CTX_TYPE_CHAR, NULL); 190 | 191 | map_entry(ctx->types, (void *) &CTX_TYPE_INTEGER, &index); 192 | map_update(ctx->types, &index, (void *) &CTX_TYPE_INTEGER, NULL); 193 | 194 | map_entry(ctx->types, (void *) &CTX_TYPE_STRING, &index); 195 | map_update(ctx->types, &index, (void *) &CTX_TYPE_STRING, NULL); 196 | } 197 | 198 | return ctx; 199 | } 200 | 201 | void ctx_free(Ctx *ctx) 202 | { 203 | if (ctx) { 204 | unsigned long i; 205 | MapIndex index; 206 | 207 | for (map_iterator(ctx->strings, &index); map_next(ctx->strings, &index);) { 208 | if (map_value(ctx->strings, &index)) { 209 | String *string = map_key(ctx->strings, &index); 210 | free((void *) string->data); 211 | free(string); 212 | } 213 | } 214 | map_free(ctx->strings); 215 | 216 | for (map_iterator(ctx->type_lists, &index); map_next(ctx->type_lists, &index);) { 217 | if (map_value(ctx->type_lists, &index)) { 218 | TypeList *list = map_key(ctx->type_lists, &index); 219 | free(list->types); 220 | free(list); 221 | } 222 | } 223 | map_free(ctx->type_lists); 224 | 225 | for (map_iterator(ctx->types, &index); map_next(ctx->types, &index);) { 226 | if (map_value(ctx->types, &index)) { 227 | Type *type = map_key(ctx->types, &index); 228 | free(type); 229 | } 230 | } 231 | map_free(ctx->types); 232 | 233 | for (i = 0; i < array_count(ctx->defs); i++) { 234 | Def *def = *(Def **) array_at(ctx->defs, i); 235 | syntax_tree_unref(def->syntax); 236 | free(def); 237 | } 238 | array_free(ctx->defs); 239 | 240 | map_free(ctx->resolved); 241 | map_free(ctx->syntax_type); 242 | free(ctx); 243 | } 244 | } 245 | 246 | const String *ctx_string(Ctx *ctx, const char *data, unsigned long length) 247 | { 248 | MapIndex index; 249 | 250 | String string; 251 | string.data = data; 252 | string.length = length; 253 | 254 | if (map_entry(ctx->strings, &string, &index)) { 255 | return map_key(ctx->strings, &index); 256 | } else { 257 | String *instance = xmalloc(sizeof(String)); 258 | char *ndata = xmalloc(length + 1); 259 | 260 | memcpy(ndata, data, length); 261 | ndata[length] = '\0'; 262 | 263 | instance->length = length; 264 | instance->data = ndata; 265 | map_update(ctx->strings, &index, instance, instance); 266 | return instance; 267 | } 268 | } 269 | 270 | const Type *ctx_type(TypeKind kind) 271 | { 272 | switch (kind) { 273 | case TYPE_BOOLEAN: 274 | return &CTX_TYPE_BOOLEAN; 275 | case TYPE_CHAR: 276 | return &CTX_TYPE_CHAR; 277 | case TYPE_INTEGER: 278 | return &CTX_TYPE_INTEGER; 279 | case TYPE_STRING: 280 | return &CTX_TYPE_STRING; 281 | default: 282 | unreachable(); 283 | } 284 | } 285 | 286 | const Type *ctx_array_type(Ctx *ctx, const Type *base, unsigned long length) 287 | { 288 | MapIndex index; 289 | 290 | ArrayType type; 291 | type.kind = TYPE_ARRAY; 292 | type.base = base; 293 | type.length = length; 294 | 295 | if (map_entry(ctx->types, &type, &index)) { 296 | return map_key(ctx->types, &index); 297 | } else { 298 | ArrayType *instance = dup(&type, sizeof(ArrayType), 1); 299 | map_update(ctx->types, &index, instance, instance); 300 | return (Type *) instance; 301 | } 302 | } 303 | 304 | const Type *ctx_proc_type(Ctx *ctx, const TypeList *params) 305 | { 306 | MapIndex index; 307 | 308 | ProcType type; 309 | type.kind = TYPE_PROC; 310 | type.params = params; 311 | 312 | if (map_entry(ctx->types, &type, &index)) { 313 | return map_key(ctx->types, &index); 314 | } else { 315 | ProcType *instance = dup(&type, sizeof(ProcType), 1); 316 | map_update(ctx->types, &index, instance, instance); 317 | return (Type *) instance; 318 | } 319 | } 320 | 321 | const TypeList *ctx_type_list(Ctx *ctx, const Type **types, unsigned long length) 322 | { 323 | const Type **copy = dup(types, sizeof(Type *), length); 324 | return ctx_take_type_list(ctx, copy, length); 325 | } 326 | 327 | const TypeList *ctx_take_type_list(Ctx *ctx, const Type **types, unsigned long length) 328 | { 329 | if (length == 0) { 330 | return &CTX_TYPE_LIST_EMPTY; 331 | } else { 332 | MapIndex index; 333 | 334 | TypeList list; 335 | list.types = types; 336 | list.length = length; 337 | 338 | if (map_entry(ctx->type_lists, &list, &index)) { 339 | free(types); 340 | return map_key(ctx->type_lists, &index); 341 | } else { 342 | TypeList *instance = dup(&list, sizeof(TypeList), 1); 343 | map_update(ctx->type_lists, &index, instance, instance); 344 | return instance; 345 | } 346 | } 347 | } 348 | 349 | const Def *ctx_define(Ctx *ctx, DefKind kind, const String *name, const SyntaxTree *syntax) 350 | { 351 | Def *def = xmalloc(sizeof(Def)); 352 | def->kind = kind; 353 | def->name = name; 354 | def->syntax = syntax_tree_ref(syntax); 355 | array_push(ctx->defs, &def); 356 | return def; 357 | } 358 | 359 | const Def *ctx_resolve(Ctx *ctx, const SyntaxTree *syntax, const Def *def) 360 | { 361 | MapIndex index; 362 | 363 | const RawSyntaxNode *node = syntax_tree_raw(syntax); 364 | if (def) { 365 | if (map_entry(ctx->resolved, (void *) node, &index)) { 366 | unreachable(); 367 | } else { 368 | map_update(ctx->resolved, &index, (void *) node, (void *) def); 369 | return def; 370 | } 371 | } else { 372 | if (map_entry(ctx->resolved, (void *) node, &index)) { 373 | return map_value(ctx->resolved, &index); 374 | } else { 375 | return NULL; 376 | } 377 | } 378 | } 379 | 380 | const Type *ctx_type_of(const Ctx *ctx, const SyntaxTree *syntax, const Type *type) 381 | { 382 | MapIndex index; 383 | 384 | const RawSyntaxNode *node = syntax_tree_raw(syntax); 385 | if (type) { 386 | if (map_entry(ctx->syntax_type, (void *) node, &index)) { 387 | unreachable(); 388 | } else { 389 | map_update(ctx->syntax_type, &index, (void *) node, (void *) type); 390 | return type; 391 | } 392 | } else { 393 | if (map_entry(ctx->syntax_type, (void *) node, &index)) { 394 | return map_value(ctx->syntax_type, &index); 395 | } else { 396 | return NULL; 397 | } 398 | } 399 | } 400 | 401 | const char *string_data(const String *string) 402 | { 403 | return string->data; 404 | } 405 | 406 | unsigned long string_length(const String *string) 407 | { 408 | return string->length; 409 | } 410 | 411 | const Type *type_list_at(const TypeList *list, unsigned long index) 412 | { 413 | return list->types[index]; 414 | } 415 | 416 | unsigned long type_list_count(const TypeList *list) 417 | { 418 | return list->length; 419 | } 420 | 421 | TypeKind type_kind(const Type *type) 422 | { 423 | return type->kind; 424 | } 425 | 426 | int type_is_std(const Type *type) 427 | { 428 | return type->kind == TYPE_BOOLEAN || type->kind == TYPE_CHAR || type->kind == TYPE_INTEGER; 429 | } 430 | 431 | static unsigned long type_to_string_core(FILE *stream, const Type *type) 432 | { 433 | switch (type->kind) { 434 | case TYPE_BOOLEAN: 435 | return fprintf(stream, "boolean"); 436 | case TYPE_CHAR: 437 | return fprintf(stream, "char"); 438 | case TYPE_INTEGER: 439 | return fprintf(stream, "integer"); 440 | case TYPE_STRING: 441 | return fprintf(stream, "string"); 442 | case TYPE_ARRAY: { 443 | const ArrayType *array = (const ArrayType *) type; 444 | unsigned long count = fprintf(stream, "array[%lu] of", array->length); 445 | count += type_to_string_core(stream, array->base); 446 | return count; 447 | } 448 | case TYPE_PROC: { 449 | const ProcType *proc = (const ProcType *) type; 450 | unsigned long count = fprintf(stream, "procedure("); 451 | unsigned long i; 452 | for (i = 0; i < proc->params->length; i++) { 453 | if (i > 0) { 454 | count += fprintf(stream, ", "); 455 | } 456 | count += type_to_string_core(stream, proc->params->types[i]); 457 | } 458 | count += fprintf(stream, ")"); 459 | return count; 460 | } 461 | default: 462 | unreachable(); 463 | } 464 | } 465 | 466 | char *type_to_string(const Type *type) 467 | { 468 | FILE *stream = tmpfile(); 469 | unsigned long length = type_to_string_core(stream, type); 470 | char *result = xmalloc(length + 1); 471 | fseek(stream, 0, SEEK_SET); 472 | fread(result, 1, length, stream); 473 | result[length] = '\0'; 474 | fclose(stream); 475 | return result; 476 | } 477 | 478 | const Type *array_type_base(const ArrayType *type) 479 | { 480 | return type->base; 481 | } 482 | 483 | unsigned long array_type_length(const ArrayType *type) 484 | { 485 | return type->length; 486 | } 487 | 488 | const TypeList *proc_type_params(const ProcType *type) 489 | { 490 | return type->params; 491 | } 492 | 493 | DefKind def_kind(const Def *def) 494 | { 495 | return def->kind; 496 | } 497 | 498 | const String *def_name(const Def *def) 499 | { 500 | return def->name; 501 | } 502 | 503 | const SyntaxTree *def_syntax(const Def *def) 504 | { 505 | return def->syntax; 506 | } 507 | -------------------------------------------------------------------------------- /src/context.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef CONTEXT_H 18 | #define CONTEXT_H 19 | 20 | #include "context_fwd.h" 21 | #include "syntax_tree.h" 22 | 23 | Ctx *ctx_new(void); 24 | void ctx_free(Ctx *ctx); 25 | const String *ctx_string(Ctx *ctx, const char *data, unsigned long length); 26 | const Type *ctx_array_type(Ctx *ctx, const Type *base, unsigned long length); 27 | const Type *ctx_proc_type(Ctx *ctx, const TypeList *params); 28 | const Type *ctx_type(TypeKind kind); 29 | const TypeList *ctx_type_list(Ctx *ctx, const Type **types, unsigned long length); 30 | const TypeList *ctx_take_type_list(Ctx *ctx, const Type **types, unsigned long length); 31 | const Def *ctx_define(Ctx *ctx, DefKind kind, const String *name, const SyntaxTree *syntax); 32 | const Def *ctx_resolve(Ctx *ctx, const SyntaxTree *syntax, const Def *def); 33 | const Type *ctx_type_of(const Ctx *ctx, const SyntaxTree *syntax, const Type *type); 34 | 35 | const char *string_data(const String *string); 36 | unsigned long string_length(const String *string); 37 | 38 | const Type *type_list_at(const TypeList *list, unsigned long index); 39 | unsigned long type_list_count(const TypeList *list); 40 | 41 | TypeKind type_kind(const Type *type); 42 | int type_is_std(const Type *type); 43 | char *type_to_string(const Type *type); 44 | 45 | const Type *array_type_base(const ArrayType *type); 46 | unsigned long array_type_length(const ArrayType *type); 47 | 48 | const TypeList *proc_type_params(const ProcType *type); 49 | 50 | DefKind def_kind(const Def *def); 51 | const String *def_name(const Def *def); 52 | const SyntaxTree *def_syntax(const Def *def); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/context_fwd.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef CONTEXT_FWD_H 18 | #define CONTEXT_FWD_H 19 | 20 | typedef enum { 21 | TYPE_BOOLEAN, 22 | TYPE_CHAR, 23 | TYPE_INTEGER, 24 | TYPE_STRING, 25 | TYPE_ARRAY, 26 | TYPE_PROC 27 | } TypeKind; 28 | 29 | typedef enum { 30 | DEF_PROGRAM, 31 | DEF_PROC, 32 | DEF_VAR, 33 | DEF_PARAM, 34 | DEF_LOCAL 35 | } DefKind; 36 | 37 | typedef struct String String; 38 | typedef struct TypeList TypeList; 39 | typedef struct Type Type; 40 | typedef struct ArrayType ArrayType; 41 | typedef struct ProcType ProcType; 42 | typedef struct Def Def; 43 | typedef struct Ctx Ctx; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/lexer.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include "compiler.h" 21 | #include "source.h" 22 | #include "syntax_kind.h" 23 | #include "utility.h" 24 | 25 | typedef struct Lexer Lexer; 26 | 27 | struct Lexer { 28 | const Source *source; 29 | unsigned long offset; 30 | unsigned long index; 31 | }; 32 | 33 | static void bump(Lexer *lexer) 34 | { 35 | if (lexer->offset + lexer->index < lexer->source->text_length) { 36 | ++lexer->index; 37 | } 38 | } 39 | 40 | static int first(Lexer *lexer) 41 | { 42 | return lexer->offset + lexer->index < lexer->source->text_length 43 | ? lexer->source->text[lexer->offset + lexer->index] 44 | : EOF; 45 | } 46 | 47 | static int eat(Lexer *lexer, int c) 48 | { 49 | int result = first(lexer) == c; 50 | if (result) { 51 | bump(lexer); 52 | } 53 | return result; 54 | } 55 | 56 | static int eat_if(Lexer *lexer, int (*predicate)(int)) 57 | { 58 | int result = predicate(first(lexer)); 59 | if (result) { 60 | bump(lexer); 61 | } 62 | return result; 63 | } 64 | 65 | static LexStatus tokenize(Lexer *lexer, SyntaxKind kind, LexedToken *lexed) 66 | { 67 | lexed->kind = kind; 68 | lexed->offset = lexer->offset; 69 | lexed->length = lexer->index; 70 | 71 | lexer->offset += lexer->index; 72 | lexer->index = 0; 73 | return LEX_OK; 74 | } 75 | 76 | static LexStatus token_unexpected(Lexer *lexer, LexedToken *lexed) 77 | { 78 | bump(lexer); 79 | tokenize(lexer, SYNTAX_BAD_TOKEN, lexed); 80 | return LEX_ERROR_STRAY_CHAR; 81 | } 82 | 83 | static LexStatus token_identifier_and_keyword(Lexer *lexer, LexedToken *lexed) 84 | { 85 | if (eat_if(lexer, &is_alphabet)) { 86 | SyntaxKind kind; 87 | while (eat_if(lexer, &is_alphabet) || eat_if(lexer, &is_number)) { } 88 | kind = syntax_kind_from_keyword(lexer->source->text + lexer->offset, lexer->index); 89 | return tokenize(lexer, kind != SYNTAX_BAD_TOKEN ? kind : SYNTAX_IDENT_TOKEN, lexed); 90 | } else { 91 | return token_unexpected(lexer, lexed); 92 | } 93 | } 94 | 95 | static LexStatus token_integer(Lexer *lexer, LexedToken *lexed) 96 | { 97 | if (eat_if(lexer, &is_number)) { 98 | while (eat_if(lexer, &is_number)) { } 99 | 100 | if (strtoul(lexer->source->text + lexer->offset, NULL, 10) > 32768) { 101 | tokenize(lexer, SYNTAX_BAD_TOKEN, lexed); 102 | return LEX_ERROR_TOO_BIG_NUMBER; 103 | } else { 104 | return tokenize(lexer, SYNTAX_NUMBER_LIT, lexed); 105 | } 106 | } else { 107 | return token_unexpected(lexer, lexed); 108 | } 109 | } 110 | 111 | static LexStatus token_string(Lexer *lexer, LexedToken *lexed) 112 | { 113 | if (eat(lexer, '\'')) { 114 | int contain_non_graphic = 0; 115 | while (1) { 116 | if (eat(lexer, '\'') && !eat(lexer, '\'')) { 117 | if (contain_non_graphic) { 118 | tokenize(lexer, SYNTAX_BAD_TOKEN, lexed); 119 | return LEX_ERROR_NONGRAPHIC_CHAR; 120 | } else { 121 | return tokenize(lexer, SYNTAX_STRING_LIT, lexed); 122 | } 123 | } else if (first(lexer) == '\r' || first(lexer) == '\n' || first(lexer) == EOF) { 124 | tokenize(lexer, SYNTAX_BAD_TOKEN, lexed); 125 | return LEX_ERROR_UNTERMINATED_STRING; 126 | } else if (!eat_if(lexer, &is_graphic)) { 127 | contain_non_graphic = 1; 128 | bump(lexer); 129 | } 130 | } 131 | } else { 132 | return token_unexpected(lexer, lexed); 133 | } 134 | } 135 | 136 | static LexStatus token_whitespace(Lexer *lexer, LexedToken *lexed) 137 | { 138 | if (eat_if(lexer, &is_space)) { 139 | while (eat_if(lexer, &is_space)) { } 140 | return tokenize(lexer, SYNTAX_SPACE_TRIVIA, lexed); 141 | } else { 142 | return token_unexpected(lexer, lexed); 143 | } 144 | } 145 | 146 | static LexStatus token_comment(Lexer *lexer, LexedToken *lexed) 147 | { 148 | if (eat(lexer, '{')) { 149 | while (1) { 150 | if (eat(lexer, '}')) { 151 | return tokenize(lexer, SYNTAX_BRACES_COMMENT_TRIVIA, lexed); 152 | } else if (first(lexer) == EOF) { 153 | tokenize(lexer, SYNTAX_BAD_TOKEN, lexed); 154 | return LEX_ERROR_UNTERMINATED_COMMENT; 155 | } else { 156 | bump(lexer); 157 | } 158 | } 159 | } else if (eat(lexer, '/')) { 160 | if (eat(lexer, '*')) { 161 | while (1) { 162 | if (eat(lexer, '*') && eat(lexer, '/')) { 163 | return tokenize(lexer, SYNTAX_C_COMMENT_TRIVIA, lexed); 164 | } else if (first(lexer) == EOF) { 165 | tokenize(lexer, SYNTAX_BAD_TOKEN, lexed); 166 | return LEX_ERROR_UNTERMINATED_COMMENT; 167 | } else { 168 | bump(lexer); 169 | } 170 | } 171 | } else { 172 | return token_unexpected(lexer, lexed); 173 | } 174 | } else { 175 | return token_unexpected(lexer, lexed); 176 | } 177 | } 178 | 179 | static LexStatus token_symbol(Lexer *lexer, LexedToken *lexed) 180 | { 181 | if (eat(lexer, '+')) { 182 | return tokenize(lexer, SYNTAX_PLUS_TOKEN, lexed); 183 | } else if (eat(lexer, '-')) { 184 | return tokenize(lexer, SYNTAX_MINUS_TOKEN, lexed); 185 | } else if (eat(lexer, '*')) { 186 | return tokenize(lexer, SYNTAX_STAR_TOKEN, lexed); 187 | } else if (eat(lexer, '=')) { 188 | return tokenize(lexer, SYNTAX_EQUAL_TOKEN, lexed); 189 | } else if (eat(lexer, '<')) { 190 | if (eat(lexer, '>')) { 191 | return tokenize(lexer, SYNTAX_NOTEQ_TOKEN, lexed); 192 | } else if (eat(lexer, '=')) { 193 | return tokenize(lexer, SYNTAX_LESSEQ_TOKEN, lexed); 194 | } else { 195 | return tokenize(lexer, SYNTAX_LESS_TOKEN, lexed); 196 | } 197 | } else if (eat(lexer, '>')) { 198 | if (eat(lexer, '=')) { 199 | return tokenize(lexer, SYNTAX_GREATEREQ_TOKEN, lexed); 200 | } else { 201 | return tokenize(lexer, SYNTAX_GREATER_TOKEN, lexed); 202 | } 203 | } else if (eat(lexer, '(')) { 204 | return tokenize(lexer, SYNTAX_LPAREN_TOKEN, lexed); 205 | } else if (eat(lexer, ')')) { 206 | return tokenize(lexer, SYNTAX_RPAREN_TOKEN, lexed); 207 | } else if (eat(lexer, '[')) { 208 | return tokenize(lexer, SYNTAX_LBRACKET_TOKEN, lexed); 209 | } else if (eat(lexer, ']')) { 210 | return tokenize(lexer, SYNTAX_RBRACKET_TOKEN, lexed); 211 | } else if (eat(lexer, ':')) { 212 | if (eat(lexer, '=')) { 213 | return tokenize(lexer, SYNTAX_ASSIGN_TOKEN, lexed); 214 | } else { 215 | return tokenize(lexer, SYNTAX_COLON_TOKEN, lexed); 216 | } 217 | } else if (eat(lexer, '.')) { 218 | return tokenize(lexer, SYNTAX_DOT_TOKEN, lexed); 219 | } else if (eat(lexer, ',')) { 220 | return tokenize(lexer, SYNTAX_COMMA_TOKEN, lexed); 221 | } else if (eat(lexer, ';')) { 222 | return tokenize(lexer, SYNTAX_SEMI_TOKEN, lexed); 223 | } else { 224 | return token_unexpected(lexer, lexed); 225 | } 226 | } 227 | 228 | LexStatus mpplc_lex(const Source *source, unsigned long offset, LexedToken *lexed) 229 | { 230 | Lexer lexer; 231 | lexer.source = source; 232 | lexer.offset = offset; 233 | lexer.index = 0; 234 | 235 | if (first(&lexer) == EOF) { 236 | tokenize(&lexer, SYNTAX_EOF_TOKEN, lexed); 237 | return LEX_EOF; 238 | } else if (is_alphabet(first(&lexer))) { 239 | return token_identifier_and_keyword(&lexer, lexed); 240 | } else if (is_number(first(&lexer))) { 241 | return token_integer(&lexer, lexed); 242 | } else if (first(&lexer) == '\'') { 243 | return token_string(&lexer, lexed); 244 | } else if (is_space(first(&lexer))) { 245 | return token_whitespace(&lexer, lexed); 246 | } else if (first(&lexer) == '{' || first(&lexer) == '/') { 247 | return token_comment(&lexer, lexed); 248 | } else { 249 | return token_symbol(&lexer, lexed); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "array.h" 23 | #include "compiler.h" 24 | #include "context.h" 25 | #include "mppl_syntax.h" 26 | #include "source.h" 27 | 28 | const char *program; 29 | Array *filenames = NULL; 30 | 31 | int dump_syntax = 0; 32 | int pretty_print = 0; 33 | int syntax_only = 0; 34 | int emit_llvm = 0; 35 | int emit_casl2 = 0; 36 | 37 | static int run_compiler(void) 38 | { 39 | unsigned long i; 40 | int result = EXIT_SUCCESS; 41 | 42 | for (i = 0; i < array_count(filenames); ++i) { 43 | const char *filename = *(const char **) array_at(filenames, i); 44 | Ctx *ctx = ctx_new(); 45 | Source *source = source_new(filename, strlen(filename)); 46 | MpplProgram *syntax = NULL; 47 | 48 | if (!source) { 49 | fprintf(stderr, "Cannot open file: %s\n", filename); 50 | result = EXIT_FAILURE; 51 | continue; 52 | } 53 | 54 | if (mpplc_parse(source, ctx, &syntax)) { 55 | if (dump_syntax) { 56 | mpplc_dump_syntax(syntax); 57 | } 58 | 59 | if (pretty_print) { 60 | mpplc_pretty_print(syntax, NULL); 61 | } 62 | 63 | if (mpplc_resolve(source, syntax, ctx) && mpplc_check(source, syntax, ctx)) { 64 | if (!syntax_only) { 65 | if (emit_casl2) { 66 | mpplc_codegen_casl2(source, syntax, ctx); 67 | } 68 | 69 | if (emit_llvm) { 70 | mpplc_codegen_llvm_ir(source, syntax, ctx); 71 | } 72 | } 73 | } 74 | } 75 | 76 | mppl_unref(syntax); 77 | source_free(source); 78 | ctx_free(ctx); 79 | } 80 | return result; 81 | } 82 | 83 | static void print_help(void) 84 | { 85 | printf( 86 | "Usage: %s [OPTIONS] INPUT\n" 87 | "Options:\n" 88 | " --dump-syntax Dump syntax tree\n" 89 | " --pretty-print Pretty print the input file\n" 90 | " --syntax-only Check syntax only\n" 91 | " --emit-llvm Emit LLVM IR\n" 92 | " --emit-casl2 Emit CASL2\n" 93 | " --help Print this help message\n", 94 | program); 95 | fflush(stdout); 96 | } 97 | 98 | static void deinit(void) 99 | { 100 | array_free(filenames); 101 | filenames = NULL; 102 | } 103 | 104 | static void init(int argc, const char **argv) 105 | { 106 | int i; 107 | int stop = 0; 108 | int status = EXIT_SUCCESS; 109 | 110 | program = argv[0]; 111 | filenames = array_new(sizeof(const char *)); 112 | 113 | if (argc < 2) { 114 | print_help(); 115 | stop = 1; 116 | status = EXIT_FAILURE; 117 | } else { 118 | for (i = 1; i < argc; ++i) { 119 | if (strcmp(argv[i], "--dump-syntax") == 0) { 120 | dump_syntax = 1; 121 | } else if (strcmp(argv[i], "--pretty-print") == 0) { 122 | pretty_print = 1; 123 | } else if (strcmp(argv[i], "--syntax-only") == 0) { 124 | syntax_only = 1; 125 | } else if (strcmp(argv[i], "--emit-llvm") == 0) { 126 | emit_llvm = 1; 127 | } else if (strcmp(argv[i], "--emit-casl2") == 0) { 128 | emit_casl2 = 1; 129 | } else if (strcmp(argv[i], "--help") == 0) { 130 | print_help(); 131 | stop = 1; 132 | status = EXIT_SUCCESS; 133 | } else if (strcmp(argv[i], "--") == 0) { 134 | for (++i; i < argc; ++i) { 135 | array_push(filenames, &argv[i]); 136 | } 137 | } else if (argv[i][0] == '-') { 138 | fprintf(stderr, "Unknown option: %s\n", argv[i]); 139 | print_help(); 140 | stop = 1; 141 | status = EXIT_FAILURE; 142 | } else { 143 | array_push(filenames, &argv[i]); 144 | } 145 | } 146 | } 147 | 148 | if (!emit_llvm && !emit_casl2) { 149 | emit_casl2 = 1; 150 | } 151 | 152 | if (stop) { 153 | deinit(); 154 | exit(status); 155 | } 156 | } 157 | 158 | int main(int argc, const char **argv) 159 | { 160 | int status; 161 | 162 | char *mode = getenv("MPPLC_MODE"); 163 | if (mode) { 164 | if (strcmp(mode, "TASK1") == 0) { 165 | return mpplc_task1(argc, argv); 166 | } else if (strcmp(mode, "TASK2") == 0) { 167 | return mpplc_task2(argc, argv); 168 | } else if (strcmp(mode, "TASK3") == 0) { 169 | return mpplc_task3(argc, argv); 170 | } else if (strcmp(mode, "TASK4") == 0) { 171 | return mpplc_task4(argc, argv); 172 | } 173 | } 174 | 175 | init(argc, argv); 176 | status = run_compiler(); 177 | deinit(); 178 | return status; 179 | } 180 | -------------------------------------------------------------------------------- /src/map.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "map.h" 22 | #include "utility.h" 23 | 24 | #define NEIGHBORHOOD ((long) sizeof(unsigned long) * CHAR_BIT) 25 | 26 | struct MapBucket { 27 | unsigned long hop; 28 | void *key; 29 | void *value; 30 | }; 31 | 32 | struct Map { 33 | unsigned long count; 34 | unsigned long mask; 35 | MapBucket *buckets; 36 | MapHasher *hasher; 37 | MapComparator *comparator; 38 | }; 39 | 40 | static unsigned long map_default_hasher(const void *value) 41 | { 42 | return fnv1a(FNV1A_INIT, &value, sizeof(value)); 43 | } 44 | 45 | static int map_default_comparator(const void *left, const void *right) 46 | { 47 | return left == right; 48 | } 49 | 50 | Map *map_new(MapHasher *hasher, MapComparator *comparator) 51 | { 52 | return map_new_with_capacity(1l << 4, hasher, comparator); 53 | } 54 | 55 | Map *map_new_with_capacity(unsigned long capacity, MapHasher *hasher, MapComparator *comparator) 56 | { 57 | Map *map = xmalloc(sizeof(Map)); 58 | map->count = 0; 59 | map->mask = 0; 60 | map->buckets = NULL; 61 | map->hasher = hasher ? hasher : &map_default_hasher; 62 | map->comparator = comparator ? comparator : &map_default_comparator; 63 | map_reserve(map, capacity); 64 | return map; 65 | } 66 | 67 | void map_free(Map *map) 68 | { 69 | if (map) { 70 | free(map->buckets); 71 | free(map); 72 | } 73 | } 74 | 75 | unsigned long map_count(Map *map) 76 | { 77 | return map->count; 78 | } 79 | 80 | static void map_index_init(MapIndex *index, Map *map, void *key) 81 | { 82 | index->_bucket = map->buckets + (map->hasher(key) & map->mask); 83 | index->_slot = NULL; 84 | } 85 | 86 | void map_reserve(Map *map, unsigned long capacity) 87 | { 88 | unsigned long i; 89 | unsigned long new_mask = capacity - 1; 90 | 91 | for (i = 1; i < sizeof(new_mask); i <<= 1) { 92 | new_mask |= new_mask >> i; 93 | } 94 | 95 | if (new_mask > map->mask) { 96 | MapBucket *old_buckets = map->buckets; 97 | unsigned long old_mask = map->mask; 98 | unsigned long hop = 0; 99 | 100 | map->count = 0; 101 | map->mask = new_mask; 102 | map->buckets = xmalloc(sizeof(MapBucket) * (new_mask + NEIGHBORHOOD)); 103 | for (i = 0; i < new_mask + NEIGHBORHOOD; ++i) { 104 | map->buckets[i].hop = 0; 105 | map->buckets[i].key = NULL; 106 | map->buckets[i].value = NULL; 107 | } 108 | 109 | if (old_buckets) { 110 | for (i = 0; i < old_mask + NEIGHBORHOOD; ++i) { 111 | hop = (hop >> 1) | old_buckets[i].hop; 112 | if (hop & 1) { 113 | MapIndex index; 114 | map_index_init(&index, map, old_buckets[i].key); 115 | map_update(map, &index, old_buckets[i].key, old_buckets[i].value); 116 | } 117 | } 118 | free(old_buckets); 119 | } 120 | } 121 | } 122 | 123 | int map_entry(Map *map, void *key, MapIndex *index) 124 | { 125 | unsigned long i; 126 | map_index_init(index, map, key); 127 | for (i = 0; i < NEIGHBORHOOD; ++i) { 128 | if (index->_bucket->hop & (1ul << i) && map->comparator(key, index->_bucket[i].key)) { 129 | index->_slot = index->_bucket + i; 130 | break; 131 | } 132 | } 133 | return !!index->_slot; 134 | } 135 | 136 | void map_iterator(Map *map, MapIndex *index) 137 | { 138 | index->_bucket = map->buckets; 139 | index->_slot = NULL; 140 | } 141 | 142 | int map_next(Map *map, MapIndex *index) 143 | { 144 | unsigned long i; 145 | if (index->_slot) { 146 | ++index->_slot; 147 | } else { 148 | index->_slot = index->_bucket; 149 | } 150 | 151 | for (i = index->_slot - index->_bucket; i < NEIGHBORHOOD; ++i) { 152 | if (index->_bucket->hop & (1ul << i)) { 153 | index->_slot = index->_bucket + i; 154 | return 1; 155 | } 156 | } 157 | 158 | ++index->_bucket; 159 | for (; index->_bucket < map->buckets + map->mask + NEIGHBORHOOD; ++index->_bucket) { 160 | if (index->_bucket->hop) { 161 | for (i = 0; i < NEIGHBORHOOD; ++i) { 162 | if (index->_bucket->hop & (1ul << i)) { 163 | index->_slot = index->_bucket + i; 164 | return 1; 165 | } 166 | } 167 | } 168 | } 169 | 170 | index->_bucket = NULL; 171 | index->_slot = NULL; 172 | return 0; 173 | } 174 | 175 | void *map_key(Map *map, MapIndex *index) 176 | { 177 | return index->_slot ? index->_slot->key : NULL; 178 | } 179 | 180 | void *map_value(Map *map, MapIndex *index) 181 | { 182 | return index->_slot ? index->_slot->value : NULL; 183 | } 184 | 185 | void map_update(Map *map, MapIndex *index, void *key, void *value) 186 | { 187 | MapBucket *empty = index->_slot; 188 | 189 | if (!empty) { 190 | long i = index->_bucket - map->buckets < NEIGHBORHOOD 191 | ? -(index->_bucket - map->buckets) 192 | : -NEIGHBORHOOD + 1; 193 | 194 | unsigned long hop = 0; 195 | for (; i < NEIGHBORHOOD * 8; ++i) { 196 | hop = (hop >> 1) | index->_bucket[i].hop; 197 | if (i >= 0 && !(hop & 1)) { 198 | empty = index->_bucket + i; 199 | break; 200 | } 201 | } 202 | 203 | if (empty) { 204 | while (empty - index->_bucket >= NEIGHBORHOOD) { 205 | MapBucket *bucket = empty - NEIGHBORHOOD + 2; 206 | for (; bucket < empty; ++bucket) { 207 | if (bucket->hop & ((1ul << (empty - bucket + 1)) - 1)) { 208 | MapBucket *next = bucket; 209 | for (; next < empty; ++next) { 210 | if (bucket->hop & (1ul << (next - bucket))) { 211 | break; 212 | } 213 | } 214 | 215 | bucket->hop &= ~(1ul << (next - bucket)); 216 | bucket->hop |= 1ul << (empty - bucket); 217 | empty->key = next->key; 218 | empty->value = next->value; 219 | empty = next; 220 | break; 221 | } 222 | } 223 | if (bucket == empty) { 224 | empty = NULL; 225 | break; 226 | } 227 | } 228 | } 229 | } 230 | 231 | if (empty) { 232 | empty->key = key; 233 | empty->value = value; 234 | index->_bucket->hop |= 1ul << (empty - index->_bucket); 235 | index->_slot = empty; 236 | ++map->count; 237 | } else { 238 | map_reserve(map, (map->mask + 1) << 1); 239 | map_index_init(index, map, key); 240 | map_update(map, index, key, value); 241 | } 242 | } 243 | 244 | void map_erase(Map *map, MapIndex *index) 245 | { 246 | if (index->_slot) { 247 | index->_bucket->hop &= ~(1ul << (index->_slot - index->_bucket)); 248 | index->_slot = NULL; 249 | --map->count; 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/map.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef MAP_H 18 | #define MAP_H 19 | 20 | typedef unsigned long MapHasher(const void *); 21 | typedef int MapComparator(const void *, const void *); 22 | typedef struct MapBucket MapBucket; 23 | typedef struct MapIndex MapIndex; 24 | typedef struct Map Map; 25 | 26 | struct MapIndex { 27 | MapBucket *_bucket; 28 | MapBucket *_slot; 29 | }; 30 | 31 | Map *map_new(MapHasher *hasher, MapComparator *comparator); 32 | Map *map_new_with_capacity(unsigned long capacity, MapHasher *hasher, MapComparator *comparator); 33 | void map_free(Map *map); 34 | unsigned long map_count(Map *map); 35 | void map_reserve(Map *map, unsigned long capacity); 36 | int map_entry(Map *map, void *key, MapIndex *index); 37 | void map_iterator(Map *map, MapIndex *index); 38 | int map_next(Map *map, MapIndex *index); 39 | void *map_key(Map *map, MapIndex *index); 40 | void *map_value(Map *map, MapIndex *index); 41 | void map_update(Map *map, MapIndex *index, void *key, void *value); 42 | void map_erase(Map *map, MapIndex *index); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/mppl_syntax.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "mppl_syntax.h" 18 | #include "syntax_kind.h" 19 | #include "syntax_tree.h" 20 | #include "utility.h" 21 | 22 | static const SyntaxTree *syntax(const void *node) 23 | { 24 | return (const SyntaxTree *) node; 25 | } 26 | 27 | /* program */ 28 | 29 | MpplToken *mppl_program__program_token(const MpplProgram *program) 30 | { 31 | return (MpplToken *) syntax_tree_child(syntax(program), 0); 32 | } 33 | 34 | MpplToken *mppl_program__name(const MpplProgram *program) 35 | { 36 | return (MpplToken *) syntax_tree_child(syntax(program), 1); 37 | } 38 | 39 | MpplToken *mppl_program__semi_token(const MpplProgram *program) 40 | { 41 | return (MpplToken *) syntax_tree_child(syntax(program), 2); 42 | } 43 | 44 | unsigned long mppl_program__decl_part_count(const MpplProgram *program) 45 | { 46 | return syntax_tree_child_count(syntax(program)) - 6; 47 | } 48 | 49 | AnyMpplDeclPart *mppl_program__decl_part(const MpplProgram *program, unsigned long index) 50 | { 51 | return (AnyMpplDeclPart *) syntax_tree_child(syntax(program), 3 + index); 52 | } 53 | 54 | MpplCompStmt *mppl_program__stmt(const MpplProgram *program) 55 | { 56 | return (MpplCompStmt *) syntax_tree_child(syntax(program), syntax_tree_child_count(syntax(program)) - 3); 57 | } 58 | 59 | MpplToken *mppl_program__dot_token(const MpplProgram *program) 60 | { 61 | return (MpplToken *) syntax_tree_child(syntax(program), syntax_tree_child_count(syntax(program)) - 2); 62 | } 63 | 64 | MpplToken *mppl_program__eof_token(const MpplProgram *program) 65 | { 66 | return (MpplToken *) syntax_tree_child(syntax(program), syntax_tree_child_count(syntax(program)) - 1); 67 | } 68 | 69 | /* declaration part */ 70 | 71 | MpplDeclPartKind mppl_decl_part__kind(const AnyMpplDeclPart *part) 72 | { 73 | switch (syntax_tree_kind(syntax(part))) { 74 | case SYNTAX_VAR_DECL_PART: 75 | return MPPL_DECL_PART_VAR; 76 | case SYNTAX_PROC_DECL: 77 | return MPPL_DECL_PART_PROC; 78 | default: 79 | unreachable(); 80 | } 81 | } 82 | 83 | /* variable declaration part */ 84 | 85 | MpplToken *mppl_var_decl_part__var_token(const MpplVarDeclPart *part) 86 | { 87 | return (MpplToken *) syntax_tree_child(syntax(part), 0); 88 | } 89 | 90 | unsigned long mppl_var_decl_part__var_decl_count(const MpplVarDeclPart *part) 91 | { 92 | return (syntax_tree_child_count(syntax(part)) - 1) / 2; 93 | } 94 | 95 | MpplVarDecl *mppl_var_decl_part__var_decl(const MpplVarDeclPart *part, unsigned long index) 96 | { 97 | return (MpplVarDecl *) syntax_tree_child(syntax(part), 1 + 2 * index); 98 | } 99 | 100 | MpplToken *mppl_var_decl_part__semi_token(const MpplVarDeclPart *part, unsigned long index) 101 | { 102 | return (MpplToken *) syntax_tree_child(syntax(part), 2 + 2 * index); 103 | } 104 | 105 | /* variable declaration */ 106 | 107 | unsigned long mppl_var_decl__name_count(const MpplVarDecl *decl) 108 | { 109 | return (syntax_tree_child_count(syntax(decl)) - 1) / 2; 110 | } 111 | 112 | MpplToken *mppl_var_decl__name(const MpplVarDecl *decl, unsigned long index) 113 | { 114 | return (MpplToken *) syntax_tree_child(syntax(decl), index * 2); 115 | } 116 | 117 | MpplToken *mppl_var_decl__comma_token(const MpplVarDecl *decl, unsigned long index) 118 | { 119 | return (MpplToken *) syntax_tree_child(syntax(decl), 1 + index * 2); 120 | } 121 | 122 | MpplToken *mppl_var_decl__colon_token(const MpplVarDecl *decl) 123 | { 124 | return (MpplToken *) syntax_tree_child(syntax(decl), syntax_tree_child_count(syntax(decl)) - 2); 125 | } 126 | 127 | AnyMpplType *mppl_var_decl__type(const MpplVarDecl *decl) 128 | { 129 | return (AnyMpplType *) syntax_tree_child(syntax(decl), syntax_tree_child_count(syntax(decl)) - 1); 130 | } 131 | 132 | /* procedure declaration */ 133 | 134 | MpplToken *mppl_proc_decl__procedure_token(const MpplProcDecl *decl) 135 | { 136 | return (MpplToken *) syntax_tree_child(syntax(decl), 0); 137 | } 138 | 139 | MpplToken *mppl_proc_decl__name(const MpplProcDecl *decl) 140 | { 141 | return (MpplToken *) syntax_tree_child(syntax(decl), 1); 142 | } 143 | 144 | MpplFmlParamList *mppl_proc_decl__fml_param_list(const MpplProcDecl *decl) 145 | { 146 | return (MpplFmlParamList *) syntax_tree_child(syntax(decl), 2); 147 | } 148 | 149 | MpplToken *mppl_proc_decl__semi_token_0(const MpplProcDecl *decl) 150 | { 151 | return (MpplToken *) syntax_tree_child(syntax(decl), 3); 152 | } 153 | 154 | MpplVarDeclPart *mppl_proc_decl__var_decl_part(const MpplProcDecl *decl) 155 | { 156 | return (MpplVarDeclPart *) syntax_tree_child(syntax(decl), 4); 157 | } 158 | 159 | MpplCompStmt *mppl_proc_decl__comp_stmt(const MpplProcDecl *decl) 160 | { 161 | return (MpplCompStmt *) syntax_tree_child(syntax(decl), 5); 162 | } 163 | 164 | MpplToken *mppl_proc_decl__semi_token_1(const MpplProcDecl *decl) 165 | { 166 | return (MpplToken *) syntax_tree_child(syntax(decl), 6); 167 | } 168 | 169 | /* formal parameter list */ 170 | 171 | MpplToken *mppl_fml_param_list__lparen_token(const MpplFmlParamList *list) 172 | { 173 | return (MpplToken *) syntax_tree_child(syntax(list), 0); 174 | } 175 | 176 | unsigned long mppl_fml_param_list__sec_count(const MpplFmlParamList *list) 177 | { 178 | return (syntax_tree_child_count(syntax(list)) - 1) / 2; 179 | } 180 | 181 | MpplFmlParamSec *mppl_fml_param_list__sec(const MpplFmlParamList *list, unsigned long index) 182 | { 183 | return (MpplFmlParamSec *) syntax_tree_child(syntax(list), 1 + index * 2); 184 | } 185 | 186 | MpplToken *mppl_fml_param_list__semi_token(const MpplFmlParamList *list, unsigned long index) 187 | { 188 | return (MpplToken *) syntax_tree_child(syntax(list), 2 + index * 2); 189 | } 190 | 191 | MpplToken *mppl_fml_param_list__rparen_token(const MpplFmlParamList *list) 192 | { 193 | return (MpplToken *) syntax_tree_child(syntax(list), syntax_tree_child_count(syntax(list)) - 1); 194 | } 195 | 196 | /* formal parameter section */ 197 | 198 | unsigned long mppl_fml_param_sec__name_count(const MpplFmlParamSec *sec) 199 | { 200 | return (syntax_tree_child_count(syntax(sec)) - 1) / 2; 201 | } 202 | 203 | MpplToken *mppl_fml_param_sec__name(const MpplFmlParamSec *sec, unsigned long index) 204 | { 205 | return (MpplToken *) syntax_tree_child(syntax(sec), index * 2); 206 | } 207 | 208 | MpplToken *mppl_fml_param_sec__comma_token(const MpplFmlParamSec *sec, unsigned long index) 209 | { 210 | return (MpplToken *) syntax_tree_child(syntax(sec), 1 + index * 2); 211 | } 212 | 213 | MpplToken *mppl_fml_param_sec__colon_token(const MpplFmlParamSec *sec) 214 | { 215 | return (MpplToken *) syntax_tree_child(syntax(sec), syntax_tree_child_count(syntax(sec)) - 2); 216 | } 217 | 218 | AnyMpplType *mppl_fml_param_sec__type(const MpplFmlParamSec *sec) 219 | { 220 | return (AnyMpplType *) syntax_tree_child(syntax(sec), syntax_tree_child_count(syntax(sec)) - 1); 221 | } 222 | 223 | /* statement */ 224 | 225 | MpplStmtKind mppl_stmt__kind(const AnyMpplStmt *stmt) 226 | { 227 | switch (syntax_tree_kind(syntax(stmt))) { 228 | case SYNTAX_ASSIGN_STMT: 229 | return MPPL_STMT_ASSIGN; 230 | case SYNTAX_IF_STMT: 231 | return MPPL_STMT_IF; 232 | case SYNTAX_WHILE_STMT: 233 | return MPPL_STMT_WHILE; 234 | case SYNTAX_BREAK_STMT: 235 | return MPPL_STMT_BREAK; 236 | case SYNTAX_CALL_STMT: 237 | return MPPL_STMT_CALL; 238 | case SYNTAX_RETURN_STMT: 239 | return MPPL_STMT_RETURN; 240 | case SYNTAX_INPUT_STMT: 241 | return MPPL_STMT_INPUT; 242 | case SYNTAX_OUTPUT_STMT: 243 | return MPPL_STMT_OUTPUT; 244 | case SYNTAX_COMP_STMT: 245 | return MPPL_STMT_COMP; 246 | default: 247 | unreachable(); 248 | } 249 | } 250 | 251 | /* assignment statement */ 252 | 253 | AnyMpplVar *mppl_assign_stmt__lhs(const MpplAssignStmt *stmt) 254 | { 255 | return (AnyMpplVar *) syntax_tree_child(syntax(stmt), 0); 256 | } 257 | 258 | MpplToken *mppl_assign_stmt__assign_token(const MpplAssignStmt *stmt) 259 | { 260 | return (MpplToken *) syntax_tree_child(syntax(stmt), 1); 261 | } 262 | 263 | AnyMpplExpr *mppl_assign_stmt__rhs(const MpplAssignStmt *stmt) 264 | { 265 | return (AnyMpplExpr *) syntax_tree_child(syntax(stmt), 2); 266 | } 267 | 268 | /* if statement */ 269 | 270 | MpplToken *mppl_if_stmt__if_token(const MpplIfStmt *stmt) 271 | { 272 | return (MpplToken *) syntax_tree_child(syntax(stmt), 0); 273 | } 274 | 275 | AnyMpplExpr *mppl_if_stmt__cond(const MpplIfStmt *stmt) 276 | { 277 | return (AnyMpplExpr *) syntax_tree_child(syntax(stmt), 1); 278 | } 279 | 280 | MpplToken *mppl_if_stmt__then_token(const MpplIfStmt *stmt) 281 | { 282 | return (MpplToken *) syntax_tree_child(syntax(stmt), 2); 283 | } 284 | 285 | AnyMpplStmt *mppl_if_stmt__then_stmt(const MpplIfStmt *stmt) 286 | { 287 | return (AnyMpplStmt *) syntax_tree_child(syntax(stmt), 3); 288 | } 289 | 290 | MpplToken *mppl_if_stmt__else_token(const MpplIfStmt *stmt) 291 | { 292 | return (MpplToken *) syntax_tree_child(syntax(stmt), 4); 293 | } 294 | 295 | AnyMpplStmt *mppl_if_stmt__else_stmt(const MpplIfStmt *stmt) 296 | { 297 | return (AnyMpplStmt *) syntax_tree_child(syntax(stmt), 5); 298 | } 299 | 300 | /* while statement */ 301 | 302 | MpplToken *mppl_while_stmt__while_token(const MpplWhileStmt *stmt) 303 | { 304 | return (MpplToken *) syntax_tree_child(syntax(stmt), 0); 305 | } 306 | 307 | AnyMpplExpr *mppl_while_stmt__cond(const MpplWhileStmt *stmt) 308 | { 309 | return (AnyMpplExpr *) syntax_tree_child(syntax(stmt), 1); 310 | } 311 | 312 | MpplToken *mppl_while_stmt__do_token(const MpplWhileStmt *stmt) 313 | { 314 | return (MpplToken *) syntax_tree_child(syntax(stmt), 2); 315 | } 316 | 317 | AnyMpplStmt *mppl_while_stmt__do_stmt(const MpplWhileStmt *stmt) 318 | { 319 | return (AnyMpplStmt *) syntax_tree_child(syntax(stmt), 3); 320 | } 321 | 322 | /* break statement */ 323 | 324 | MpplToken *mppl_break_stmt__break_token(const MpplBreakStmt *stmt) 325 | { 326 | return (MpplToken *) syntax_tree_child(syntax(stmt), 0); 327 | } 328 | 329 | /* call statement */ 330 | 331 | MpplToken *mppl_call_stmt__call_token(const MpplCallStmt *stmt) 332 | { 333 | return (MpplToken *) syntax_tree_child(syntax(stmt), 0); 334 | } 335 | 336 | MpplToken *mppl_call_stmt__name(const MpplCallStmt *stmt) 337 | { 338 | return (MpplToken *) syntax_tree_child(syntax(stmt), 1); 339 | } 340 | 341 | MpplActParamList *mppl_call_stmt__act_param_list(const MpplCallStmt *stmt) 342 | { 343 | return (MpplActParamList *) syntax_tree_child(syntax(stmt), 2); 344 | } 345 | 346 | /* return statement */ 347 | 348 | MpplToken *mppl_return_stmt__return_token(const MpplReturnStmt *stmt) 349 | { 350 | return (MpplToken *) syntax_tree_child(syntax(stmt), 0); 351 | } 352 | 353 | /* input statement */ 354 | 355 | MpplToken *mppl_input_stmt__read_token(const MpplInputStmt *stmt) 356 | { 357 | return (MpplToken *) syntax_tree_child(syntax(stmt), 0); 358 | } 359 | 360 | MpplInputList *mppl_input_stmt__input_list(const MpplInputStmt *stmt) 361 | { 362 | return (MpplInputList *) syntax_tree_child(syntax(stmt), 1); 363 | } 364 | 365 | /* output statement */ 366 | 367 | MpplToken *mppl_output_stmt__write_token(const MpplOutputStmt *stmt) 368 | { 369 | return (MpplToken *) syntax_tree_child(syntax(stmt), 0); 370 | } 371 | 372 | MpplOutList *mppl_output_stmt__output_list(const MpplOutputStmt *stmt) 373 | { 374 | return (MpplOutList *) syntax_tree_child(syntax(stmt), 1); 375 | } 376 | 377 | /* compound statement */ 378 | 379 | MpplToken *mppl_comp_stmt__begin_token(const MpplCompStmt *stmt) 380 | { 381 | return (MpplToken *) syntax_tree_child(syntax(stmt), 0); 382 | } 383 | 384 | unsigned long mppl_comp_stmt__stmt_count(const MpplCompStmt *stmt) 385 | { 386 | return (syntax_tree_child_count(syntax(stmt)) - 1) / 2; 387 | } 388 | 389 | AnyMpplStmt *mppl_comp_stmt__stmt(const MpplCompStmt *stmt, unsigned long index) 390 | { 391 | return (AnyMpplStmt *) syntax_tree_child(syntax(stmt), 1 + index * 2); 392 | } 393 | 394 | MpplToken *mppl_comp_stmt__semi_token(const MpplCompStmt *stmt, unsigned long index) 395 | { 396 | return (MpplToken *) syntax_tree_child(syntax(stmt), 2 + index * 2); 397 | } 398 | 399 | MpplToken *mppl_comp_stmt__end_token(const MpplCompStmt *stmt) 400 | { 401 | return (MpplToken *) syntax_tree_child(syntax(stmt), syntax_tree_child_count(syntax(stmt)) - 1); 402 | } 403 | 404 | /* actual parameter list */ 405 | 406 | MpplToken *mppl_act_param_list__lparen_token(const MpplActParamList *list) 407 | { 408 | return (MpplToken *) syntax_tree_child(syntax(list), 0); 409 | } 410 | 411 | unsigned long mppl_act_param_list__expr_count(const MpplActParamList *list) 412 | { 413 | return (syntax_tree_child_count(syntax(list)) - 1) / 2; 414 | } 415 | 416 | AnyMpplExpr *mppl_act_param_list__expr(const MpplActParamList *list, unsigned long index) 417 | { 418 | return (AnyMpplExpr *) syntax_tree_child(syntax(list), 1 + index * 2); 419 | } 420 | 421 | MpplToken *mppl_act_param_list__comma_token(const MpplActParamList *list, unsigned long index) 422 | { 423 | return (MpplToken *) syntax_tree_child(syntax(list), 2 + index * 2); 424 | } 425 | 426 | MpplToken *mppl_act_param_list__rparen_token(const MpplActParamList *list) 427 | { 428 | return (MpplToken *) syntax_tree_child(syntax(list), syntax_tree_child_count(syntax(list)) - 1); 429 | } 430 | 431 | /* input list */ 432 | 433 | MpplToken *mppl_input_list__lparen_token(const MpplInputList *list) 434 | { 435 | return (MpplToken *) syntax_tree_child(syntax(list), 0); 436 | } 437 | 438 | unsigned long mppl_input_list__var_count(const MpplInputList *list) 439 | { 440 | return (syntax_tree_child_count(syntax(list)) - 1) / 2; 441 | } 442 | 443 | AnyMpplVar *mppl_input_list__var(const MpplInputList *list, unsigned long index) 444 | { 445 | return (AnyMpplVar *) syntax_tree_child(syntax(list), 1 + index * 2); 446 | } 447 | 448 | MpplToken *mppl_input_list__comma_token(const MpplInputList *list, unsigned long index) 449 | { 450 | return (MpplToken *) syntax_tree_child(syntax(list), 2 + index * 2); 451 | } 452 | 453 | MpplToken *mppl_input_list__rparen_token(const MpplInputList *list) 454 | { 455 | return (MpplToken *) syntax_tree_child(syntax(list), syntax_tree_child_count(syntax(list)) - 1); 456 | } 457 | 458 | /* expression */ 459 | 460 | MpplExprKind mppl_expr__kind(const AnyMpplExpr *expr) 461 | { 462 | switch (syntax_tree_kind(syntax(expr))) { 463 | case SYNTAX_BINARY_EXPR: 464 | return MPPL_EXPR_BINARY; 465 | case SYNTAX_PAREN_EXPR: 466 | return MPPL_EXPR_PAREN; 467 | case SYNTAX_NOT_EXPR: 468 | return MPPL_EXPR_NOT; 469 | case SYNTAX_CAST_EXPR: 470 | return MPPL_EXPR_CAST; 471 | case SYNTAX_ENTIRE_VAR: 472 | case SYNTAX_INDEXED_VAR: 473 | return MPPL_EXPR_VAR; 474 | case SYNTAX_NUMBER_LIT: 475 | case SYNTAX_STRING_LIT: 476 | case SYNTAX_TRUE_KW: 477 | case SYNTAX_FALSE_KW: 478 | return MPPL_EXPR_LIT; 479 | default: 480 | unreachable(); 481 | } 482 | } 483 | 484 | /* binary expression */ 485 | 486 | AnyMpplExpr *mppl_binary_expr__lhs(const MpplBinaryExpr *expr) 487 | { 488 | return (AnyMpplExpr *) syntax_tree_child(syntax(expr), 0); 489 | } 490 | 491 | MpplToken *mppl_binary_expr__op_token(const MpplBinaryExpr *expr) 492 | { 493 | return (MpplToken *) syntax_tree_child(syntax(expr), 1); 494 | } 495 | 496 | AnyMpplExpr *mppl_binary_expr__rhs(const MpplBinaryExpr *expr) 497 | { 498 | return (AnyMpplExpr *) syntax_tree_child(syntax(expr), 2); 499 | } 500 | 501 | /* parenthesized expression */ 502 | 503 | MpplToken *mppl_paren_expr__lparen_token(const MpplParenExpr *expr) 504 | { 505 | return (MpplToken *) syntax_tree_child(syntax(expr), 0); 506 | } 507 | 508 | AnyMpplExpr *mppl_paren_expr__expr(const MpplParenExpr *expr) 509 | { 510 | return (AnyMpplExpr *) syntax_tree_child(syntax(expr), 1); 511 | } 512 | 513 | MpplToken *mppl_paren_expr__rparen_token(const MpplParenExpr *expr) 514 | { 515 | return (MpplToken *) syntax_tree_child(syntax(expr), 2); 516 | } 517 | 518 | /* not expression */ 519 | 520 | MpplToken *mppl_not_expr__not_token(const MpplNotExpr *expr) 521 | { 522 | return (MpplToken *) syntax_tree_child(syntax(expr), 0); 523 | } 524 | 525 | AnyMpplExpr *mppl_not_expr__expr(const MpplNotExpr *expr) 526 | { 527 | return (AnyMpplExpr *) syntax_tree_child(syntax(expr), 1); 528 | } 529 | 530 | /* cast expression */ 531 | 532 | AnyMpplStdType *mppl_cast_expr__type(const MpplCastExpr *expr) 533 | { 534 | return (AnyMpplStdType *) syntax_tree_child(syntax(expr), 0); 535 | } 536 | 537 | MpplToken *mppl_cast_expr__lparen_token(const MpplCastExpr *expr) 538 | { 539 | return (MpplToken *) syntax_tree_child(syntax(expr), 1); 540 | } 541 | 542 | AnyMpplExpr *mppl_cast_expr__expr(const MpplCastExpr *expr) 543 | { 544 | return (AnyMpplExpr *) syntax_tree_child(syntax(expr), 2); 545 | } 546 | 547 | MpplToken *mppl_cast_expr__rparen_token(const MpplCastExpr *expr) 548 | { 549 | return (MpplToken *) syntax_tree_child(syntax(expr), 3); 550 | } 551 | 552 | /* variable */ 553 | 554 | MpplVarKind mppl_var__kind(const AnyMpplVar *var) 555 | { 556 | switch (syntax_tree_kind(syntax(var))) { 557 | case SYNTAX_ENTIRE_VAR: 558 | return MPPL_VAR_ENTIRE; 559 | case SYNTAX_INDEXED_VAR: 560 | return MPPL_VAR_INDEXED; 561 | default: 562 | unreachable(); 563 | } 564 | } 565 | 566 | /* entire variable */ 567 | 568 | MpplToken *mppl_entire_var__name(const MpplEntireVar *var) 569 | { 570 | return (MpplToken *) syntax_tree_child(syntax(var), 0); 571 | } 572 | 573 | /* indexed variable */ 574 | 575 | MpplToken *mppl_indexed_var__name(const MpplIndexedVar *var) 576 | { 577 | return (MpplToken *) syntax_tree_child(syntax(var), 0); 578 | } 579 | 580 | MpplToken *mppl_indexed_var__lbracket_token(const MpplIndexedVar *var) 581 | { 582 | return (MpplToken *) syntax_tree_child(syntax(var), 1); 583 | } 584 | 585 | AnyMpplExpr *mppl_indexed_var__expr(const MpplIndexedVar *var) 586 | { 587 | return (AnyMpplExpr *) syntax_tree_child(syntax(var), 2); 588 | } 589 | 590 | MpplToken *mppl_indexed_var__rbracket_token(const MpplIndexedVar *var) 591 | { 592 | return (MpplToken *) syntax_tree_child(syntax(var), 3); 593 | } 594 | 595 | /* type */ 596 | 597 | MpplTypeKind mppl_type__kind(const AnyMpplType *type) 598 | { 599 | switch (syntax_tree_kind(syntax(type))) { 600 | case SYNTAX_INTEGER_KW: 601 | case SYNTAX_BOOLEAN_KW: 602 | case SYNTAX_CHAR_KW: 603 | return MPPL_TYPE_STD; 604 | case SYNTAX_ARRAY_TYPE: 605 | return MPPL_TYPE_ARRAY; 606 | default: 607 | unreachable(); 608 | } 609 | } 610 | 611 | /* standard type */ 612 | 613 | MpplStdTypeKind mppl_std_type__kind(const AnyMpplStdType *type) 614 | { 615 | switch (syntax_tree_kind(syntax(type))) { 616 | case SYNTAX_INTEGER_KW: 617 | return MPPL_STD_TYPE_INTEGER; 618 | case SYNTAX_BOOLEAN_KW: 619 | return MPPL_STD_TYPE_BOOLEAN; 620 | case SYNTAX_CHAR_KW: 621 | return MPPL_STD_TYPE_CHAR; 622 | default: 623 | unreachable(); 624 | } 625 | } 626 | 627 | /* array type */ 628 | 629 | MpplToken *mppl_array_type__array_token(const MpplArrayType *type) 630 | { 631 | return (MpplToken *) syntax_tree_child(syntax(type), 0); 632 | } 633 | 634 | MpplToken *mppl_array_type__lbracket_token(const MpplArrayType *type) 635 | { 636 | return (MpplToken *) syntax_tree_child(syntax(type), 1); 637 | } 638 | 639 | MpplNumberLit *mppl_array_type__size(const MpplArrayType *type) 640 | { 641 | return (MpplNumberLit *) syntax_tree_child(syntax(type), 2); 642 | } 643 | 644 | MpplToken *mppl_array_type__rbracket_token(const MpplArrayType *type) 645 | { 646 | return (MpplToken *) syntax_tree_child(syntax(type), 3); 647 | } 648 | 649 | MpplToken *mppl_array_type__of_token(const MpplArrayType *type) 650 | { 651 | return (MpplToken *) syntax_tree_child(syntax(type), 4); 652 | } 653 | 654 | AnyMpplStdType *mppl_array_type__type(const MpplArrayType *type) 655 | { 656 | return (AnyMpplStdType *) syntax_tree_child(syntax(type), 5); 657 | } 658 | 659 | /* output list */ 660 | 661 | MpplToken *mppl_out_list__lparen_token(const MpplOutList *list) 662 | { 663 | return (MpplToken *) syntax_tree_child(syntax(list), 0); 664 | } 665 | 666 | unsigned long mppl_out_list__out_value_count(const MpplOutList *list) 667 | { 668 | return (syntax_tree_child_count(syntax(list)) - 1) / 2; 669 | } 670 | 671 | MpplOutValue *mppl_out_list__out_value(const MpplOutList *list, unsigned long index) 672 | { 673 | return (MpplOutValue *) syntax_tree_child(syntax(list), 1 + index * 2); 674 | } 675 | 676 | MpplToken *mppl_out_list__comma_token(const MpplOutList *list, unsigned long index) 677 | { 678 | return (MpplToken *) syntax_tree_child(syntax(list), 2 + index * 2); 679 | } 680 | 681 | MpplToken *mppl_out_list__rparen_token(const MpplOutList *list) 682 | { 683 | return (MpplToken *) syntax_tree_child(syntax(list), syntax_tree_child_count(syntax(list)) - 1); 684 | } 685 | 686 | /* output value */ 687 | 688 | AnyMpplExpr *mppl_out_value__expr(const MpplOutValue *value) 689 | { 690 | return (AnyMpplExpr *) syntax_tree_child(syntax(value), 0); 691 | } 692 | 693 | MpplToken *mppl_out_value__colon_token(const MpplOutValue *value) 694 | { 695 | return (MpplToken *) syntax_tree_child(syntax(value), 1); 696 | } 697 | 698 | MpplNumberLit *mppl_out_value__width(const MpplOutValue *value) 699 | { 700 | return (MpplNumberLit *) syntax_tree_child(syntax(value), 2); 701 | } 702 | 703 | /* literal */ 704 | 705 | MpplLitKind mppl_lit__kind(const AnyMpplLit *lit) 706 | { 707 | switch (syntax_tree_kind(syntax(lit))) { 708 | case SYNTAX_NUMBER_LIT: 709 | return MPPL_LIT_NUMBER; 710 | case SYNTAX_STRING_LIT: 711 | return MPPL_LIT_STRING; 712 | case SYNTAX_TRUE_KW: 713 | return MPPL_LIT_BOOLEAN; 714 | case SYNTAX_FALSE_KW: 715 | return MPPL_LIT_BOOLEAN; 716 | default: 717 | unreachable(); 718 | } 719 | } 720 | 721 | /* utility */ 722 | 723 | const void *mppl_ref(const void *syntax) 724 | { 725 | return syntax_tree_ref(syntax); 726 | } 727 | 728 | void mppl_unref(const void *syntax) 729 | { 730 | syntax_tree_unref(syntax); 731 | } 732 | -------------------------------------------------------------------------------- /src/mppl_syntax.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef SYNTAX_H 18 | #define SYNTAX_H 19 | 20 | typedef struct MpplProgram MpplProgram; 21 | 22 | typedef enum { 23 | MPPL_DECL_PART_VAR, 24 | MPPL_DECL_PART_PROC 25 | } MpplDeclPartKind; 26 | 27 | typedef struct AnyMpplDeclPart AnyMpplDeclPart; 28 | typedef struct MpplVarDeclPart MpplVarDeclPart; 29 | typedef struct MpplVarDecl MpplVarDecl; 30 | typedef struct MpplProcDecl MpplProcDecl; 31 | 32 | typedef struct MpplFmlParamList MpplFmlParamList; 33 | typedef struct MpplFmlParamSec MpplFmlParamSec; 34 | 35 | typedef enum { 36 | MPPL_STMT_ASSIGN, 37 | MPPL_STMT_IF, 38 | MPPL_STMT_WHILE, 39 | MPPL_STMT_BREAK, 40 | MPPL_STMT_CALL, 41 | MPPL_STMT_RETURN, 42 | MPPL_STMT_INPUT, 43 | MPPL_STMT_OUTPUT, 44 | MPPL_STMT_COMP 45 | } MpplStmtKind; 46 | 47 | typedef struct AnyMpplStmt AnyMpplStmt; 48 | typedef struct MpplAssignStmt MpplAssignStmt; 49 | typedef struct MpplIfStmt MpplIfStmt; 50 | typedef struct MpplWhileStmt MpplWhileStmt; 51 | typedef struct MpplBreakStmt MpplBreakStmt; 52 | typedef struct MpplCallStmt MpplCallStmt; 53 | typedef struct MpplReturnStmt MpplReturnStmt; 54 | typedef struct MpplInputStmt MpplInputStmt; 55 | typedef struct MpplOutputStmt MpplOutputStmt; 56 | typedef struct MpplCompStmt MpplCompStmt; 57 | typedef struct MpplActParamList MpplActParamList; 58 | 59 | typedef enum { 60 | MPPL_EXPR_BINARY, 61 | MPPL_EXPR_PAREN, 62 | MPPL_EXPR_NOT, 63 | MPPL_EXPR_CAST, 64 | MPPL_EXPR_VAR, 65 | MPPL_EXPR_LIT 66 | } MpplExprKind; 67 | 68 | typedef struct AnyMpplExpr AnyMpplExpr; 69 | typedef struct MpplBinaryExpr MpplBinaryExpr; 70 | typedef struct MpplParenExpr MpplParenExpr; 71 | typedef struct MpplNotExpr MpplNotExpr; 72 | typedef struct MpplCastExpr MpplCastExpr; 73 | 74 | typedef enum { 75 | MPPL_VAR_ENTIRE, 76 | MPPL_VAR_INDEXED 77 | } MpplVarKind; 78 | 79 | typedef struct AnyMpplVar AnyMpplVar; 80 | typedef struct MpplEntireVar MpplEntireVar; 81 | typedef struct MpplIndexedVar MpplIndexedVar; 82 | 83 | typedef enum { 84 | MPPL_TYPE_STD, 85 | MPPL_TYPE_ARRAY 86 | } MpplTypeKind; 87 | 88 | typedef struct AnyMpplType AnyMpplType; 89 | typedef struct MpplArrayType MpplArrayType; 90 | 91 | typedef enum { 92 | MPPL_STD_TYPE_INTEGER, 93 | MPPL_STD_TYPE_BOOLEAN, 94 | MPPL_STD_TYPE_CHAR 95 | } MpplStdTypeKind; 96 | 97 | typedef struct AnyMpplStdType AnyMpplStdType; 98 | typedef struct MpplStdTypeInteger MpplStdTypeInteger; 99 | typedef struct MpplStdTypeBoolean MpplStdTypeBoolean; 100 | typedef struct MpplStdTypeChar MpplStdTypeChar; 101 | 102 | typedef struct MpplInputList MpplInputList; 103 | typedef struct MpplOutputList MpplOutList; 104 | typedef struct MpplOutputValue MpplOutValue; 105 | 106 | typedef enum { 107 | MPPL_LIT_NUMBER, 108 | MPPL_LIT_BOOLEAN, 109 | MPPL_LIT_STRING 110 | } MpplLitKind; 111 | 112 | typedef struct AnyMpplLit AnyMpplLit; 113 | typedef struct MpplNumberLit MpplNumberLit; 114 | typedef struct MpplBooleanLit MpplBooleanLit; 115 | typedef struct MpplStringLit MpplStringLit; 116 | 117 | typedef struct MpplToken MpplToken; 118 | 119 | MpplToken *mppl_program__program_token(const MpplProgram *program); 120 | MpplToken *mppl_program__name(const MpplProgram *program); 121 | MpplToken *mppl_program__semi_token(const MpplProgram *program); 122 | unsigned long mppl_program__decl_part_count(const MpplProgram *program); 123 | AnyMpplDeclPart *mppl_program__decl_part(const MpplProgram *program, unsigned long index); 124 | MpplCompStmt *mppl_program__stmt(const MpplProgram *program); 125 | MpplToken *mppl_program__dot_token(const MpplProgram *program); 126 | MpplToken *mppl_program__eof_token(const MpplProgram *program); 127 | 128 | MpplDeclPartKind mppl_decl_part__kind(const AnyMpplDeclPart *part); 129 | 130 | MpplToken *mppl_var_decl_part__var_token(const MpplVarDeclPart *part); 131 | unsigned long mppl_var_decl_part__var_decl_count(const MpplVarDeclPart *part); 132 | MpplVarDecl *mppl_var_decl_part__var_decl(const MpplVarDeclPart *part, unsigned long index); 133 | MpplToken *mppl_var_decl_part__semi_token(const MpplVarDeclPart *part, unsigned long index); 134 | 135 | unsigned long mppl_var_decl__name_count(const MpplVarDecl *decl); 136 | MpplToken *mppl_var_decl__name(const MpplVarDecl *decl, unsigned long index); 137 | MpplToken *mppl_var_decl__comma_token(const MpplVarDecl *decl, unsigned long index); 138 | MpplToken *mppl_var_decl__colon_token(const MpplVarDecl *decl); 139 | AnyMpplType *mppl_var_decl__type(const MpplVarDecl *decl); 140 | 141 | MpplToken *mppl_proc_decl__procedure_token(const MpplProcDecl *decl); 142 | MpplToken *mppl_proc_decl__name(const MpplProcDecl *decl); 143 | MpplFmlParamList *mppl_proc_decl__fml_param_list(const MpplProcDecl *decl); 144 | MpplToken *mppl_proc_decl__semi_token_0(const MpplProcDecl *decl); 145 | MpplVarDeclPart *mppl_proc_decl__var_decl_part(const MpplProcDecl *decl); 146 | MpplCompStmt *mppl_proc_decl__comp_stmt(const MpplProcDecl *decl); 147 | MpplToken *mppl_proc_decl__semi_token_1(const MpplProcDecl *decl); 148 | 149 | MpplToken *mppl_fml_param_list__lparen_token(const MpplFmlParamList *list); 150 | unsigned long mppl_fml_param_list__sec_count(const MpplFmlParamList *list); 151 | MpplFmlParamSec *mppl_fml_param_list__sec(const MpplFmlParamList *list, unsigned long index); 152 | MpplToken *mppl_fml_param_list__semi_token(const MpplFmlParamList *list, unsigned long index); 153 | MpplToken *mppl_fml_param_list__rparen_token(const MpplFmlParamList *list); 154 | 155 | unsigned long mppl_fml_param_sec__name_count(const MpplFmlParamSec *sec); 156 | MpplToken *mppl_fml_param_sec__name(const MpplFmlParamSec *sec, unsigned long index); 157 | MpplToken *mppl_fml_param_sec__comma_token(const MpplFmlParamSec *sec, unsigned long index); 158 | MpplToken *mppl_fml_param_sec__colon_token(const MpplFmlParamSec *sec); 159 | AnyMpplType *mppl_fml_param_sec__type(const MpplFmlParamSec *sec); 160 | 161 | MpplStmtKind mppl_stmt__kind(const AnyMpplStmt *stmt); 162 | 163 | AnyMpplVar *mppl_assign_stmt__lhs(const MpplAssignStmt *stmt); 164 | MpplToken *mppl_assign_stmt__assign_token(const MpplAssignStmt *stmt); 165 | AnyMpplExpr *mppl_assign_stmt__rhs(const MpplAssignStmt *stmt); 166 | 167 | MpplToken *mppl_if_stmt__if_token(const MpplIfStmt *stmt); 168 | AnyMpplExpr *mppl_if_stmt__cond(const MpplIfStmt *stmt); 169 | MpplToken *mppl_if_stmt__then_token(const MpplIfStmt *stmt); 170 | AnyMpplStmt *mppl_if_stmt__then_stmt(const MpplIfStmt *stmt); 171 | MpplToken *mppl_if_stmt__else_token(const MpplIfStmt *stmt); 172 | AnyMpplStmt *mppl_if_stmt__else_stmt(const MpplIfStmt *stmt); 173 | 174 | MpplToken *mppl_while_stmt__while_token(const MpplWhileStmt *stmt); 175 | AnyMpplExpr *mppl_while_stmt__cond(const MpplWhileStmt *stmt); 176 | MpplToken *mppl_while_stmt__do_token(const MpplWhileStmt *stmt); 177 | AnyMpplStmt *mppl_while_stmt__do_stmt(const MpplWhileStmt *stmt); 178 | 179 | MpplToken *mppl_break_stmt__break_token(const MpplBreakStmt *stmt); 180 | 181 | MpplToken *mppl_call_stmt__call_token(const MpplCallStmt *stmt); 182 | MpplToken *mppl_call_stmt__name(const MpplCallStmt *stmt); 183 | MpplActParamList *mppl_call_stmt__act_param_list(const MpplCallStmt *stmt); 184 | 185 | MpplToken *mppl_return_stmt__return_token(const MpplReturnStmt *stmt); 186 | 187 | MpplToken *mppl_input_stmt__read_token(const MpplInputStmt *stmt); 188 | MpplInputList *mppl_input_stmt__input_list(const MpplInputStmt *stmt); 189 | 190 | MpplToken *mppl_output_stmt__write_token(const MpplOutputStmt *stmt); 191 | MpplOutList *mppl_output_stmt__output_list(const MpplOutputStmt *stmt); 192 | 193 | MpplToken *mppl_comp_stmt__begin_token(const MpplCompStmt *stmt); 194 | unsigned long mppl_comp_stmt__stmt_count(const MpplCompStmt *stmt); 195 | AnyMpplStmt *mppl_comp_stmt__stmt(const MpplCompStmt *stmt, unsigned long index); 196 | MpplToken *mppl_comp_stmt__semi_token(const MpplCompStmt *stmt, unsigned long index); 197 | MpplToken *mppl_comp_stmt__end_token(const MpplCompStmt *stmt); 198 | 199 | MpplToken *mppl_act_param_list__lparen_token(const MpplActParamList *list); 200 | unsigned long mppl_act_param_list__expr_count(const MpplActParamList *list); 201 | AnyMpplExpr *mppl_act_param_list__expr(const MpplActParamList *list, unsigned long index); 202 | MpplToken *mppl_act_param_list__comma_token(const MpplActParamList *list, unsigned long index); 203 | MpplToken *mppl_act_param_list__rparen_token(const MpplActParamList *list); 204 | 205 | MpplToken *mppl_input_list__lparen_token(const MpplInputList *list); 206 | unsigned long mppl_input_list__var_count(const MpplInputList *list); 207 | AnyMpplVar *mppl_input_list__var(const MpplInputList *list, unsigned long index); 208 | MpplToken *mppl_input_list__comma_token(const MpplInputList *list, unsigned long index); 209 | MpplToken *mppl_input_list__rparen_token(const MpplInputList *list); 210 | 211 | MpplExprKind mppl_expr__kind(const AnyMpplExpr *expr); 212 | 213 | AnyMpplExpr *mppl_binary_expr__lhs(const MpplBinaryExpr *expr); 214 | MpplToken *mppl_binary_expr__op_token(const MpplBinaryExpr *expr); 215 | AnyMpplExpr *mppl_binary_expr__rhs(const MpplBinaryExpr *expr); 216 | 217 | MpplToken *mppl_paren_expr__lparen_token(const MpplParenExpr *expr); 218 | AnyMpplExpr *mppl_paren_expr__expr(const MpplParenExpr *expr); 219 | MpplToken *mppl_paren_expr__rparen_token(const MpplParenExpr *expr); 220 | 221 | MpplToken *mppl_not_expr__not_token(const MpplNotExpr *expr); 222 | AnyMpplExpr *mppl_not_expr__expr(const MpplNotExpr *expr); 223 | 224 | AnyMpplStdType *mppl_cast_expr__type(const MpplCastExpr *expr); 225 | MpplToken *mppl_cast_expr__lparen_token(const MpplCastExpr *expr); 226 | AnyMpplExpr *mppl_cast_expr__expr(const MpplCastExpr *expr); 227 | MpplToken *mppl_cast_expr__rparen_token(const MpplCastExpr *expr); 228 | 229 | MpplVarKind mppl_var__kind(const AnyMpplVar *var); 230 | 231 | MpplToken *mppl_entire_var__name(const MpplEntireVar *var); 232 | 233 | MpplToken *mppl_indexed_var__name(const MpplIndexedVar *var); 234 | MpplToken *mppl_indexed_var__lbracket_token(const MpplIndexedVar *var); 235 | AnyMpplExpr *mppl_indexed_var__expr(const MpplIndexedVar *var); 236 | MpplToken *mppl_indexed_var__rbracket_token(const MpplIndexedVar *var); 237 | 238 | MpplTypeKind mppl_type__kind(const AnyMpplType *type); 239 | 240 | MpplStdTypeKind mppl_std_type__kind(const AnyMpplStdType *type); 241 | 242 | MpplToken *mppl_array_type__array_token(const MpplArrayType *type); 243 | MpplToken *mppl_array_type__lbracket_token(const MpplArrayType *type); 244 | MpplNumberLit *mppl_array_type__size(const MpplArrayType *type); 245 | MpplToken *mppl_array_type__rbracket_token(const MpplArrayType *type); 246 | MpplToken *mppl_array_type__of_token(const MpplArrayType *type); 247 | AnyMpplStdType *mppl_array_type__type(const MpplArrayType *type); 248 | 249 | MpplToken *mppl_out_list__lparen_token(const MpplOutList *list); 250 | unsigned long mppl_out_list__out_value_count(const MpplOutList *list); 251 | MpplOutValue *mppl_out_list__out_value(const MpplOutList *list, unsigned long index); 252 | MpplToken *mppl_out_list__comma_token(const MpplOutList *list, unsigned long index); 253 | MpplToken *mppl_out_list__rparen_token(const MpplOutList *list); 254 | 255 | AnyMpplExpr *mppl_out_value__expr(const MpplOutValue *value); 256 | MpplToken *mppl_out_value__colon_token(const MpplOutValue *value); 257 | MpplNumberLit *mppl_out_value__width(const MpplOutValue *value); 258 | 259 | MpplLitKind mppl_lit__kind(const AnyMpplLit *lit); 260 | 261 | const void *mppl_ref(const void *syntax); 262 | void mppl_unref(const void *syntax); 263 | 264 | #endif 265 | -------------------------------------------------------------------------------- /src/mppl_syntax_ext.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef MPPL_SYNTAX_EXT_H 18 | #define MPPL_SYNTAX_EXT_H 19 | 20 | #include "context_fwd.h" 21 | #include "mppl_syntax.h" 22 | 23 | typedef struct MpplAstWalker MpplAstWalker; 24 | 25 | struct MpplAstWalker { 26 | void (*visit_program)(const MpplAstWalker *walker, const MpplProgram *program, void *data); 27 | void (*visit_decl_part)(const MpplAstWalker *walker, const AnyMpplDeclPart *part, void *data); 28 | void (*visit_var_decl_part)(const MpplAstWalker *walker, const MpplVarDeclPart *part, void *data); 29 | void (*visit_var_decl)(const MpplAstWalker *walker, const MpplVarDecl *decl, void *data); 30 | void (*visit_proc_decl)(const MpplAstWalker *walker, const MpplProcDecl *decl, void *data); 31 | void (*visit_fml_param_list)(const MpplAstWalker *walker, const MpplFmlParamList *list, void *data); 32 | void (*visit_fml_param_sec)(const MpplAstWalker *walker, const MpplFmlParamSec *sec, void *data); 33 | void (*visit_stmt)(const MpplAstWalker *walker, const AnyMpplStmt *stmt, void *data); 34 | void (*visit_assign_stmt)(const MpplAstWalker *walker, const MpplAssignStmt *stmt, void *data); 35 | void (*visit_if_stmt)(const MpplAstWalker *walker, const MpplIfStmt *stmt, void *data); 36 | void (*visit_while_stmt)(const MpplAstWalker *walker, const MpplWhileStmt *stmt, void *data); 37 | void (*visit_break_stmt)(const MpplAstWalker *walker, const MpplBreakStmt *stmt, void *data); 38 | void (*visit_call_stmt)(const MpplAstWalker *walker, const MpplCallStmt *stmt, void *data); 39 | void (*visit_return_stmt)(const MpplAstWalker *walker, const MpplReturnStmt *stmt, void *data); 40 | void (*visit_input_stmt)(const MpplAstWalker *walker, const MpplInputStmt *stmt, void *data); 41 | void (*visit_output_stmt)(const MpplAstWalker *walker, const MpplOutputStmt *stmt, void *data); 42 | void (*visit_comp_stmt)(const MpplAstWalker *walker, const MpplCompStmt *stmt, void *data); 43 | void (*visit_act_param_list)(const MpplAstWalker *walker, const MpplActParamList *list, void *data); 44 | void (*visit_expr)(const MpplAstWalker *walker, const AnyMpplExpr *expr, void *data); 45 | void (*visit_binary_expr)(const MpplAstWalker *walker, const MpplBinaryExpr *expr, void *data); 46 | void (*visit_paren_expr)(const MpplAstWalker *walker, const MpplParenExpr *expr, void *data); 47 | void (*visit_not_expr)(const MpplAstWalker *walker, const MpplNotExpr *expr, void *data); 48 | void (*visit_cast_expr)(const MpplAstWalker *walker, const MpplCastExpr *expr, void *data); 49 | void (*visit_var)(const MpplAstWalker *walker, const AnyMpplVar *var, void *data); 50 | void (*visit_entire_var)(const MpplAstWalker *walker, const MpplEntireVar *var, void *data); 51 | void (*visit_indexed_var)(const MpplAstWalker *walker, const MpplIndexedVar *var, void *data); 52 | void (*visit_type)(const MpplAstWalker *walker, const AnyMpplType *type, void *data); 53 | void (*visit_array_type)(const MpplAstWalker *walker, const MpplArrayType *type, void *data); 54 | void (*visit_std_type)(const MpplAstWalker *walker, const AnyMpplStdType *type, void *data); 55 | void (*visit_std_type_integer)(const MpplAstWalker *walker, const MpplStdTypeInteger *type, void *data); 56 | void (*visit_std_type_boolean)(const MpplAstWalker *walker, const MpplStdTypeBoolean *type, void *data); 57 | void (*visit_std_type_char)(const MpplAstWalker *walker, const MpplStdTypeChar *type, void *data); 58 | void (*visit_input_list)(const MpplAstWalker *walker, const MpplInputList *list, void *data); 59 | void (*visit_output_list)(const MpplAstWalker *walker, const MpplOutList *list, void *data); 60 | void (*visit_output_value)(const MpplAstWalker *walker, const MpplOutValue *value, void *data); 61 | void (*visit_lit)(const MpplAstWalker *walker, const AnyMpplLit *lit, void *data); 62 | void (*visit_number_lit)(const MpplAstWalker *walker, const MpplNumberLit *lit, void *data); 63 | void (*visit_boolean_lit)(const MpplAstWalker *walker, const MpplBooleanLit *lit, void *data); 64 | void (*visit_string_lit)(const MpplAstWalker *walker, const MpplStringLit *lit, void *data); 65 | }; 66 | 67 | const Type *mppl_std_type__to_type(const AnyMpplStdType *syntax); 68 | 69 | const Type *mppl_type__to_type(const AnyMpplType *syntax, Ctx *ctx); 70 | 71 | long mppl_lit_number__to_long(const MpplNumberLit *syntax); 72 | 73 | char *mppl_lit_string__to_string(const MpplStringLit *syntax); 74 | 75 | int mppl_lit_boolean__to_int(const MpplBooleanLit *syntax); 76 | 77 | void mppl_ast_walker__setup(MpplAstWalker *walker); 78 | void mppl_ast_walker__travel(MpplAstWalker *walker, const MpplProgram *syntax, void *data); 79 | 80 | void mppl_ast__walk_program(const MpplAstWalker *walker, const MpplProgram *syntax, void *data); 81 | void mppl_ast__walk_decl_part(const MpplAstWalker *walker, const AnyMpplDeclPart *syntax, void *data); 82 | void mppl_ast__walk_var_decl_part(const MpplAstWalker *walker, const MpplVarDeclPart *syntax, void *data); 83 | void mppl_ast__walk_var_decl(const MpplAstWalker *walker, const MpplVarDecl *syntax, void *data); 84 | void mppl_ast__walk_proc_decl(const MpplAstWalker *walker, const MpplProcDecl *syntax, void *data); 85 | void mppl_ast__walk_fml_param_list(const MpplAstWalker *walker, const MpplFmlParamList *syntax, void *data); 86 | void mppl_ast__walk_fml_param_sec(const MpplAstWalker *walker, const MpplFmlParamSec *syntax, void *data); 87 | void mppl_ast__walk_stmt(const MpplAstWalker *walker, const AnyMpplStmt *syntax, void *data); 88 | void mppl_ast__walk_assign_stmt(const MpplAstWalker *walker, const MpplAssignStmt *syntax, void *data); 89 | void mppl_ast__walk_if_stmt(const MpplAstWalker *walker, const MpplIfStmt *syntax, void *data); 90 | void mppl_ast__walk_while_stmt(const MpplAstWalker *walker, const MpplWhileStmt *syntax, void *data); 91 | void mppl_ast__walk_break_stmt(const MpplAstWalker *walker, const MpplBreakStmt *syntax, void *data); 92 | void mppl_ast__walk_call_stmt(const MpplAstWalker *walker, const MpplCallStmt *syntax, void *data); 93 | void mppl_ast__walk_return_stmt(const MpplAstWalker *walker, const MpplReturnStmt *syntax, void *data); 94 | void mppl_ast__walk_input_stmt(const MpplAstWalker *walker, const MpplInputStmt *syntax, void *data); 95 | void mppl_ast__walk_output_stmt(const MpplAstWalker *walker, const MpplOutputStmt *syntax, void *data); 96 | void mppl_ast__walk_comp_stmt(const MpplAstWalker *walker, const MpplCompStmt *syntax, void *data); 97 | void mppl_ast__walk_act_param_list(const MpplAstWalker *walker, const MpplActParamList *syntax, void *data); 98 | void mppl_ast__walk_expr(const MpplAstWalker *walker, const AnyMpplExpr *syntax, void *data); 99 | void mppl_ast__walk_binary_expr(const MpplAstWalker *walker, const MpplBinaryExpr *syntax, void *data); 100 | void mppl_ast__walk_paren_expr(const MpplAstWalker *walker, const MpplParenExpr *syntax, void *data); 101 | void mppl_ast__walk_not_expr(const MpplAstWalker *walker, const MpplNotExpr *syntax, void *data); 102 | void mppl_ast__walk_cast_expr(const MpplAstWalker *walker, const MpplCastExpr *syntax, void *data); 103 | void mppl_ast__walk_var(const MpplAstWalker *walker, const AnyMpplVar *syntax, void *data); 104 | void mppl_ast__walk_entire_var(const MpplAstWalker *walker, const MpplEntireVar *syntax, void *data); 105 | void mppl_ast__walk_indexed_var(const MpplAstWalker *walker, const MpplIndexedVar *syntax, void *data); 106 | void mppl_ast__walk_type(const MpplAstWalker *walker, const AnyMpplType *syntax, void *data); 107 | void mppl_ast__walk_array_type(const MpplAstWalker *walker, const MpplArrayType *syntax, void *data); 108 | void mppl_ast__walk_std_type(const MpplAstWalker *walker, const AnyMpplStdType *syntax, void *data); 109 | void mppl_ast__walk_std_type_integer(const MpplAstWalker *walker, const MpplStdTypeInteger *syntax, void *data); 110 | void mppl_ast__walk_std_type_boolean(const MpplAstWalker *walker, const MpplStdTypeBoolean *syntax, void *data); 111 | void mppl_ast__walk_std_type_char(const MpplAstWalker *walker, const MpplStdTypeChar *syntax, void *data); 112 | void mppl_ast__walk_input_list(const MpplAstWalker *walker, const MpplInputList *syntax, void *data); 113 | void mppl_ast__walk_output_list(const MpplAstWalker *walker, const MpplOutList *syntax, void *data); 114 | void mppl_ast__walk_output_value(const MpplAstWalker *walker, const MpplOutValue *syntax, void *data); 115 | void mppl_ast__walk_lit(const MpplAstWalker *walker, const AnyMpplLit *syntax, void *data); 116 | void mppl_ast__walk_number_lit(const MpplAstWalker *walker, const MpplNumberLit *syntax, void *data); 117 | void mppl_ast__walk_boolean_lit(const MpplAstWalker *walker, const MpplBooleanLit *syntax, void *data); 118 | void mppl_ast__walk_string_lit(const MpplAstWalker *walker, const MpplStringLit *syntax, void *data); 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /src/report.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef MESSAGE_H 18 | #define MESSAGE_H 19 | 20 | #include 21 | 22 | #include "source.h" 23 | 24 | typedef enum { 25 | REPORT_KIND_NOTE, 26 | REPORT_KIND_WARN, 27 | REPORT_KIND_ERROR 28 | } ReportKind; 29 | 30 | typedef struct ReportAnnotation ReportAnnotation; 31 | typedef struct Report Report; 32 | 33 | Report *report_new(ReportKind kind, unsigned long offset, const char *format, ...); 34 | Report *report_new_with_args(ReportKind kind, unsigned long offset, const char *format, va_list args); 35 | void report_free(Report *report); 36 | void report_annotation(Report *report, unsigned long start, unsigned long end, const char *format, ...); 37 | void report_annotation_with_args(Report *report, unsigned long start, unsigned long end, const char *format, va_list args); 38 | void report_note(Report *report, const char *format, ...); 39 | void report_note_with_args(Report *report, const char *format, va_list args); 40 | void report_emit(Report *report, const Source *source); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/resolver.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include "array.h" 21 | #include "compiler.h" 22 | #include "context.h" 23 | #include "context_fwd.h" 24 | #include "map.h" 25 | #include "mppl_syntax.h" 26 | #include "mppl_syntax_ext.h" 27 | #include "report.h" 28 | #include "source.h" 29 | #include "string.h" 30 | #include "syntax_kind.h" 31 | #include "syntax_tree.h" 32 | #include "utility.h" 33 | 34 | typedef struct Scope Scope; 35 | typedef struct Resolver Resolver; 36 | 37 | struct Scope { 38 | Map *defs; 39 | Scope *parent; 40 | const SyntaxTree *syntax; 41 | }; 42 | 43 | struct Resolver { 44 | Scope *scope; 45 | Ctx *ctx; 46 | Array *errors; 47 | }; 48 | 49 | static void error_def_conflict(Resolver *resolver, const String *name, const SyntaxTree *previous, const SyntaxTree *conflict) 50 | { 51 | unsigned long previous_offset = syntax_tree_offset(previous); 52 | unsigned long conflict_offset = syntax_tree_offset(conflict); 53 | 54 | Report *report = report_new(REPORT_KIND_ERROR, conflict_offset, "conflicting definition of `%s`", string_data(name)); 55 | report_annotation(report, previous_offset, previous_offset + string_length(name), 56 | "previous definition of `%s`", string_data(name)); 57 | report_annotation(report, conflict_offset, conflict_offset + string_length(name), "redefinition of `%s`", string_data(name)); 58 | array_push(resolver->errors, &report); 59 | } 60 | 61 | static void error_var_res_failure(Resolver *resolver, const String *name, const SyntaxTree *use) 62 | { 63 | unsigned long offset = syntax_tree_offset(use); 64 | 65 | Report *report = report_new(REPORT_KIND_ERROR, offset, "failed to resolve `%s`", string_data(name)); 66 | report_annotation(report, offset, offset + string_length(name), 67 | "use of undeclared variable or parameter `%s`", string_data(name)); 68 | array_push(resolver->errors, &report); 69 | } 70 | 71 | static void error_proc_res_failure(Resolver *resolver, const String *name, const SyntaxTree *use) 72 | { 73 | unsigned long offset = syntax_tree_offset(use); 74 | 75 | Report *report = report_new(REPORT_KIND_ERROR, offset, "failed to resolve `%s`", string_data(name)); 76 | report_annotation(report, offset, offset + string_length(name), 77 | "use of undeclared procedure `%s`", string_data(name)); 78 | array_push(resolver->errors, &report); 79 | } 80 | 81 | static void push_scope(Resolver *resolver, const SyntaxTree *syntax) 82 | { 83 | Scope *scope = xmalloc(sizeof(Scope)); 84 | scope->defs = map_new(NULL, NULL); 85 | scope->parent = resolver->scope; 86 | scope->syntax = syntax; 87 | resolver->scope = scope; 88 | } 89 | 90 | static void pop_scope(Resolver *resolver) 91 | { 92 | Scope *scope = resolver->scope; 93 | resolver->scope = scope->parent; 94 | map_free(scope->defs); 95 | free(scope); 96 | } 97 | 98 | static void try_define(Resolver *resolver, DefKind kind, const SyntaxTree *item_syntax, const SyntaxTree *name_syntax) 99 | { 100 | MapIndex index; 101 | const RawSyntaxToken *name_token = (const RawSyntaxToken *) syntax_tree_raw(name_syntax); 102 | 103 | if (resolver->scope && map_entry(resolver->scope->defs, (void *) name_token->string, &index)) { 104 | const Def *previous = map_value(resolver->scope->defs, &index); 105 | error_def_conflict(resolver, def_name(previous), def_syntax(previous), item_syntax); 106 | } else { 107 | const Def *def = ctx_define(resolver->ctx, kind, name_token->string, item_syntax); 108 | ctx_resolve(resolver->ctx, name_syntax, def); 109 | if (resolver->scope) { 110 | map_update(resolver->scope->defs, &index, (void *) name_token->string, (void *) def); 111 | } 112 | } 113 | } 114 | 115 | static const Def *get_def(Resolver *resolver, const String *name) 116 | { 117 | Scope *scope; 118 | for (scope = resolver->scope; scope; scope = scope->parent) { 119 | MapIndex index; 120 | if (map_entry(scope->defs, (void *) name, &index)) { 121 | return map_value(scope->defs, &index); 122 | } 123 | } 124 | return NULL; 125 | } 126 | 127 | static const Def *try_resolve(Resolver *resolver, const SyntaxTree *syntax, int is_proc) 128 | { 129 | const RawSyntaxToken *node = (const RawSyntaxToken *) syntax_tree_raw(syntax); 130 | const Def *def = get_def(resolver, node->string); 131 | if (resolver->scope && def) { 132 | ctx_resolve(resolver->ctx, syntax, def); 133 | return def; 134 | } else { 135 | if (is_proc) { 136 | error_proc_res_failure(resolver, node->string, syntax); 137 | } else { 138 | error_var_res_failure(resolver, node->string, syntax); 139 | } 140 | return NULL; 141 | } 142 | } 143 | 144 | static void error_call_stmt_recursion(Resolver *resolver, const Def *proc, const SyntaxTree *name_syntax) 145 | { 146 | unsigned long offset = syntax_tree_offset(name_syntax); 147 | unsigned long length = syntax_tree_text_length(name_syntax); 148 | 149 | Report *report = report_new(REPORT_KIND_ERROR, offset, "recursion is prohibited"); 150 | report_annotation(report, offset, offset + length, "recursive call to `%s`", string_data(def_name(proc))); 151 | array_push(resolver->errors, &report); 152 | } 153 | 154 | static void visit_program(const MpplAstWalker *walker, const MpplProgram *syntax, void *resolver) 155 | { 156 | MpplToken *name_syntax = mppl_program__name(syntax); 157 | try_define(resolver, DEF_PROGRAM, (const SyntaxTree *) syntax, (SyntaxTree *) name_syntax); 158 | mppl_unref(name_syntax); 159 | push_scope(resolver, (const SyntaxTree *) syntax); 160 | mppl_ast__walk_program(walker, syntax, resolver); 161 | pop_scope(resolver); 162 | } 163 | 164 | static void visit_proc_decl(const MpplAstWalker *walker, const MpplProcDecl *syntax, void *resolver) 165 | { 166 | MpplToken *name_syntax = mppl_proc_decl__name(syntax); 167 | try_define(resolver, DEF_PROC, (const SyntaxTree *) syntax, (SyntaxTree *) name_syntax); 168 | mppl_unref(name_syntax); 169 | push_scope(resolver, (const SyntaxTree *) syntax); 170 | mppl_ast__walk_proc_decl(walker, syntax, resolver); 171 | pop_scope(resolver); 172 | } 173 | 174 | static void visit_var_decl(const MpplAstWalker *walker, const MpplVarDecl *syntax, void *resolver) 175 | { 176 | unsigned long i; 177 | Resolver *r = resolver; 178 | DefKind kind = syntax_tree_kind(r->scope->syntax) == SYNTAX_PROGRAM ? DEF_VAR : DEF_LOCAL; 179 | for (i = 0; i < mppl_var_decl__name_count(syntax); ++i) { 180 | MpplToken *name_syntax = mppl_var_decl__name(syntax, i); 181 | try_define(resolver, kind, (const SyntaxTree *) syntax, (SyntaxTree *) name_syntax); 182 | mppl_unref(name_syntax); 183 | } 184 | (void) walker; 185 | } 186 | 187 | static void visit_fml_param_sec(const MpplAstWalker *walker, const MpplFmlParamSec *syntax, void *resolver) 188 | { 189 | unsigned long i; 190 | for (i = 0; i < mppl_fml_param_sec__name_count(syntax); ++i) { 191 | SyntaxTree *name_syntax = (SyntaxTree *) mppl_fml_param_sec__name(syntax, i); 192 | try_define(resolver, DEF_PARAM, (const SyntaxTree *) syntax, name_syntax); 193 | mppl_unref(name_syntax); 194 | } 195 | (void) walker; 196 | } 197 | 198 | static void visit_entire_var(const MpplAstWalker *walker, const MpplEntireVar *syntax, void *resolver) 199 | { 200 | MpplToken *name_syntax = mppl_entire_var__name(syntax); 201 | try_resolve(resolver, (const SyntaxTree *) name_syntax, 0); 202 | mppl_unref(name_syntax); 203 | (void) walker; 204 | } 205 | 206 | static void visit_indexed_var(const MpplAstWalker *walker, const MpplIndexedVar *syntax, void *resolver) 207 | { 208 | MpplToken *name_syntax = mppl_indexed_var__name(syntax); 209 | try_resolve(resolver, (const SyntaxTree *) name_syntax, 0); 210 | mppl_unref(name_syntax); 211 | mppl_ast__walk_indexed_var(walker, syntax, resolver); 212 | } 213 | 214 | static void visit_call_stmt(const MpplAstWalker *walker, const MpplCallStmt *syntax, void *resolver) 215 | { 216 | MpplToken *name_syntax = mppl_call_stmt__name(syntax); 217 | const Def *proc = try_resolve(resolver, (const SyntaxTree *) name_syntax, 1); 218 | if (proc) { 219 | const SyntaxTree *node; 220 | for (node = (const SyntaxTree *) syntax; node; node = syntax_tree_parent(node)) { 221 | if (syntax_tree_kind(node) == SYNTAX_PROC_DECL && syntax_tree_raw(node) == syntax_tree_raw(def_syntax(proc))) { 222 | error_call_stmt_recursion(resolver, proc, (const SyntaxTree *) name_syntax); 223 | break; 224 | } 225 | } 226 | } 227 | mppl_unref(name_syntax); 228 | mppl_ast__walk_call_stmt(walker, syntax, resolver); 229 | } 230 | 231 | int mpplc_resolve(const Source *source, const MpplProgram *syntax, Ctx *ctx) 232 | { 233 | MpplAstWalker walker; 234 | Resolver resolver; 235 | int result = 1; 236 | resolver.scope = NULL; 237 | resolver.ctx = ctx; 238 | resolver.errors = array_new(sizeof(Report *)); 239 | 240 | mppl_ast_walker__setup(&walker); 241 | walker.visit_program = &visit_program; 242 | walker.visit_proc_decl = &visit_proc_decl; 243 | walker.visit_var_decl = &visit_var_decl; 244 | walker.visit_fml_param_sec = &visit_fml_param_sec; 245 | walker.visit_entire_var = &visit_entire_var; 246 | walker.visit_indexed_var = &visit_indexed_var; 247 | walker.visit_call_stmt = &visit_call_stmt; 248 | mppl_ast_walker__travel(&walker, syntax, &resolver); 249 | 250 | if (array_count(resolver.errors)) { 251 | unsigned long i; 252 | for (i = 0; i < array_count(resolver.errors); ++i) { 253 | report_emit(*(Report **) array_at(resolver.errors, i), source); 254 | } 255 | result = 0; 256 | } 257 | array_free(resolver.errors); 258 | return result; 259 | } 260 | -------------------------------------------------------------------------------- /src/source.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "array.h" 23 | #include "source.h" 24 | #include "utility.h" 25 | 26 | Source *source_new(const char *file_name, unsigned long file_name_length) 27 | { 28 | Source *source = xmalloc(sizeof(Source)); 29 | { 30 | source->file_name = xmalloc(file_name_length + 1); 31 | strncpy(source->file_name, file_name, file_name_length); 32 | source->file_name[file_name_length] = '\0'; 33 | source->file_name_length = file_name_length; 34 | } 35 | 36 | { 37 | FILE *file = fopen(source->file_name, "rb"); 38 | if (file) { 39 | Array *text = array_new(sizeof(char)); 40 | char buffer[4096]; 41 | unsigned long length; 42 | while ((length = fread(buffer, sizeof(char), sizeof(buffer), file)) > 0) { 43 | array_push_count(text, buffer, length); 44 | } 45 | buffer[0] = '\0'; 46 | array_push(text, buffer); 47 | array_fit(text); 48 | source->text_length = array_count(text) - 1; 49 | source->text = array_steal(text); 50 | fclose(file); 51 | } else { 52 | source->text_length = -1ul; 53 | source->text = NULL; 54 | } 55 | } 56 | 57 | if (source->text) { 58 | Array *line_offsets = array_new(sizeof(unsigned long)); 59 | Array *line_lengths = array_new(sizeof(unsigned long)); 60 | unsigned long offset = 0; 61 | array_push(line_offsets, &offset); 62 | while (offset < source->text_length) { 63 | unsigned long length = strcspn(source->text + offset, "\r\n"); 64 | array_push(line_lengths, &length); 65 | offset += length; 66 | 67 | if (offset < source->text_length) { 68 | if (!strncmp(source->text + offset, "\r\n", 2) || !strncmp(source->text + offset, "\n\r", 2)) { 69 | offset += 2; 70 | } else { 71 | offset += 1; 72 | } 73 | array_push(line_offsets, &offset); 74 | } 75 | } 76 | source->line_count = array_count(line_offsets); 77 | source->line_offsets = array_steal(line_offsets); 78 | source->line_lengths = array_steal(line_lengths); 79 | } else { 80 | source->line_count = -1ul; 81 | source->line_offsets = NULL; 82 | source->line_lengths = NULL; 83 | } 84 | 85 | if (source->file_name && source->text && source->line_offsets) { 86 | return source; 87 | } else { 88 | source_free(source); 89 | return NULL; 90 | } 91 | } 92 | 93 | void source_free(Source *source) 94 | { 95 | if (source) { 96 | free(source->file_name); 97 | free(source->text); 98 | free(source->line_offsets); 99 | free(source->line_lengths); 100 | free(source); 101 | } 102 | } 103 | 104 | int source_location(const Source *source, unsigned long offset, SourceLocation *location) 105 | { 106 | if (offset >= source->text_length) { 107 | return 0; 108 | } else { 109 | unsigned long left = 0; 110 | unsigned long right = source->line_count; 111 | while (right - left > 1) { 112 | unsigned long middle = (right - left) / 2 + left; 113 | if (offset < source->line_offsets[middle]) { 114 | right = middle; 115 | } else { 116 | left = middle; 117 | } 118 | } 119 | if (location) { 120 | location->line = left; 121 | location->column = offset - source->line_offsets[left]; 122 | } 123 | return 1; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/source.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef SOURCE_H 18 | #define SOURCE_H 19 | 20 | typedef struct SourceLocation SourceLocation; 21 | typedef struct Source Source; 22 | 23 | struct SourceLocation { 24 | unsigned long line; 25 | unsigned long column; 26 | }; 27 | 28 | struct Source { 29 | char *file_name; 30 | unsigned long file_name_length; 31 | char *text; 32 | unsigned long text_length; 33 | unsigned long *line_offsets; 34 | unsigned long *line_lengths; 35 | unsigned long line_count; 36 | }; 37 | 38 | Source *source_new(const char *file_name, unsigned long file_name_length); 39 | void source_free(Source *source); 40 | int source_location(const Source *source, unsigned long offset, SourceLocation *location); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/syntax_kind.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #include "syntax_kind.h" 20 | 21 | typedef struct Keyword Keyword; 22 | 23 | struct Keyword { 24 | const char *keyword; 25 | SyntaxKind kind; 26 | }; 27 | 28 | static Keyword KEYWORDS[] = { 29 | { "and", SYNTAX_AND_KW }, 30 | { "array", SYNTAX_ARRAY_KW }, 31 | { "begin", SYNTAX_BEGIN_KW }, 32 | { "boolean", SYNTAX_BOOLEAN_KW }, 33 | { "break", SYNTAX_BREAK_KW }, 34 | { "call", SYNTAX_CALL_KW }, 35 | { "char", SYNTAX_CHAR_KW }, 36 | { "div", SYNTAX_DIV_KW }, 37 | { "do", SYNTAX_DO_KW }, 38 | { "else", SYNTAX_ELSE_KW }, 39 | { "end", SYNTAX_END_KW }, 40 | { "false", SYNTAX_FALSE_KW }, 41 | { "if", SYNTAX_IF_KW }, 42 | { "integer", SYNTAX_INTEGER_KW }, 43 | { "not", SYNTAX_NOT_KW }, 44 | { "of", SYNTAX_OF_KW }, 45 | { "or", SYNTAX_OR_KW }, 46 | { "procedure", SYNTAX_PROCEDURE_KW }, 47 | { "program", SYNTAX_PROGRAM_KW }, 48 | { "read", SYNTAX_READ_KW }, 49 | { "readln", SYNTAX_READLN_KW }, 50 | { "return", SYNTAX_RETURN_KW }, 51 | { "then", SYNTAX_THEN_KW }, 52 | { "true", SYNTAX_TRUE_KW }, 53 | { "var", SYNTAX_VAR_KW }, 54 | { "while", SYNTAX_WHILE_KW }, 55 | { "write", SYNTAX_WRITE_KW }, 56 | { "writeln", SYNTAX_WRITELN_KW }, 57 | }; 58 | 59 | SyntaxKind syntax_kind_from_keyword(const char *string, unsigned long size) 60 | { 61 | unsigned long i; 62 | for (i = 0; i < sizeof(KEYWORDS) / sizeof(Keyword); ++i) { 63 | if (!strncmp(KEYWORDS[i].keyword, string, size) && !KEYWORDS[i].keyword[size]) { 64 | return KEYWORDS[i].kind; 65 | } 66 | } 67 | return SYNTAX_BAD_TOKEN; 68 | } 69 | 70 | int syntax_kind_is_token(SyntaxKind kind) 71 | { 72 | return kind <= SYNTAX_EOF_TOKEN; 73 | } 74 | 75 | int syntax_kind_is_trivia(SyntaxKind kind) 76 | { 77 | return kind >= SYNTAX_SPACE_TRIVIA && kind <= SYNTAX_C_COMMENT_TRIVIA; 78 | } 79 | 80 | static const char *SYNTAX_TO_STRING[] = { 81 | "BAD_TOKEN", 82 | "IDENT_TOKEN", 83 | "NUMBER_LIT", 84 | "STRING_LIT", 85 | "PLUS_TOKEN", 86 | "MINUS_TOKEN", 87 | "STAR_TOKEN", 88 | "EQUAL_TOKEN", 89 | "NOTEQ_TOKEN", 90 | "LESS_TOKEN", 91 | "LESSEQ_TOKEN", 92 | "GREATER_TOKEN", 93 | "GREATEREQ_TOKEN", 94 | "LPAREN_TOKEN", 95 | "RPAREN_TOKEN", 96 | "LBRACKET_TOKEN", 97 | "RBRACKET_TOKEN", 98 | "ASSIGN_TOKEN", 99 | "DOT_TOKEN", 100 | "COMMA_TOKEN", 101 | "COLON_TOKEN", 102 | "SEMI_TOKEN", 103 | "PROGRAM_KW", 104 | "VAR_KW", 105 | "ARRAY_KW", 106 | "OF_KW", 107 | "BEGIN_KW", 108 | "END_KW", 109 | "IF_KW", 110 | "THEN_KW", 111 | "ELSE_KW", 112 | "PROCEDURE_KW", 113 | "RETURN_KW", 114 | "CALL_KW", 115 | "WHILE_KW", 116 | "DO_KW", 117 | "NOT_KW", 118 | "OR_KW", 119 | "DIV_KW", 120 | "AND_KW", 121 | "CHAR_KW", 122 | "INTEGER_KW", 123 | "BOOLEAN_KW", 124 | "READ_KW", 125 | "WRITE_KW", 126 | "READLN_KW", 127 | "WRITELN_KW", 128 | "TRUE_KW", 129 | "FALSE_KW", 130 | "BREAK_KW", 131 | "EOF_TOKEN", 132 | "SPACE_TRIVIA", 133 | "BRACES_COMMENT_TRIVIA", 134 | "C_COMMENT_TRIVIA", 135 | "PROGRAM", 136 | "VAR_DECL_PART", 137 | "VAR_DECL", 138 | "ARRAY_TYPE", 139 | "PROC_DECL", 140 | "FML_PARAM_LIST", 141 | "FML_PARAM_SEC", 142 | "ASSIGN_STMT", 143 | "IF_STMT", 144 | "WHILE_STMT", 145 | "BREAK_STMT", 146 | "CALL_STMT", 147 | "ACT_PARAM_LIST", 148 | "RETURN_STMT", 149 | "INPUT_STMT", 150 | "INPUT_LIST", 151 | "OUTPUT_STMT", 152 | "OUTPUT_LIST", 153 | "OUTPUT_VALUE", 154 | "COMP_STMT", 155 | "ENTIRE_VAR", 156 | "INDEXED_VAR", 157 | "BINARY_EXPR", 158 | "PAREN_EXPR", 159 | "NOT_EXPR", 160 | "CAST_EXPR", 161 | }; 162 | 163 | const char *syntax_kind_to_string(SyntaxKind kind) 164 | { 165 | return SYNTAX_TO_STRING[kind]; 166 | } 167 | -------------------------------------------------------------------------------- /src/syntax_kind.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef SYNTAX_KIND_H 18 | #define SYNTAX_KIND_H 19 | 20 | typedef enum { 21 | SYNTAX_BAD_TOKEN, 22 | SYNTAX_IDENT_TOKEN, 23 | SYNTAX_NUMBER_LIT, 24 | SYNTAX_STRING_LIT, 25 | SYNTAX_PLUS_TOKEN, 26 | SYNTAX_MINUS_TOKEN, 27 | SYNTAX_STAR_TOKEN, 28 | SYNTAX_EQUAL_TOKEN, 29 | SYNTAX_NOTEQ_TOKEN, 30 | SYNTAX_LESS_TOKEN, 31 | SYNTAX_LESSEQ_TOKEN, 32 | SYNTAX_GREATER_TOKEN, 33 | SYNTAX_GREATEREQ_TOKEN, 34 | SYNTAX_LPAREN_TOKEN, 35 | SYNTAX_RPAREN_TOKEN, 36 | SYNTAX_LBRACKET_TOKEN, 37 | SYNTAX_RBRACKET_TOKEN, 38 | SYNTAX_ASSIGN_TOKEN, 39 | SYNTAX_DOT_TOKEN, 40 | SYNTAX_COMMA_TOKEN, 41 | SYNTAX_COLON_TOKEN, 42 | SYNTAX_SEMI_TOKEN, 43 | SYNTAX_PROGRAM_KW, 44 | SYNTAX_VAR_KW, 45 | SYNTAX_ARRAY_KW, 46 | SYNTAX_OF_KW, 47 | SYNTAX_BEGIN_KW, 48 | SYNTAX_END_KW, 49 | SYNTAX_IF_KW, 50 | SYNTAX_THEN_KW, 51 | SYNTAX_ELSE_KW, 52 | SYNTAX_PROCEDURE_KW, 53 | SYNTAX_RETURN_KW, 54 | SYNTAX_CALL_KW, 55 | SYNTAX_WHILE_KW, 56 | SYNTAX_DO_KW, 57 | SYNTAX_NOT_KW, 58 | SYNTAX_OR_KW, 59 | SYNTAX_DIV_KW, 60 | SYNTAX_AND_KW, 61 | SYNTAX_CHAR_KW, 62 | SYNTAX_INTEGER_KW, 63 | SYNTAX_BOOLEAN_KW, 64 | SYNTAX_READ_KW, 65 | SYNTAX_WRITE_KW, 66 | SYNTAX_READLN_KW, 67 | SYNTAX_WRITELN_KW, 68 | SYNTAX_TRUE_KW, 69 | SYNTAX_FALSE_KW, 70 | SYNTAX_BREAK_KW, 71 | SYNTAX_EOF_TOKEN, 72 | SYNTAX_SPACE_TRIVIA, 73 | SYNTAX_BRACES_COMMENT_TRIVIA, 74 | SYNTAX_C_COMMENT_TRIVIA, 75 | SYNTAX_PROGRAM, 76 | SYNTAX_VAR_DECL_PART, 77 | SYNTAX_VAR_DECL, 78 | SYNTAX_ARRAY_TYPE, 79 | SYNTAX_PROC_DECL, 80 | SYNTAX_FML_PARAM_LIST, 81 | SYNTAX_FML_PARAM_SEC, 82 | SYNTAX_ASSIGN_STMT, 83 | SYNTAX_IF_STMT, 84 | SYNTAX_WHILE_STMT, 85 | SYNTAX_BREAK_STMT, 86 | SYNTAX_CALL_STMT, 87 | SYNTAX_ACT_PARAM_LIST, 88 | SYNTAX_RETURN_STMT, 89 | SYNTAX_INPUT_STMT, 90 | SYTANX_INPUT_LIST, 91 | SYNTAX_OUTPUT_STMT, 92 | SYNTAX_OUTPUT_LIST, 93 | SYNTAX_OUTPUT_VALUE, 94 | SYNTAX_COMP_STMT, 95 | SYNTAX_ENTIRE_VAR, 96 | SYNTAX_INDEXED_VAR, 97 | SYNTAX_BINARY_EXPR, 98 | SYNTAX_PAREN_EXPR, 99 | SYNTAX_NOT_EXPR, 100 | SYNTAX_CAST_EXPR 101 | } SyntaxKind; 102 | 103 | SyntaxKind syntax_kind_from_keyword(const char *string, unsigned long size); 104 | int syntax_kind_is_token(SyntaxKind kind); 105 | int syntax_kind_is_trivia(SyntaxKind kind); 106 | const char *syntax_kind_to_string(SyntaxKind kind); 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /src/syntax_tree.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "array.h" 22 | #include "context.h" 23 | #include "string.h" 24 | #include "syntax_kind.h" 25 | #include "syntax_tree.h" 26 | #include "utility.h" 27 | 28 | struct SyntaxTree { 29 | const SyntaxTree *parent; 30 | RawSyntaxNode *inner; 31 | unsigned long offset; 32 | unsigned long ref; 33 | }; 34 | 35 | struct SyntaxBuilder { 36 | Array *parents; 37 | Array *children; 38 | Array *leading_trivia; 39 | Array *trailing_trivia; 40 | }; 41 | 42 | unsigned long raw_syntax_node_text_length(const RawSyntaxNode *node) 43 | { 44 | if (!node) { 45 | return 0; 46 | } else if (syntax_kind_is_token(node->kind)) { 47 | RawSyntaxToken *token = (RawSyntaxToken *) node; 48 | return string_length(token->string); 49 | } else { 50 | RawSyntaxTree *tree = (RawSyntaxTree *) node; 51 | return tree->text_length; 52 | } 53 | } 54 | 55 | unsigned long raw_syntax_node_trivia_length(const RawSyntaxNode *node) 56 | { 57 | if (!node) { 58 | return 0; 59 | } else if (syntax_kind_is_token(node->kind)) { 60 | RawSyntaxToken *token = (RawSyntaxToken *) node; 61 | unsigned long i; 62 | unsigned long result = 0; 63 | for (i = 0; i < token->leading_trivia_count; ++i) { 64 | result += string_length(token->leading_trivia[i].string); 65 | } 66 | return result; 67 | } else { 68 | RawSyntaxTree *tree = (RawSyntaxTree *) node; 69 | return tree->children_count ? raw_syntax_node_trivia_length(tree->children[0]) : 0; 70 | } 71 | } 72 | 73 | static void raw_syntax_node_print_impl(const RawSyntaxNode *node, unsigned long depth, unsigned long offset) 74 | { 75 | printf("%*.s", (int) depth * 2, ""); 76 | if (!node) { 77 | printf("(NULL)\n"); 78 | } else if (syntax_kind_is_token(node->kind)) { 79 | RawSyntaxToken *token = (RawSyntaxToken *) node; 80 | offset += raw_syntax_node_trivia_length(node); 81 | printf("%s @ %lu..%lu \"%s\"\n", syntax_kind_to_string(token->kind), offset, offset + string_length(token->string), string_data(token->string)); 82 | } else { 83 | unsigned long i; 84 | RawSyntaxTree *tree = (RawSyntaxTree *) node; 85 | unsigned long tree_offset = offset + raw_syntax_node_trivia_length(node); 86 | printf("%s @ %lu..%lu\n", syntax_kind_to_string(tree->kind), tree_offset, tree_offset + tree->text_length); 87 | for (i = 0; i < tree->children_count; ++i) { 88 | raw_syntax_node_print_impl(tree->children[i], depth + 1, offset); 89 | offset += raw_syntax_node_trivia_length(tree->children[i]); 90 | offset += raw_syntax_node_text_length(tree->children[i]); 91 | } 92 | } 93 | } 94 | 95 | void raw_syntax_node_print(const RawSyntaxNode *node) 96 | { 97 | raw_syntax_node_print_impl(node, 0, 0); 98 | } 99 | 100 | void raw_syntax_node_free(RawSyntaxNode *node) 101 | { 102 | if (node) { 103 | if (syntax_kind_is_token(node->kind)) { 104 | RawSyntaxToken *token = (RawSyntaxToken *) node; 105 | free(token->leading_trivia); 106 | free(token->trailing_trivia); 107 | free(token); 108 | } else { 109 | RawSyntaxTree *tree = (RawSyntaxTree *) node; 110 | unsigned long i; 111 | for (i = 0; i < tree->children_count; ++i) { 112 | raw_syntax_node_free(tree->children[i]); 113 | } 114 | free(tree->children); 115 | free(tree); 116 | } 117 | } 118 | } 119 | 120 | static SyntaxTree *syntax_tree_new(const SyntaxTree *parent, RawSyntaxNode *inner, unsigned long offset) 121 | { 122 | SyntaxTree *tree = xmalloc(sizeof(SyntaxTree)); 123 | tree->parent = syntax_tree_ref(parent); 124 | tree->inner = inner; 125 | tree->offset = offset; 126 | tree->ref = 1; 127 | return tree; 128 | } 129 | 130 | const SyntaxTree *syntax_tree_ref(const SyntaxTree *tree) 131 | { 132 | SyntaxTree *mutable_tree = (SyntaxTree *) tree; 133 | if (tree) { 134 | ++mutable_tree->ref; 135 | } 136 | return tree; 137 | } 138 | 139 | void syntax_tree_unref(const SyntaxTree *tree) 140 | { 141 | SyntaxTree *mutable_tree = (SyntaxTree *) tree; 142 | if (tree && --mutable_tree->ref == 0) { 143 | if (tree->parent) { 144 | syntax_tree_unref(tree->parent); 145 | } else { 146 | raw_syntax_node_free(tree->inner); 147 | } 148 | free(mutable_tree); 149 | } 150 | } 151 | 152 | const RawSyntaxNode *syntax_tree_raw(const SyntaxTree *tree) 153 | { 154 | return tree->inner; 155 | } 156 | 157 | SyntaxKind syntax_tree_kind(const SyntaxTree *tree) 158 | { 159 | return syntax_tree_raw(tree)->kind; 160 | } 161 | 162 | unsigned long syntax_tree_offset(const SyntaxTree *tree) 163 | { 164 | return tree->offset; 165 | } 166 | 167 | unsigned long syntax_tree_text_length(const SyntaxTree *tree) 168 | { 169 | return raw_syntax_node_text_length(syntax_tree_raw(tree)); 170 | } 171 | 172 | unsigned long syntax_tree_trivia_length(const SyntaxTree *tree) 173 | { 174 | return raw_syntax_node_trivia_length(syntax_tree_raw(tree)); 175 | } 176 | 177 | const SyntaxTree *syntax_tree_parent(const SyntaxTree *tree) 178 | { 179 | return tree->parent; 180 | } 181 | 182 | unsigned long syntax_tree_child_count(const SyntaxTree *tree) 183 | { 184 | const RawSyntaxTree *inner = (RawSyntaxTree *) tree->inner; 185 | if (syntax_kind_is_token(inner->kind)) { 186 | return 0; 187 | } else { 188 | return inner->children_count; 189 | } 190 | } 191 | 192 | SyntaxTree *syntax_tree_child(const SyntaxTree *tree, unsigned long index) 193 | { 194 | const RawSyntaxTree *inner = (RawSyntaxTree *) tree->inner; 195 | if (syntax_kind_is_token(inner->kind) || index >= inner->children_count || !inner->children[index]) { 196 | return NULL; 197 | } else { 198 | unsigned long offset = tree->offset; 199 | unsigned long i; 200 | 201 | for (i = 0; i <= index; ++i) { 202 | if (i > 0) { 203 | offset += raw_syntax_node_trivia_length(inner->children[i]); 204 | } 205 | if (i < index) { 206 | offset += raw_syntax_node_text_length(inner->children[i]); 207 | } 208 | } 209 | return syntax_tree_new(tree, inner->children[index], offset); 210 | } 211 | } 212 | 213 | void syntax_tree_visit(const SyntaxTree *tree, SyntaxTreeVisitor *visitor, void *data) 214 | { 215 | if (tree) { 216 | const RawSyntaxTree *inner = (const RawSyntaxTree *) tree->inner; 217 | if (visitor(tree, data, 1)) { 218 | if (!syntax_kind_is_token(inner->kind)) { 219 | unsigned long i; 220 | for (i = 0; i < inner->children_count; ++i) { 221 | SyntaxTree *child = syntax_tree_child(tree, i); 222 | syntax_tree_visit(child, visitor, data); 223 | syntax_tree_unref(child); 224 | } 225 | } 226 | visitor(tree, data, 0); 227 | } 228 | } 229 | } 230 | 231 | SyntaxBuilder *syntax_builder_new(void) 232 | { 233 | SyntaxBuilder *builder = xmalloc(sizeof(SyntaxBuilder)); 234 | builder->parents = array_new(sizeof(unsigned long)); 235 | builder->children = array_new(sizeof(RawSyntaxNode *)); 236 | builder->leading_trivia = array_new(sizeof(RawSyntaxTrivia)); 237 | builder->trailing_trivia = array_new(sizeof(RawSyntaxTrivia)); 238 | return builder; 239 | } 240 | 241 | void syntax_builder_free(SyntaxBuilder *builder) 242 | { 243 | if (builder) { 244 | array_free(builder->parents); 245 | array_free(builder->children); 246 | array_free(builder->leading_trivia); 247 | array_free(builder->trailing_trivia); 248 | free(builder); 249 | } 250 | } 251 | 252 | unsigned long syntax_builder_checkpoint(SyntaxBuilder *builder) 253 | { 254 | return array_count(builder->children); 255 | } 256 | 257 | void syntax_builder_start_tree(SyntaxBuilder *builder) 258 | { 259 | syntax_builder_start_tree_at(builder, syntax_builder_checkpoint(builder)); 260 | } 261 | 262 | void syntax_builder_start_tree_at(SyntaxBuilder *builder, unsigned long checkpoint) 263 | { 264 | array_push(builder->parents, &checkpoint); 265 | } 266 | 267 | void syntax_builder_end_tree(SyntaxBuilder *builder, SyntaxKind kind) 268 | { 269 | unsigned long i; 270 | unsigned long checkpoint = *(unsigned long *) array_back(builder->parents); 271 | RawSyntaxNode **children = (RawSyntaxNode **) array_at(builder->children, checkpoint); 272 | unsigned long count = array_count(builder->children) - checkpoint; 273 | 274 | RawSyntaxTree *tree = xmalloc(sizeof(RawSyntaxTree)); 275 | tree->kind = kind; 276 | tree->text_length = 0; 277 | for (i = 0; i < count; ++i) { 278 | if (i > 0) { 279 | tree->text_length += raw_syntax_node_trivia_length(children[i]); 280 | } 281 | tree->text_length += raw_syntax_node_text_length(children[i]); 282 | } 283 | 284 | tree->children_count = count; 285 | tree->children = dup(children, sizeof(RawSyntaxNode *), count); 286 | 287 | array_pop(builder->parents); 288 | array_pop_count(builder->children, count); 289 | array_push(builder->children, &tree); 290 | } 291 | 292 | void syntax_builder_null(SyntaxBuilder *builder) 293 | { 294 | RawSyntaxNode *node = NULL; 295 | array_push(builder->children, &node); 296 | } 297 | 298 | void syntax_builder_trivia(SyntaxBuilder *builder, SyntaxKind kind, const String *text, int leading) 299 | { 300 | RawSyntaxTrivia trivia; 301 | trivia.kind = kind; 302 | trivia.string = text; 303 | 304 | if (leading) { 305 | array_push(builder->leading_trivia, &trivia); 306 | } else { 307 | array_push(builder->trailing_trivia, &trivia); 308 | } 309 | } 310 | 311 | void syntax_builder_token(SyntaxBuilder *builder, SyntaxKind kind, const String *text) 312 | { 313 | RawSyntaxToken *token = xmalloc(sizeof(RawSyntaxToken)); 314 | token->kind = kind; 315 | token->string = text; 316 | 317 | token->leading_trivia_count = array_count(builder->leading_trivia); 318 | token->leading_trivia = dup(array_data(builder->leading_trivia), sizeof(RawSyntaxTrivia), token->leading_trivia_count); 319 | token->trailing_trivia_count = array_count(builder->trailing_trivia); 320 | token->trailing_trivia = dup(array_data(builder->trailing_trivia), sizeof(RawSyntaxTrivia), token->trailing_trivia_count); 321 | array_push(builder->children, &token); 322 | 323 | array_clear(builder->leading_trivia); 324 | array_clear(builder->trailing_trivia); 325 | } 326 | 327 | SyntaxTree *syntax_builder_build(SyntaxBuilder *builder) 328 | { 329 | RawSyntaxNode **root = (RawSyntaxNode **) array_front(builder->children); 330 | SyntaxTree *tree = syntax_tree_new(NULL, *root, raw_syntax_node_trivia_length(*root)); 331 | syntax_builder_free(builder); 332 | return tree; 333 | } 334 | -------------------------------------------------------------------------------- /src/syntax_tree.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef SYNTAX_TREE_H 18 | #define SYNTAX_TREE_H 19 | 20 | #include "context_fwd.h" 21 | #include "syntax_kind.h" 22 | 23 | typedef struct RawSyntaxTrivia RawSyntaxTrivia; 24 | typedef struct RawSyntaxToken RawSyntaxToken; 25 | typedef struct RawSyntaxTree RawSyntaxTree; 26 | typedef struct RawSyntaxNode RawSyntaxNode; 27 | 28 | typedef struct SyntaxTree SyntaxTree; 29 | typedef int SyntaxTreeVisitor(const SyntaxTree *tree, void *data, int enter); 30 | 31 | typedef struct SyntaxBuilder SyntaxBuilder; 32 | 33 | struct RawSyntaxTrivia { 34 | SyntaxKind kind; 35 | const String *string; 36 | }; 37 | 38 | struct RawSyntaxToken { 39 | SyntaxKind kind; 40 | const String *string; 41 | unsigned long leading_trivia_count; 42 | RawSyntaxTrivia *leading_trivia; 43 | unsigned long trailing_trivia_count; 44 | RawSyntaxTrivia *trailing_trivia; 45 | }; 46 | 47 | struct RawSyntaxTree { 48 | SyntaxKind kind; 49 | unsigned long text_length; 50 | unsigned long children_count; 51 | RawSyntaxNode **children; 52 | }; 53 | 54 | struct RawSyntaxNode { 55 | SyntaxKind kind; 56 | unsigned long text_length; 57 | }; 58 | 59 | unsigned long raw_syntax_node_text_length(const RawSyntaxNode *node); 60 | unsigned long raw_syntax_node_trivia_length(const RawSyntaxNode *node); 61 | 62 | void raw_syntax_node_print(const RawSyntaxNode *node); 63 | void raw_syntax_node_free(RawSyntaxNode *node); 64 | 65 | const SyntaxTree *syntax_tree_ref(const SyntaxTree *tree); 66 | void syntax_tree_unref(const SyntaxTree *tree); 67 | const RawSyntaxNode *syntax_tree_raw(const SyntaxTree *tree); 68 | SyntaxKind syntax_tree_kind(const SyntaxTree *tree); 69 | unsigned long syntax_tree_offset(const SyntaxTree *tree); 70 | unsigned long syntax_tree_text_length(const SyntaxTree *tree); 71 | unsigned long syntax_tree_trivia_length(const SyntaxTree *tree); 72 | const SyntaxTree *syntax_tree_parent(const SyntaxTree *tree); 73 | unsigned long syntax_tree_child_count(const SyntaxTree *tree); 74 | SyntaxTree *syntax_tree_child(const SyntaxTree *tree, unsigned long index); 75 | void syntax_tree_visit(const SyntaxTree *tree, SyntaxTreeVisitor *visitor, void *data); 76 | 77 | SyntaxBuilder *syntax_builder_new(void); 78 | void syntax_builder_free(SyntaxBuilder *builder); 79 | unsigned long syntax_builder_checkpoint(SyntaxBuilder *builder); 80 | void syntax_builder_start_tree(SyntaxBuilder *builder); 81 | void syntax_builder_start_tree_at(SyntaxBuilder *builder, unsigned long checkpoint); 82 | void syntax_builder_end_tree(SyntaxBuilder *builder, SyntaxKind kind); 83 | void syntax_builder_null(SyntaxBuilder *builder); 84 | void syntax_builder_trivia(SyntaxBuilder *builder, SyntaxKind kind, const String *text, int leading); 85 | void syntax_builder_token(SyntaxBuilder *builder, SyntaxKind kind, const String *text); 86 | SyntaxTree *syntax_builder_build(SyntaxBuilder *builder); 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/tasks.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "array.h" 22 | #include "compiler.h" 23 | #include "context.h" 24 | #include "map.h" 25 | #include "source.h" 26 | #include "syntax_kind.h" 27 | #include "utility.h" 28 | 29 | typedef struct CounterToken CounterToken; 30 | typedef struct CounterEntry CounterEntry; 31 | typedef struct Counter Counter; 32 | 33 | struct CounterToken { 34 | SyntaxKind kind; 35 | char *text; 36 | unsigned long text_length; 37 | }; 38 | 39 | struct CounterEntry { 40 | CounterToken token; 41 | unsigned long count; 42 | }; 43 | 44 | struct Counter { 45 | Array *token_counts; 46 | Array *identifer_counts; 47 | }; 48 | 49 | static CounterEntry *counter_entry_new(SyntaxKind kind, const char *text, unsigned long text_length) 50 | { 51 | CounterEntry *entry = xmalloc(sizeof(CounterEntry)); 52 | entry->count = 0; 53 | entry->token.kind = kind; 54 | entry->token.text = xmalloc(sizeof(char) * (text_length + 1)); 55 | strncpy(entry->token.text, text, text_length); 56 | entry->token.text[text_length] = '\0'; 57 | entry->token.text_length = text_length; 58 | return entry; 59 | } 60 | 61 | static void counter_entry_free(CounterEntry *entry) 62 | { 63 | if (entry) { 64 | free(entry->token.text); 65 | free(entry); 66 | } 67 | } 68 | 69 | static void increment_token(Map *counts, SyntaxKind kind, const char *text, unsigned long text_length) 70 | { 71 | CounterEntry *counter = counter_entry_new(kind, text, text_length); 72 | MapIndex index; 73 | if (map_entry(counts, &counter->token, &index)) { 74 | counter_entry_free(counter); 75 | counter = map_value(counts, &index); 76 | } else { 77 | map_update(counts, &index, &counter->token, counter); 78 | } 79 | ++counter->count; 80 | } 81 | 82 | static unsigned long counter_token_hash(const void *key) 83 | { 84 | const CounterToken *token = key; 85 | unsigned long hash = FNV1A_INIT; 86 | hash = fnv1a(hash, &token->kind, sizeof(SyntaxKind)); 87 | hash = fnv1a(hash, token->text, token->text_length); 88 | return hash; 89 | } 90 | 91 | static int counter_token_compare(const void *left, const void *right) 92 | { 93 | const CounterToken *l = left; 94 | const CounterToken *r = right; 95 | 96 | return l->kind == r->kind && l->text_length == r->text_length && strcmp(l->text, r->text) == 0; 97 | } 98 | 99 | static int counter_entry_ptr_compare(const void *left, const void *right) 100 | { 101 | const CounterEntry *l = *(const CounterEntry **) left; 102 | const CounterEntry *r = *(const CounterEntry **) right; 103 | 104 | if (l->token.kind != r->token.kind) { 105 | return l->token.kind < r->token.kind ? -1 : 1; 106 | } else { 107 | return strcmp(l->token.text, r->token.text); 108 | } 109 | } 110 | 111 | static Array *list_token(Map *counts) 112 | { 113 | Array *list = array_new_with_capacity(sizeof(CounterEntry *), map_count(counts)); 114 | MapIndex entry; 115 | for (map_iterator(counts, &entry); map_next(counts, &entry);) { 116 | CounterEntry *value = map_value(counts, &entry); 117 | array_push(list, &value); 118 | } 119 | map_free(counts); 120 | qsort(array_data(list), array_count(list), sizeof(CounterEntry *), &counter_entry_ptr_compare); 121 | return list; 122 | } 123 | 124 | static LexStatus token_count_init(Counter *count, const Source *source) 125 | { 126 | LexedToken token; 127 | LexStatus status; 128 | Map *token_counts; 129 | Map *identifier_counts; 130 | 131 | unsigned long offset = 0; 132 | 133 | token_counts = map_new(&counter_token_hash, &counter_token_compare); 134 | identifier_counts = map_new(&counter_token_hash, &counter_token_compare); 135 | while (1) { 136 | status = mpplc_lex(source, offset, &token); 137 | if (status != LEX_OK) { 138 | break; 139 | } 140 | 141 | offset += token.length; 142 | if (syntax_kind_is_trivia(token.kind)) { 143 | continue; 144 | } 145 | 146 | switch (token.kind) { 147 | case SYNTAX_IDENT_TOKEN: 148 | increment_token(identifier_counts, token.kind, source->text + token.offset, token.length); 149 | increment_token(token_counts, SYNTAX_IDENT_TOKEN, "NAME", 4); 150 | break; 151 | case SYNTAX_NUMBER_LIT: 152 | increment_token(token_counts, SYNTAX_NUMBER_LIT, "NUMBER", 6); 153 | break; 154 | case SYNTAX_STRING_LIT: 155 | increment_token(token_counts, SYNTAX_STRING_LIT, "STRING", 6); 156 | break; 157 | default: 158 | increment_token(token_counts, token.kind, source->text + token.offset, token.length); 159 | break; 160 | } 161 | } 162 | count->token_counts = list_token(token_counts); 163 | count->identifer_counts = list_token(identifier_counts); 164 | return status; 165 | } 166 | 167 | static void token_count_deinit(Counter *count) 168 | { 169 | unsigned long i; 170 | 171 | for (i = 0; i < array_count(count->token_counts); ++i) { 172 | CounterEntry **entry = array_at(count->token_counts, i); 173 | counter_entry_free(*entry); 174 | } 175 | array_free(count->token_counts); 176 | 177 | for (i = 0; i < array_count(count->identifer_counts); ++i) { 178 | CounterEntry **entry = array_at(count->identifer_counts, i); 179 | counter_entry_free(*entry); 180 | } 181 | array_free(count->identifer_counts); 182 | } 183 | 184 | static unsigned long get_token_display_width(CounterEntry *entry) 185 | { 186 | return entry->token.text_length; 187 | } 188 | 189 | static unsigned long get_max_token_display_width(CounterEntry **entries, unsigned long length) 190 | { 191 | unsigned long result = 0; 192 | unsigned long i; 193 | for (i = 0; i < length; ++i) { 194 | unsigned long width = get_token_display_width(entries[i]); 195 | if (result < width) { 196 | result = width; 197 | } 198 | } 199 | return result; 200 | } 201 | 202 | static unsigned long get_count_display_width(CounterEntry *entry) 203 | { 204 | unsigned long result = 1; 205 | unsigned long count = entry->count; 206 | while (count > 9) { 207 | ++result; 208 | count /= 10; 209 | } 210 | return result; 211 | } 212 | 213 | static unsigned long get_max_count_display_width(CounterEntry **entries, unsigned long length) 214 | { 215 | unsigned long result = 0; 216 | unsigned long i; 217 | for (i = 0; i < length; ++i) { 218 | unsigned long width = get_count_display_width(entries[i]); 219 | if (result < width) { 220 | result = width; 221 | } 222 | } 223 | return result; 224 | } 225 | 226 | static void token_count_print(Counter *count) 227 | { 228 | unsigned long i, j; 229 | const char *identifier_prefix = " \"Identifier\" "; 230 | unsigned long identifier_prefix_width = strlen(identifier_prefix); 231 | 232 | unsigned long max_token_display_width; 233 | unsigned long max_count_display_width; 234 | 235 | { 236 | unsigned long max_token_width 237 | = get_max_token_display_width(array_data(count->token_counts), array_count(count->token_counts)); 238 | unsigned long max_identifier_width 239 | = get_max_token_display_width(array_data(count->identifer_counts), array_count(count->identifer_counts)) 240 | + identifier_prefix_width; 241 | 242 | max_token_display_width 243 | = max_token_width > max_identifier_width ? max_token_width : max_identifier_width; 244 | } 245 | 246 | { 247 | unsigned long max_token_count_width 248 | = get_max_count_display_width(array_data(count->token_counts), array_count(count->token_counts)); 249 | unsigned long max_identifier_count_width 250 | = get_max_count_display_width(array_data(count->identifer_counts), array_count(count->identifer_counts)); 251 | 252 | max_count_display_width 253 | = max_token_count_width > max_identifier_count_width ? max_token_count_width : max_identifier_count_width; 254 | } 255 | 256 | for (i = 0; i < array_count(count->token_counts); ++i) { 257 | CounterEntry *token_entry = *(CounterEntry **) array_at(count->token_counts, i); 258 | unsigned long token_space_width = (max_token_display_width - get_token_display_width(token_entry)) 259 | + (max_count_display_width - get_count_display_width(token_entry)) + 2; 260 | printf("\"%s\"%*c%lu\n", token_entry->token.text, (int) token_space_width, ' ', token_entry->count); 261 | 262 | if (token_entry->token.kind == SYNTAX_IDENT_TOKEN) { 263 | for (j = 0; j < array_count(count->identifer_counts); ++j) { 264 | CounterEntry *identifier_entry = *(CounterEntry **) array_at(count->identifer_counts, j); 265 | unsigned long identifier_space_width 266 | = (max_token_display_width - identifier_prefix_width - get_token_display_width(identifier_entry)) 267 | + (max_count_display_width - get_count_display_width(identifier_entry)) + 2; 268 | printf("%s\"%s\"%*c%lu\n", 269 | identifier_prefix, identifier_entry->token.text, (int) identifier_space_width, ' ', identifier_entry->count); 270 | } 271 | } 272 | } 273 | } 274 | 275 | int mpplc_task1(int argc, const char **argv) 276 | { 277 | int status = EXIT_SUCCESS; 278 | if (argc != 2) { 279 | fprintf(stderr, "Usage: %s INPUT\n", argv[0]); 280 | status = EXIT_FAILURE; 281 | } else { 282 | Source *source = source_new(argv[1], strlen(argv[1])); 283 | if (!source) { 284 | fprintf(stderr, "Cannot open file: %s\n", argv[1]); 285 | status = EXIT_FAILURE; 286 | } else { 287 | Counter counter; 288 | switch (token_count_init(&counter, source)) { 289 | case LEX_EOF: 290 | token_count_print(&counter); 291 | break; 292 | 293 | case LEX_ERROR_STRAY_CHAR: 294 | fprintf(stderr, "Error: Stray character in program\n"); 295 | status = EXIT_FAILURE; 296 | break; 297 | 298 | case LEX_ERROR_NONGRAPHIC_CHAR: 299 | fprintf(stderr, "Error: Non-graphic character in string\n"); 300 | status = EXIT_FAILURE; 301 | break; 302 | 303 | case LEX_ERROR_UNTERMINATED_STRING: 304 | fprintf(stderr, "Error: String is unterminated\n"); 305 | status = EXIT_FAILURE; 306 | break; 307 | 308 | case LEX_ERROR_UNTERMINATED_COMMENT: 309 | fprintf(stderr, "Error: Comment is unterminated\n"); 310 | status = EXIT_FAILURE; 311 | break; 312 | 313 | default: 314 | unreachable(); 315 | } 316 | source_free(source); 317 | token_count_deinit(&counter); 318 | } 319 | } 320 | return status; 321 | } 322 | 323 | int mpplc_task2(int argc, const char **argv) 324 | { 325 | int status = EXIT_SUCCESS; 326 | if (argc != 2) { 327 | fprintf(stderr, "Usage: %s INPUT\n", argv[0]); 328 | status = EXIT_FAILURE; 329 | } else { 330 | Source *source = source_new(argv[1], strlen(argv[1])); 331 | MpplProgram *syntax = NULL; 332 | if (!source) { 333 | fprintf(stderr, "Cannot open file: %s\n", argv[1]); 334 | status = EXIT_FAILURE; 335 | } else if (mpplc_parse(source, NULL, &syntax)) { 336 | mpplc_pretty_print(syntax, NULL); 337 | status = EXIT_FAILURE; 338 | } 339 | mppl_unref(syntax); 340 | source_free(source); 341 | } 342 | return status; 343 | } 344 | 345 | int mpplc_task3(int argc, const char **argv) 346 | { 347 | fprintf(stderr, "Task 3 is not implemented\n"); 348 | return EXIT_FAILURE; 349 | } 350 | 351 | int mpplc_task4(int argc, const char **argv) 352 | { 353 | int status = EXIT_SUCCESS; 354 | if (argc != 2) { 355 | fprintf(stderr, "Usage: %s INPUT\n", argv[0]); 356 | status = EXIT_FAILURE; 357 | } else { 358 | Ctx *ctx = ctx_new(); 359 | Source *source = source_new(argv[1], strlen(argv[1])); 360 | MpplProgram *syntax = NULL; 361 | if (!source) { 362 | fprintf(stderr, "Cannot open file: %s\n", argv[1]); 363 | status = EXIT_FAILURE; 364 | } else if (mpplc_parse(source, ctx, &syntax) && mpplc_resolve(source, syntax, ctx) && mpplc_check(source, syntax, ctx)) { 365 | mpplc_codegen_casl2(source, syntax, ctx); 366 | } 367 | mppl_unref(syntax); 368 | source_free(source); 369 | ctx_free(ctx); 370 | } 371 | return status; 372 | } 373 | -------------------------------------------------------------------------------- /src/terminal.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef TERMINAL_H 18 | #define TERMINAL_H 19 | 20 | #define TERM_RESET 0 21 | #define TERM_BOLD 1 22 | #define TERM_FAINT 2 23 | #define TERM_ITALIC 3 24 | #define TERM_UNDERLINE 4 25 | #define TERM_FG_BLACK 30 26 | #define TERM_FG_RED 31 27 | #define TERM_FG_GREEN 32 28 | #define TERM_FG_YELLOW 33 29 | #define TERM_FG_BLUE 34 30 | #define TERM_FG_MAGENTA 35 31 | #define TERM_FG_CYAN 36 32 | #define TERM_FG_WHITE 37 33 | #define TERM_FG_SELECT 38 34 | #define TERM_BG_BLACK 40 35 | #define TERM_BG_RED 41 36 | #define TERM_BG_GREEN 42 37 | #define TERM_BG_YELLOW 43 38 | #define TERM_BG_BLUE 44 39 | #define TERM_BG_MAGENTA 45 40 | #define TERM_BG_CYAN 46 41 | #define TERM_BG_WHITE 47 42 | #define TERM_BG_SELECT 48 43 | #define TERM_FG_BRIGHT_BLACK 90 44 | #define TERM_FG_BRIGHT_RED 91 45 | #define TERM_FG_BRIGHT_GREEN 92 46 | #define TERM_FG_BRIGHT_YELLOW 93 47 | #define TERM_FG_BRIGHT_BLUE 94 48 | #define TERM_FG_BRIGHT_MAGENTA 95 49 | #define TERM_FG_BRIGHT_CYAN 96 50 | #define TERM_FG_BRIGHT_WHITE 97 51 | #define TERM_BG_BRIGHT_BLACK 100 52 | #define TERM_BG_BRIGHT_RED 101 53 | #define TERM_BG_BRIGHT_GREEN 102 54 | #define TERM_BG_BRIGHT_YELLOW 103 55 | #define TERM_BG_BRIGHT_BLUE 104 56 | #define TERM_BG_BRIGHT_MAGENTA 105 57 | #define TERM_BG_BRIGHT_CYAN 106 58 | #define TERM_BG_BRIGHT_WHITE 107 59 | 60 | #define TERM_COLOR_RGB 2 61 | #define TERM_COLOR_256 5 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/utility.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "utility.h" 22 | 23 | void *xmalloc(unsigned long size) 24 | { 25 | void *result = malloc(size); 26 | if (!result) { 27 | fprintf(stderr, "Internal Error: Failed to allocate memory. Aborted."); 28 | exit(EXIT_FAILURE); 29 | } 30 | return result; 31 | } 32 | 33 | void *dup(const void *ptr, unsigned long size, unsigned long count) 34 | { 35 | if (count == 0) { 36 | return NULL; 37 | } else { 38 | void *result = xmalloc(size * count); 39 | memcpy(result, ptr, size * count); 40 | return result; 41 | } 42 | } 43 | 44 | unsigned long fnv1a(unsigned long hash, const void *ptr, unsigned long len) 45 | { 46 | const unsigned char *data = ptr; 47 | const unsigned char *end = data + len; 48 | const unsigned long prime = 0x01000193ul; 49 | 50 | for (; data < end; ++data) { 51 | hash = (hash ^ *data) * prime; 52 | } 53 | return 0xFFFFFFFFul & hash; 54 | } 55 | 56 | unsigned long popcount(void *data, unsigned long count) 57 | { 58 | static const unsigned char table[] = { 59 | #define B2(n) n, n + 1, n + 1, n + 2 60 | #define B4(n) B2(n), B2(n + 1), B2(n + 1), B2(n + 2) 61 | #define B6(n) B4(n), B4(n + 1), B4(n + 1), B4(n + 2) 62 | B6(0), B6(1), B6(1), B6(2) 63 | }; 64 | 65 | unsigned long result = 0; 66 | unsigned long i; 67 | for (i = 0; i < count; ++i) { 68 | result += table[((unsigned char *) data)[i]]; 69 | } 70 | return result; 71 | } 72 | 73 | int is_alphabet(int c) 74 | { 75 | return !!strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", c); 76 | } 77 | 78 | int is_number(int c) 79 | { 80 | return c >= '0' && c <= '9'; 81 | } 82 | 83 | int is_space(int c) 84 | { 85 | return !!strchr(" \t\r\n", c); 86 | } 87 | 88 | int is_graphic(int c) 89 | { 90 | return is_alphabet(c) || is_number(c) || is_space(c) || !!strchr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", c); 91 | } 92 | 93 | long utf8_len(const char *str, long len) 94 | { 95 | if ((str[0] & 0x80) == 0x00) { 96 | return 1; 97 | } else if ((str[0] & 0xE0) == 0xC0) { 98 | return len >= 2 && (str[1] & 0xC0) == 0x80 ? 2 : -1; 99 | } else if ((str[0] & 0xF0) == 0xE0) { 100 | return len >= 3 && (str[1] & 0xC0) == 0x80 && (str[2] & 0xC0) == 0x80 ? 3 : -1; 101 | } else if ((str[0] & 0xF8) == 0xF0) { 102 | return len >= 4 && (str[1] & 0xC0) == 0x80 && (str[2] & 0xC0) == 0x80 && (str[3] & 0xC0) == 0x80 ? 4 : -1; 103 | } else { 104 | return -1; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/utility.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Shota Minami 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef UTILITY_H 18 | #define UTILITY_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | void *xmalloc(unsigned long size); 25 | void *dup(const void *ptr, unsigned long size, unsigned long count); 26 | 27 | #define FNV1A_INIT 0x811C9DC5ul 28 | 29 | unsigned long fnv1a(unsigned long hash, const void *ptr, unsigned long len); 30 | 31 | unsigned long popcount(void *data, unsigned long size); 32 | 33 | #define ULONG_BIT (sizeof(unsigned long) * CHAR_BIT) 34 | 35 | #define BITSET(name, bits) unsigned long name[(bits + ULONG_BIT - 1) / ULONG_BIT] 36 | 37 | #define bitset_set(self, index) (self[(index) / ULONG_BIT] |= 1ul << ((index) % ULONG_BIT)) 38 | #define bitset_reset(self, index) (self[(index) / ULONG_BIT] &= ~(1ul << ((index) % ULONG_BIT))) 39 | #define bitset_get(self, index) ((self[(index) / ULONG_BIT] >> ((index) % ULONG_BIT)) & 1ul) 40 | #define bitset_clear(self) memset(self, 0, sizeof(self)) 41 | #define bitset_count(self) popcount(self, sizeof(self)) 42 | 43 | int is_alphabet(int c); 44 | int is_number(int c); 45 | int is_space(int c); 46 | int is_graphic(int c); 47 | 48 | long utf8_len(const char *str, long len); 49 | 50 | #define unreachable() \ 51 | do { \ 52 | fprintf(stderr, "Internal Error: Entered unreachable region [%s:%d]\n", __FILE__, __LINE__); \ 53 | exit(EXIT_FAILURE); \ 54 | } while (0) 55 | 56 | #endif 57 | --------------------------------------------------------------------------------