├── src ├── tests │ ├── lexer_test.cct │ ├── parser_test.cct │ ├── test.cct │ ├── unit_tests.c │ ├── interpret_test.c │ ├── hash_map_test.c │ ├── object_test.c │ └── stack_test.c ├── debug.c ├── queue.c ├── stack.c ├── char_stream.c ├── seconds.c ├── types.c ├── vm │ ├── opcodes.c │ └── vm.c ├── hash_map.c ├── compiler.c └── concoct.c ├── .github ├── pull_request_template.md ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature-request.md │ └── bug-report.md ├── CODE_OF_CONDUCT.md ├── SECURITY.md ├── dependabot.yml ├── workflows │ ├── build.yml │ └── analyze.yml ├── CONTRIBUTING.md └── README.md ├── .gitignore ├── Containerfile ├── appveyor.yml ├── LICENSE.txt ├── lib └── linenoise │ ├── LICENSE │ ├── example.c │ ├── linenoise.h │ └── README.md ├── include ├── debug.h ├── compiler.h ├── stack.h ├── char_stream.h ├── concoct.h ├── queue.h ├── parser.h ├── hash_map.h ├── seconds.h ├── types.h ├── vm │ ├── instructions.h │ ├── vm.h │ └── opcodes.h ├── version.h.in ├── lexer.h └── memory.h ├── docs └── ConcoctGrammar.txt └── CMakeLists.txt /src/tests/lexer_test.cct: -------------------------------------------------------------------------------- 1 | var myVar = 5 + 3 2 | myVar2 = myVar++ 3 | myVar += 35 -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Proposed Change(s) 2 | - 3 | - 4 | - 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Get support on Matrix 4 | url: https://matrix.concoct.ist/ 5 | about: Live chat support 6 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | The Concoct Team is quite welcoming. The expectation is simply that people respect one another and make the best effort to resolve conflicts peacefully. 3 | -------------------------------------------------------------------------------- /src/tests/parser_test.cct: -------------------------------------------------------------------------------- 1 | do { 2 | if "Hello" { 3 | for test in tests { 4 | if test { 5 | x = (-5 * +3) / space.cls.obj 6 | } 7 | } 8 | msg = "Hello" 9 | } 10 | y = 3 11 | } 12 | while 5 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Create a request for a new feature 4 | title: '' 5 | labels: new feature 6 | assignees: '' 7 | --- 8 | 9 | ### Feature Description 10 | * 11 | 12 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please report any security vulnerabilities discovered in Concoct using the [issue tracker](https://github.com/Concoctist/concoct/issues/new). 6 | You may also notify us via [Matrix](https://matrix.concoct.ist/). 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | *.la 4 | *.so 5 | *.crt 6 | *.dll 7 | *.exe 8 | *.key 9 | *.lib 10 | *.log 11 | *.obj 12 | *.out 13 | *.pem 14 | *.pid 15 | *.core 16 | *.dylib 17 | build/ 18 | bld/ 19 | include/version.h 20 | logs/ 21 | doxygen/ 22 | cmake_install.cmake 23 | CMakeCache.txt 24 | CMakeFiles/ 25 | Makefile 26 | .vs/ 27 | .vscode/ 28 | /CMakeSettings.json 29 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | day: "sunday" 9 | time: "00:00" 10 | timezone: "Etc/UTC" 11 | assignees: 12 | - "bamalot" 13 | - "ldilley" 14 | reviewers: 15 | - "bamalot" 16 | - "ldilley" 17 | labels: 18 | - "task" 19 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [macos-latest, ubuntu-latest, windows-latest] 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: CMake 15 | run: | 16 | mkdir bld 17 | cd bld 18 | cmake .. 19 | cd .. 20 | cmake --build bld --config Debug 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to squash a potential bug 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | ### Expected Behavior 10 | * 11 | 12 | ### Actual Behavior/Symptom(s) 13 | * 14 | 15 | ### How to Reproduce Behavior/Symptom(s) 16 | * 17 | 18 | ### Concoct Version 19 | * 20 | 21 | ### Compiler Version 22 | * 23 | 24 | ### Operating System 25 | * 26 | 27 | ### Output/Screenshot(s) 28 | 29 | -------------------------------------------------------------------------------- /Containerfile: -------------------------------------------------------------------------------- 1 | # Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 2 | # Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 3 | # http://concoct.ist/ 4 | 5 | FROM alpine:latest AS construct 6 | RUN apk update && apk add clang cmake git make musl-dev && apk cache clean 7 | RUN cd /root && git clone https://github.com/concoctist/concoct.git 8 | RUN cd /root/concoct && mkdir bld && cd bld && cmake .. && make 9 | 10 | FROM alpine:latest 11 | RUN adduser -h /home/concoct -g "Concoct Account" -S concoct 12 | USER concoct 13 | WORKDIR /home/concoct 14 | COPY --from=construct --chown=concoct /root/concoct/bld/bin/concoct . 15 | CMD ["/home/concoct/concoct"] 16 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Concoct AppVeyor config 2 | version: 0.1.{build} 3 | image: Visual Studio 2017 4 | build_script: 5 | - mkdir build 6 | - cd build 7 | - cmake --version 8 | - cmake .. -G "Visual Studio 15 2017" 9 | - cmake --build . --target concoct --config Debug 10 | - bin\Debug\concoct.exe /v 11 | on_success: 12 | - ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1 13 | - ps: ./send.ps1 success $env:WEBHOOK_URL 14 | on_failure: 15 | - ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1 16 | - ps: ./send.ps1 failure $env:WEBHOOK_URL 17 | notifications: 18 | - provider: GitHubPullRequest 19 | on_build_success: true 20 | on_build_failure: true 21 | on_build_status_changed: true 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /lib/linenoise/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2014, Salvatore Sanfilippo 2 | Copyright (c) 2010-2013, Pieter Noordhuis 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /.github/workflows/analyze.yml: -------------------------------------------------------------------------------- 1 | name: "Code Analysis" 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | 7 | jobs: 8 | analyze: 9 | name: Analyze 10 | runs-on: ubuntu-latest 11 | permissions: 12 | actions: read 13 | contents: read 14 | security-events: write 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | language: [ 'cpp' ] 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Initialize CodeQL 26 | uses: github/codeql-action/init@v3 27 | with: 28 | languages: ${{ matrix.language }} 29 | # If you wish to specify custom queries, you can do so here or in a config file. 30 | # By default, queries listed here will override any specified in a config file. 31 | # Prefix the list here with "+" to use these queries and those in the config file. 32 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 33 | 34 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 35 | # If this step fails, then you should remove it and run the build manually (see below) 36 | - name: Autobuild 37 | uses: github/codeql-action/autobuild@v3 38 | 39 | # ℹ️ Command-line programs to run using the OS shell. 40 | # 📚 https://git.io/JvXDl 41 | 42 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 43 | # and modify them (or add more) to build your code if your project 44 | # uses a compiled language 45 | 46 | #- run: | 47 | # make bootstrap 48 | # make release 49 | 50 | - name: Perform CodeQL Analysis 51 | uses: github/codeql-action/analyze@v3 52 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Concoct 2 | 3 | ## Issue Tracker 4 | 5 | https://github.com/Concoctist/concoct/issues 6 | 7 | ## IRC 8 | 9 | irc://irc.concoct.ist:6697/concoct 10 | 11 | ## Matrix 12 | 13 | https://matrix.concoct.ist/ 14 | 15 | ## Wiki 16 | 17 | https://github.com/Concoctist/concoct/wiki 18 | 19 | ## Coding Convention 20 | 21 | Please follow the existing styling as closely as possible for any code contributions. Here are some tips to follow: 22 | 23 | * Use the C99 standard. 24 | 25 | * `//` should be used for comments on a single line. 26 | 27 | * Multi-line `/* ... */` comments are encouraged before functions to document them. 28 | 29 | * Use spaces and not tabs to be consistent with the existing code. 30 | 31 | * Indent by 2 spaces. 32 | 33 | * Custom types specified with `typedef` should have the first letter of their name capitalized. 34 | 35 | * Please use Allman style for blocks. For example: 36 | ```c 37 | int func() 38 | { 39 | // ... 40 | } 41 | ``` 42 | Rather than K&R style: 43 | ```c 44 | int func() { 45 | // ... 46 | } 47 | ``` 48 | 49 | * Use `const` when possible instead of `#define` for constants as it encourages stronger typing. 50 | 51 | * Constant variable names should be fully capitalized. For example: 52 | ```c 53 | const double PI = 3.141592653589793238463; 54 | ``` 55 | 56 | * Variable and function names containing multiple words should be separated with an underscore `_` rather than using camelCase or PascalCase. 57 | 58 | * `switch` cases with only a single statement should reside on a single line. The right side should also be vertically aligned. For example: 59 | ```c 60 | switch(oc) 61 | { 62 | case OP_ADD: return "OP_ADD"; 63 | case OP_OR: return "OP_OR"; 64 | // ... 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /include/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef DEBUG_H 29 | #define DEBUG_H 30 | 31 | #include "types.h" 32 | 33 | extern bool debug_mode; 34 | 35 | #define TIMESTAMP_LENGTH ((uint8_t)64) 36 | #define TIMEZONE_LENGTH ((uint8_t)64) 37 | 38 | void debug_print(const char* message, ...); 39 | 40 | #endif // DEBUG_H 41 | -------------------------------------------------------------------------------- /src/tests/test.cct: -------------------------------------------------------------------------------- 1 | ## 2 | Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | http://concoct.ist/ 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | ## 27 | 28 | # This is a test file written in Concoct. 29 | 30 | use testlib # import testlib 31 | 32 | func add(a, b) 33 | { 34 | a + b # last statement of a function is evaluated and the value is implicitly returned 35 | } 36 | 37 | intro = "This is a Concoct test program." 38 | x = 5 39 | y = 3 40 | put(intro) # outputs string defined for intro variable 41 | put(add(x, y)) # outputs 8 42 | -------------------------------------------------------------------------------- /include/compiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef COMPILER_H 29 | #define COMPILER_H 30 | 31 | #include "hash_map.h" 32 | #include "parser.h" 33 | #include "queue.h" 34 | #include "vm/opcodes.h" 35 | 36 | // Swap last 2 stack objects to fix order if first instruction is a binary operation 37 | void swap_last_operands(Opcode oc); 38 | 39 | // Translates lexer/parser tokens to VM instructions 40 | void compile(ConcoctNodeTree* tree, ConcoctHashMap* map); 41 | 42 | #endif // COMPILER_H 43 | -------------------------------------------------------------------------------- /include/stack.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef STACK_H 29 | #define STACK_H 30 | 31 | #include // NULL, size_t 32 | #include "types.h" 33 | 34 | #define MAX_STACK_CAPACITY ((size_t)128) 35 | 36 | typedef struct stack 37 | { 38 | int16_t top; 39 | size_t count; 40 | void* objects[MAX_STACK_CAPACITY]; 41 | } Stack; 42 | 43 | // Initializes stack 44 | void init_stack(Stack* stack); 45 | 46 | // Returns object at top of stack without removal 47 | void* peek(const Stack* stack); 48 | 49 | // Returns and removes object at top of stack 50 | void* pop(Stack* stack); 51 | 52 | // Pushes new object on top of stack 53 | void push(Stack* stack, void* object); 54 | 55 | #endif // STACK_H 56 | -------------------------------------------------------------------------------- /include/char_stream.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef CCT_CHAR_STREAM_H 29 | #define CCT_CHAR_STREAM_H 30 | 31 | #include // FILE 32 | 33 | typedef enum concoct_char_stream_type 34 | { 35 | CCT_CHAR_STREAM_FILE, 36 | CCT_CHAR_STREAM_STRING 37 | } ConcoctCharStreamType; 38 | 39 | typedef struct concoct_char_stream 40 | { 41 | ConcoctCharStreamType type; 42 | union 43 | { 44 | FILE* file_input; 45 | const char* string_input; 46 | } input; 47 | int index; 48 | } ConcoctCharStream; 49 | 50 | // Character stream constructors 51 | ConcoctCharStream* cct_new_file_char_stream(FILE* in_file); 52 | ConcoctCharStream* cct_new_string_char_stream(const char* in_string); 53 | 54 | char cct_get_char_from_stream(ConcoctCharStream* stream); 55 | int cct_char_stream_eof(ConcoctCharStream* stream); 56 | void cct_delete_char_stream(ConcoctCharStream* stream); 57 | 58 | #endif // CCT_CHAR_STREAM_H 59 | -------------------------------------------------------------------------------- /include/concoct.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef CONCOCT_H 29 | #define CONCOCT_H 30 | 31 | #include // bool 32 | 33 | #define KEYWORD_AMOUNT ((uint8_t)25) 34 | #define KEYWORD_LENGTH ((uint8_t)32) 35 | 36 | #ifdef _WIN32 37 | static const char ARG_PREFIX = '/'; 38 | #else 39 | static const char ARG_PREFIX = '-'; 40 | #endif 41 | 42 | // Used to flag unused variables and silence compiler warnings 43 | #define UNUSED(x) (void)(x) 44 | 45 | void clean_exit(int status); 46 | void lex_file(const char* file_name); 47 | void lex_string(const char* input_string); 48 | void parse_file(const char* file_name); 49 | void parse_string(const char* input_string); 50 | void handle_options(int argc, char *argv[]); 51 | bool case_compare(const char* str1, const char* str2); 52 | void print_license(void); 53 | void print_usage(void); 54 | void print_version(void); 55 | void handle_sigint(int sig); 56 | void interactive_mode(void); 57 | 58 | #endif // CONCOCT_H 59 | -------------------------------------------------------------------------------- /include/queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef QUEUE_H 29 | #define QUEUE_H 30 | 31 | #include // bool 32 | #include // size_t 33 | 34 | #define MAX_QUEUE_CAPACITY ((size_t)256) 35 | 36 | typedef struct queue 37 | { 38 | int front; 39 | int back; 40 | size_t count; 41 | void* objects[MAX_QUEUE_CAPACITY]; 42 | } Queue; 43 | 44 | // Initializes queue 45 | void init_queue(Queue* queue); 46 | 47 | // Returns true if queue is empty 48 | bool is_empty(const Queue* queue); 49 | 50 | // Returns true if queue is full 51 | bool is_full(const Queue* queue); 52 | 53 | // Returns size of queue 54 | size_t size(const Queue* queue); 55 | 56 | // Returns object at the back of queue 57 | void* back(const Queue* queue); 58 | 59 | // Returns object at the front of queue 60 | void* front(const Queue* queue); 61 | 62 | // Returns and removes next object from queue 63 | void dequeue(Queue* queue, void** object); 64 | 65 | // Inserts new object into queue 66 | void enqueue(Queue* queue, void* object); 67 | 68 | #endif // QUEUE_H 69 | -------------------------------------------------------------------------------- /include/parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef PARSER_H 29 | #define PARSER_H 30 | 31 | #include "lexer.h" 32 | 33 | static const size_t CCT_NODE_COUNT_PER_BLOCK = 256; 34 | 35 | typedef struct ConcoctNode 36 | { 37 | ConcoctToken token; 38 | char* text; 39 | struct ConcoctNode* parent; 40 | size_t child_count; 41 | struct ConcoctNode** children; 42 | } ConcoctNode; 43 | 44 | typedef struct concoct_node_tree 45 | { 46 | ConcoctNode** nodes; 47 | size_t node_count; 48 | size_t node_max; 49 | ConcoctNode* root; 50 | } ConcoctNodeTree; 51 | 52 | typedef struct concoct_parser 53 | { 54 | ConcoctLexer* lexer; 55 | ConcoctNodeTree* tree; 56 | ConcoctToken current_token; 57 | size_t error_line; 58 | const char* error; 59 | } ConcoctParser; 60 | 61 | ConcoctNode* cct_new_node(ConcoctNodeTree* tree, ConcoctToken token, const char* text); 62 | ConcoctParser* cct_new_parser(ConcoctLexer* lexer); 63 | void cct_delete_parser(ConcoctParser* parser); 64 | void cct_delete_node_tree(ConcoctNodeTree* tree); 65 | ConcoctNode* cct_node_add_child(ConcoctNode* node, ConcoctNode* child); 66 | ConcoctNodeTree* cct_parse_program(ConcoctParser* parser); 67 | ConcoctNode* cct_parse_stat(ConcoctParser* parser); 68 | ConcoctNode* cct_parse_expr(ConcoctParser* parser); 69 | ConcoctToken cct_next_parser_token(ConcoctParser* parser); 70 | void cct_print_node(const ConcoctNode* node, size_t tab_level); 71 | 72 | #endif // PARSER_H 73 | -------------------------------------------------------------------------------- /include/hash_map.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef CCT_HASH_MAP_H 29 | #define CCT_HASH_MAP_H 30 | 31 | #include // false, true 32 | #include // uint32_t 33 | 34 | #define CCT_HASH_OFFSET ((uint32_t)2166136261) 35 | #define CCT_HASH_PRIME ((uint32_t)16777619) 36 | 37 | #define INITIAL_BUCKET_AMOUNT ((uint16_t)128) 38 | 39 | typedef struct ConcoctHashMapNode ConcoctHashMapNode; 40 | 41 | struct ConcoctHashMapNode 42 | { 43 | uint32_t hash; 44 | const char* key; 45 | void* value; 46 | ConcoctHashMapNode* next; 47 | }; 48 | 49 | typedef struct concoct_hash_map 50 | { 51 | ConcoctHashMapNode** buckets; 52 | uint32_t bucket_count; 53 | } ConcoctHashMap; 54 | 55 | ConcoctHashMapNode* cct_new_hash_map_node(const char* key, void* value, uint32_t hash); 56 | void cct_delete_hash_map_node(ConcoctHashMapNode* map); 57 | 58 | ConcoctHashMap* cct_new_hash_map(uint32_t bucket_count); 59 | void cct_delete_hash_map(ConcoctHashMap* map); 60 | 61 | bool cct_hash_map_has_key(const ConcoctHashMap* map, const char* key); 62 | void cct_hash_map_set(ConcoctHashMap* map, const char* key, void* value); 63 | void* cct_hash_map_get(const ConcoctHashMap* map, const char* key); 64 | void cct_hash_map_delete_entry(ConcoctHashMap* map, const char* key); 65 | 66 | ConcoctHashMapNode* get_first_node_in_bucket(const ConcoctHashMap* map, uint32_t bucket); 67 | 68 | uint32_t cct_get_hash_code(const char* str); 69 | 70 | #endif // CCT_HASH_MAP_H 71 | -------------------------------------------------------------------------------- /include/seconds.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef SECONDS_H 29 | #define SECONDS_H 30 | 31 | #include 32 | #ifndef _WIN32 33 | #include // timeval 34 | #endif // _WIN32 35 | 36 | static const uint32_t MICROSECONDS_PER_SECOND = 1000000ULL; 37 | 38 | #ifdef _WIN32 39 | #define WIN32_LEAN_AND_MEAN 40 | #include 41 | 42 | /* 43 | Since this project conforms to the C99 standard, portable gettimeofday() functionality is necessary for Windows. 44 | Otherwise, timespec from the C11 standard could be used to obtain microseconds. Visual Studio does not support 45 | timespec_get() until VS 2019 version 16.8.0. 46 | */ 47 | 48 | typedef int32_t suseconds_t; 49 | 50 | // Seconds between Windows epoch (01-01-1601 00:00:00 UTC) and Unix epoch (01-01-1970 00:00:00 UTC) 51 | static const uint64_t EPOCH_OFFSET = 11644473600000000ULL; 52 | 53 | typedef struct timeval 54 | { 55 | time_t tv_sec; // seconds 56 | suseconds_t tv_usec; // microseconds 57 | } timeval; 58 | 59 | struct timezone 60 | { 61 | int tz_minuteswest; // minutes west of Greenwich 62 | int tz_dsttime; // type of DST correction 63 | }; 64 | 65 | // Fills timeval structure with the number of seconds and microseconds since the Unix epoch 66 | int gettimeofday(struct timeval* tv, struct timezone* tz); 67 | #endif // _WIN32 68 | 69 | // Returns delta of 2 timevals 70 | double microdelta(time_t startsec, suseconds_t startusec, const struct timeval* stop); 71 | 72 | #endif // SECONDS_H 73 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // PRId64 29 | #include // va_end, va_list, va_start, vprintf() 30 | #include // printf() 31 | #include // strlen() 32 | #include // strftime(), time(), time_t, tm 33 | #include "debug.h" // debug_print(), debug_mode, TIMESTAMP_LENGTH 34 | #include "seconds.h" // gettimeofday(), microdelta() 35 | 36 | bool debug_mode = false; 37 | 38 | // Prints debug messages 39 | void debug_print(const char* message, ...) 40 | { 41 | char timestamp[TIMESTAMP_LENGTH]; 42 | char time_zone[TIMEZONE_LENGTH]; 43 | time_t rawtime; 44 | const struct tm* timedata; 45 | struct timeval tv; 46 | static time_t oldtvsec = 0; 47 | static suseconds_t oldtvusec = 0; 48 | va_list args; 49 | 50 | gettimeofday(&tv, NULL); 51 | time(&rawtime); 52 | timedata = localtime(&rawtime); 53 | va_start(args, message); 54 | strftime(timestamp, TIMESTAMP_LENGTH, "[%F %T", timedata); 55 | strftime(time_zone, TIMEZONE_LENGTH, "%z", timedata); 56 | if(strlen(time_zone) > 0) // time zone may be 0 bytes 57 | printf("Debug: %s.%06" PRId64 " %s +/-%0.6f] - ", timestamp, (int64_t)tv.tv_usec, time_zone, microdelta(oldtvsec, oldtvusec, &tv)); 58 | else 59 | printf("Debug: %s.%06" PRId64 " +/-%0.6f] - ", timestamp, (int64_t)tv.tv_usec, microdelta(oldtvsec, oldtvusec, &tv)); 60 | oldtvsec = tv.tv_sec; 61 | oldtvusec = tv.tv_usec; 62 | vprintf(message, args); 63 | va_end(args); 64 | printf("\n"); 65 | return; 66 | } 67 | -------------------------------------------------------------------------------- /src/tests/unit_tests.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // assert() 29 | #include // free() 30 | #include // strcmp(), strncpy() 31 | #include "memory.h" // stringify() 32 | 33 | void test_stringify(void) 34 | { 35 | char* str = NULL; 36 | 37 | Byte byteval = 128; 38 | void* vptr = &byteval; 39 | stringify(&str, vptr, CCT_TYPE_BYTE); 40 | assert(strcmp(str, "128") == 0); 41 | free(str); 42 | 43 | Number numval = 42; 44 | vptr = &numval; 45 | stringify(&str, vptr, CCT_TYPE_NUMBER); 46 | assert(strcmp(str, "42") == 0); 47 | free(str); 48 | 49 | Decimal decimalval = 57.05; 50 | char dec[16]; 51 | vptr = &decimalval; 52 | stringify(&str, vptr, CCT_TYPE_DECIMAL); 53 | if(str && strlen(str) > 5) 54 | { 55 | strncpy(dec, str, 5); 56 | dec[5] = '\0'; 57 | assert(strcmp(dec, "57.05") == 0); 58 | } 59 | free(str); 60 | 61 | char* testval = "Foo bar baz"; 62 | vptr = &testval; 63 | stringify(&str, vptr, CCT_TYPE_STRING); 64 | assert(strcmp(str, "Foo bar baz") == 0); 65 | free(str); 66 | 67 | Bool boolval = false; 68 | vptr = &boolval; 69 | stringify(&str, vptr, CCT_TYPE_BOOL); 70 | assert(strcmp(str, "false") == 0); 71 | free(str); 72 | 73 | boolval = true; 74 | vptr = &boolval; 75 | stringify(&str, vptr, CCT_TYPE_BOOL); 76 | assert(strcmp(str, "true") == 0); 77 | free(str); 78 | 79 | char* nullval = NULL; 80 | vptr = &nullval; 81 | stringify(&str, vptr, CCT_TYPE_NIL); 82 | assert(strcmp(str, "null") == 0); 83 | free(str); 84 | 85 | return; 86 | } 87 | 88 | int main(void) 89 | { 90 | test_stringify(); 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /src/queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "debug.h" 29 | #include "queue.h" 30 | 31 | // Initializes queue 32 | void init_queue(Queue* queue) 33 | { 34 | queue->count = 0; 35 | queue->back = MAX_QUEUE_CAPACITY - 1; 36 | queue->front = 0; 37 | if(debug_mode) 38 | debug_print("Queue initialized with %zu slots.", MAX_QUEUE_CAPACITY); 39 | return; 40 | } 41 | 42 | // Returns true if queue is empty 43 | bool is_empty(const Queue* queue) 44 | { 45 | return queue->count == 0; 46 | } 47 | 48 | // Returns true if queue is full 49 | bool is_full(const Queue* queue) 50 | { 51 | return queue->count == MAX_QUEUE_CAPACITY; 52 | } 53 | 54 | // Returns size of queue 55 | size_t size(const Queue* queue) 56 | { 57 | return queue->count; 58 | } 59 | 60 | // Returns object at the back of queue 61 | void* back(const Queue* queue) 62 | { 63 | if(is_empty(queue)) 64 | return NULL; 65 | return queue->objects[queue->back]; 66 | } 67 | 68 | // Returns object at the front of queue 69 | void* front(const Queue* queue) 70 | { 71 | if(is_empty(queue)) 72 | return NULL; 73 | return queue->objects[queue->front]; 74 | } 75 | 76 | // Returns and removes next object from queue 77 | void dequeue(Queue* queue, void** object) 78 | { 79 | if(is_empty(queue)) 80 | return; 81 | *object = queue->objects[queue->front]; 82 | queue->front = (queue->front + 1) % MAX_QUEUE_CAPACITY; 83 | queue->count--; 84 | return; 85 | } 86 | 87 | // Inserts new object into queue 88 | void enqueue(Queue* queue, void* object) 89 | { 90 | if(is_full(queue)) 91 | return; 92 | queue->back = (queue->back + 1) % MAX_QUEUE_CAPACITY; 93 | queue->objects[queue->back] = object; 94 | queue->count++; 95 | return; 96 | } 97 | -------------------------------------------------------------------------------- /src/stack.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // fprintf(), stderr 29 | #include // EXIT_FAILURE 30 | #include "debug.h" 31 | #include "stack.h" 32 | 33 | void init_stack(Stack* stack) 34 | { 35 | stack->count = 0; 36 | stack->top = -1; 37 | if(debug_mode) 38 | debug_print("Stack initialized with %zu slots.", MAX_STACK_CAPACITY); 39 | return; 40 | } 41 | 42 | // Returns object at top of stack without removal 43 | void* peek(const Stack* stack) 44 | { 45 | if(stack->top == -1) 46 | { 47 | if(debug_mode) 48 | debug_print("peek() called on empty stack!"); 49 | return NULL; 50 | } 51 | if(debug_mode) 52 | debug_print("peek() called on stack for object of type %s. Stack currently contains %zu objects.", get_data_type(stack->objects[stack->top]), stack->count); 53 | return stack->objects[stack->top]; 54 | } 55 | 56 | // Returns and removes object at top of stack 57 | void* pop(Stack* stack) 58 | { 59 | if(stack->top == -1) 60 | { 61 | fprintf(stderr, "Stack underflow occurred!\n"); 62 | return NULL; 63 | } 64 | stack->count--; 65 | if(debug_mode) 66 | debug_print("pop() called on stack for object of type %s. Stack now contains %zu objects.", get_data_type(stack->objects[stack->top]), stack->count); 67 | return stack->objects[stack->top--]; 68 | } 69 | 70 | // Pushes new object on top of stack 71 | void push(Stack* stack, void* object) 72 | { 73 | if(stack->top >= ((int)MAX_STACK_CAPACITY - 1)) 74 | { 75 | fprintf(stderr, "Stack overflow occurred!\n"); 76 | return; 77 | } 78 | stack->count++; 79 | stack->objects[++stack->top] = object; 80 | if(debug_mode) 81 | debug_print("push() called on stack for object of type %s. Stack now contains %zu objects.", get_data_type(stack->objects[stack->top]), stack->count); 82 | return; 83 | } 84 | -------------------------------------------------------------------------------- /src/char_stream.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // errno 29 | #include // malloc() 30 | #include // strerror() 31 | #include "char_stream.h" 32 | 33 | ConcoctCharStream* cct_new_file_char_stream(FILE* in_file) 34 | { 35 | ConcoctCharStream* stream = malloc(sizeof(ConcoctCharStream)); 36 | 37 | if(stream == NULL) 38 | { 39 | fprintf(stderr, "Error allocating memory for lexer: %s\n", strerror(errno)); 40 | return NULL; 41 | } 42 | 43 | stream->type = CCT_CHAR_STREAM_FILE; 44 | stream->input.file_input = in_file; 45 | stream->index = 0; 46 | return stream; 47 | } 48 | 49 | ConcoctCharStream* cct_new_string_char_stream(const char* in_string) 50 | { 51 | ConcoctCharStream* stream = malloc(sizeof(ConcoctCharStream)); 52 | 53 | if(stream == NULL) 54 | { 55 | fprintf(stderr, "Error allocating memory for lexer: %s\n", strerror(errno)); 56 | return NULL; 57 | } 58 | 59 | stream->type = CCT_CHAR_STREAM_STRING; 60 | stream->input.string_input = in_string; 61 | stream->index = 0; 62 | return stream; 63 | } 64 | 65 | char cct_get_char_from_stream(ConcoctCharStream* stream) 66 | { 67 | if(!cct_char_stream_eof(stream)) 68 | { 69 | stream->index += 1; 70 | if(stream->type == CCT_CHAR_STREAM_STRING) 71 | { 72 | char ch = stream->input.string_input[stream->index - 1]; 73 | return ch; 74 | } 75 | return (char)getc(stream->input.file_input); 76 | } 77 | return '\0'; 78 | } 79 | 80 | int cct_char_stream_eof(ConcoctCharStream* stream) 81 | { 82 | if(stream->type == CCT_CHAR_STREAM_STRING) 83 | { 84 | return stream->input.string_input[stream->index] == '\0'; 85 | } 86 | return feof(stream->input.file_input); 87 | } 88 | 89 | void cct_delete_char_stream(ConcoctCharStream* stream) 90 | { 91 | free(stream); 92 | } 93 | -------------------------------------------------------------------------------- /include/types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef TYPES_H 29 | #define TYPES_H 30 | 31 | #include // bool 32 | #include // NULL, size_t 33 | #include // uint8_t, int32_t, int64_t 34 | 35 | // Concoct data types 36 | typedef bool Bool; // true/false 37 | typedef uint8_t Byte; // unsigned char 38 | typedef int32_t Number; // signed long integer 39 | typedef int64_t BigNum; // signed long long integer 40 | typedef double Decimal; // decimal 41 | typedef struct cct_string // string 42 | { 43 | size_t length; 44 | char* strval; 45 | } String; 46 | 47 | typedef enum data_type 48 | { 49 | CCT_TYPE_NIL, 50 | CCT_TYPE_BOOL, 51 | CCT_TYPE_BYTE, 52 | CCT_TYPE_NUMBER, 53 | CCT_TYPE_BIGNUM, 54 | CCT_TYPE_DECIMAL, 55 | CCT_TYPE_STRING 56 | } DataType; 57 | 58 | // Concoct object 59 | typedef struct object 60 | { 61 | DataType datatype; 62 | Bool is_flagged; // flagged to prevent garbage collection 63 | Bool is_global; // is a global variable 64 | char* const_name; // name of constant (constants are never considered for garbage collection) 65 | union 66 | { 67 | Bool boolval; 68 | Byte byteval; 69 | Number numval; 70 | BigNum bignumval; 71 | Decimal decimalval; 72 | String strobj; // strval is contained in String struct 73 | } value; 74 | } Object; 75 | 76 | // Returns string representation of data type 77 | const char* get_type(DataType datatype); 78 | 79 | // Returns string representation of data type from object 80 | const char* get_data_type(const Object* object); 81 | 82 | // Returns value of object 83 | void* get_object_value(Object* object); 84 | 85 | // Displays value of object 86 | void print_object_value(Object* object); 87 | 88 | // Converts string to applicable type 89 | void convert_type(Object* object, char* value); 90 | 91 | #endif // TYPES_H 92 | -------------------------------------------------------------------------------- /include/vm/instructions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef INSTRUCTIONS_H 29 | #define INSTRUCTIONS_H 30 | 31 | #include "hash_map.h" 32 | #include "stack.h" 33 | #include "vm/vm.h" 34 | 35 | #define OP_NOOP (void)0 36 | 37 | RunCode unary_operand_check(const Object* operand, char* operator); 38 | RunCode binary_operand_check(const Object* operand1, const Object* operand2, char* operator); 39 | RunCode binary_operand_check_str(const Object* operand1, const Object* operand2, char* operator); 40 | RunCode op_clr(Object** rp); 41 | RunCode op_cls(Stack* stack); 42 | RunCode op_lod(Object** rp, Stack* stack, Byte dst_reg); 43 | RunCode op_mov(Object** rp, Object* object, Byte src_reg, Byte dst_reg); 44 | RunCode op_str(Object** rp, Stack* stack, Byte src_reg); 45 | RunCode op_xcg(Object** rp, Byte reg1, Byte reg2); 46 | RunCode op_pop(Stack* stack, const Object* object); 47 | RunCode op_psh(Stack* stack, char* value); 48 | RunCode op_asn(Stack* stack, ConcoctHashMap* map); 49 | RunCode op_and(Stack* stack); 50 | RunCode op_not(Stack* stack); 51 | RunCode op_or(Stack* stack); 52 | RunCode op_eql(Stack* stack); 53 | RunCode op_neq(Stack* stack); 54 | RunCode op_sle(Stack* stack); 55 | RunCode op_sln(Stack* stack); 56 | RunCode op_gt(Stack* stack); 57 | RunCode op_gte(Stack* stack); 58 | RunCode op_lt(Stack* stack); 59 | RunCode op_lte(Stack* stack); 60 | RunCode op_neg(Stack* stack); 61 | RunCode op_pos(Stack* stack); 62 | RunCode op_dec(Stack* stack); 63 | RunCode op_inc(Stack* stack); 64 | RunCode op_add(Stack* stack); 65 | RunCode op_sub(Stack* stack); 66 | RunCode op_div(Stack* stack); 67 | RunCode op_mul(Stack* stack); 68 | RunCode op_mod(Stack* stack); 69 | RunCode op_pow(Stack* stack); 70 | RunCode op_bnd(Stack* stack); 71 | RunCode op_bor(Stack* stack); 72 | RunCode op_xor(Stack* stack); 73 | RunCode op_bnt(Stack* stack); 74 | RunCode op_shl(Stack* stack); 75 | RunCode op_shr(Stack* stack); 76 | 77 | #endif // INSTRUCTIONS_H 78 | -------------------------------------------------------------------------------- /src/tests/interpret_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "debug.h" 29 | #include "hash_map.h" 30 | #include "memory.h" 31 | #include "vm/vm.h" 32 | 33 | int main(void) 34 | { 35 | Object* object = NULL; 36 | void* vptr = NULL; 37 | BigNum numval = -8675309; 38 | ConcoctHashMap* map = cct_new_hash_map(INITIAL_BUCKET_AMOUNT); 39 | debug_mode = true; 40 | init_vm(); 41 | 42 | vm.instructions[14] = OP_XCG; 43 | vm.instructions[13] = OP_LOD; 44 | vm.instructions[12] = OP_STR; 45 | vm.instructions[11] = OP_MOV; 46 | vptr = &numval; 47 | object = new_object_by_type(vptr, CCT_TYPE_BIGNUM); 48 | vm.rp[R1] = object; 49 | vm.rp[RS] = object; 50 | vm.instructions[10] = OP_RET; 51 | numval = -5552424; 52 | vptr = &numval; 53 | push(vm.sp, new_object_by_type(vptr, CCT_TYPE_BIGNUM)); 54 | vm.instructions[9] = OP_BNT; 55 | push(vm.sp, new_object("217")); 56 | push(vm.sp, new_object("107")); 57 | push(vm.sp, new_object("32")); 58 | vm.instructions[8] = OP_XOR; 59 | push(vm.sp, new_object("32")); 60 | push(vm.sp, new_object("8")); 61 | vm.instructions[7] = OP_AND; 62 | push(vm.sp, new_object("true")); 63 | push(vm.sp, new_object("true")); 64 | vm.instructions[6] = OP_NEG; 65 | push(vm.sp, new_object("35.5")); 66 | vm.instructions[5] = OP_NOP; 67 | vm.instructions[4] = OP_ADD; 68 | push(vm.sp, new_object("2")); 69 | push(vm.sp, new_object("3")); 70 | vm.instructions[3] = OP_SUB; 71 | push(vm.sp, new_object("10")); 72 | push(vm.sp, new_object("3")); 73 | vm.instructions[2] = OP_MUL; 74 | push(vm.sp, new_object("5")); 75 | push(vm.sp, new_object("2")); 76 | vm.instructions[1] = OP_DEC; 77 | push(vm.sp, new_object("99")); 78 | vm.instructions[0] = OP_POW; 79 | push(vm.sp, new_object("5")); 80 | push(vm.sp, new_object("2")); 81 | vm.instructions[15] = OP_CLR; 82 | vm.instructions[16] = OP_CLS; 83 | vm.instructions[17] = OP_END; 84 | 85 | interpret(map); 86 | cct_delete_hash_map(map); 87 | stop_vm(); 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /include/version.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef VERSION_H 29 | #define VERSION_H 30 | 31 | const char* const VERSION = "0.1.0"; 32 | const char* const GIT_REV = "@GIT_REV@"; 33 | const char* const GIT_HASH = "@GIT_HASH@"; 34 | const char* const BUILD_TIME = "@BUILD_TIME@"; 35 | const char* const BUILD_DATE = "@BUILD_DATE@"; 36 | const char* const BUILD_TYPE = "@BUILD_TYPE@"; 37 | 38 | #if defined(_WIN64) || defined(x64) || defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || \ 39 | defined(__aarch64__) || defined(__ppc64__) || defined(__powerpc64__) || defined(__sparcv9) || defined(__sparc_v9__) || \ 40 | defined(_PA_RISC2_0) || defined(__RISC2_0__) || defined(__HPPA20__) || defined(__PA8000__) || defined(__alpha) || \ 41 | defined(__alpha__) || defined(__ia64) || defined(__ia64__) 42 | #define BITNESS "64-bit" 43 | #else 44 | #define BITNESS "32-bit" 45 | #endif 46 | 47 | #if defined(_AIX) 48 | #define PLATFORM "AIX" 49 | #elif defined(__ANDROID__) 50 | #define PLATFORM "Android" 51 | #elif defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) 52 | #define PLATFORM "Windows" 53 | #elif defined(__DragonFly__) 54 | #define PLATFORM "DragonFlyBSD" 55 | #elif defined(__FreeBSD__) 56 | #define PLATFORM "FreeBSD" 57 | #elif defined(hpux) || defined(_hpux) || defined(__hpux) 58 | #define PLATFORM "HP-UX" 59 | #elif defined(__linux__) 60 | #define PLATFORM "Linux" 61 | #elif defined(__APPLE__) && defined(__MACH__) 62 | #define PLATFORM "macOS" 63 | #elif defined(__minix) 64 | #define PLATFORM "Minix" 65 | #elif defined(__NetBSD__) 66 | #define PLATFORM "NetBSD" 67 | #elif defined(__OpenBSD__) 68 | #define PLATFORM "OpenBSD" 69 | #elif defined(sun) || defined(__sun) 70 | #if defined(__SVR4) || defined(__svr4__) 71 | #define PLATFORM "Solaris" 72 | #else 73 | #define PLATFORM "SunOS" 74 | #endif 75 | #elif defined(__unix) || defined(__unix__) 76 | #define PLATFORM "Unix" 77 | #else 78 | #define PLATFORM "Unknown" 79 | #endif 80 | 81 | #endif // VERSION_H 82 | -------------------------------------------------------------------------------- /src/seconds.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // fabs() 29 | #include "seconds.h" 30 | 31 | #ifdef _WIN32 32 | /* 33 | Since this project conforms to the C99 standard, portable gettimeofday() functionality is necessary for Windows. 34 | Otherwise, timespec from the C11 standard could be used to obtain microseconds. Visual Studio does not support 35 | timespec_get() until VS 2019 version 16.8.0. 36 | */ 37 | 38 | // Fills timeval structure with the number of seconds and microseconds since the Unix epoch 39 | int gettimeofday(struct timeval* tv, struct timezone* tz) 40 | { 41 | FILETIME ft; // 64-bit value representing the number of 100-nanosecond intervals since 01-01-1601 00:00 UTC 42 | uint64_t microsecs; // microseconds 43 | TIME_ZONE_INFORMATION timezone; 44 | 45 | if(tv) 46 | { 47 | #if WINVER >= _WIN32_WINNT_WIN8 48 | GetSystemTimePreciseAsFileTime(&ft); 49 | #else 50 | GetSystemTimeAsFileTime(&ft); 51 | #endif 52 | // Concatenate high and low values of FILETIME struct fields and remove seconds between Windows and Unix epoch 53 | microsecs = (((uint64_t)ft.dwHighDateTime << 32) + ft.dwLowDateTime) / 10 - EPOCH_OFFSET; 54 | // Save number of seconds 55 | tv->tv_sec = (time_t)(microsecs / MICROSECONDS_PER_SECOND); 56 | // Save number of microseconds 57 | tv->tv_usec = (suseconds_t)(microsecs % MICROSECONDS_PER_SECOND); 58 | } 59 | 60 | // Time zones are not used for this project, but this is here for completeness 61 | if(tz) 62 | { 63 | GetTimeZoneInformation(&timezone); 64 | tz->tz_minuteswest = timezone.Bias; 65 | tz->tz_dsttime = 0; 66 | } 67 | 68 | return 0; 69 | } 70 | #endif // _WIN32 71 | 72 | // Returns delta of 2 timevals 73 | double microdelta(time_t startsec, suseconds_t startusec, const struct timeval* stop) 74 | { 75 | double start_total = startsec + (double)startusec / MICROSECONDS_PER_SECOND; 76 | double stop_total = stop->tv_sec + (double)stop->tv_usec / MICROSECONDS_PER_SECOND; 77 | if(start_total == 0.0) 78 | return 0.0; 79 | return fabs(stop_total - start_total); 80 | } 81 | -------------------------------------------------------------------------------- /include/vm/vm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef VM_H 29 | #define VM_H 30 | 31 | #include "hash_map.h" 32 | #include "stack.h" 33 | #include "types.h" // BigNum, Byte 34 | #include "vm/opcodes.h" // Opcode 35 | 36 | #define REGISTER_AMOUNT ((uint8_t)17) 37 | static const uint8_t REGISTER_EMPTY = 127; 38 | static const size_t INSTRUCTION_STORE_SIZE = 128; 39 | 40 | typedef struct vm 41 | { 42 | Opcode* instructions; // instructions to execute 43 | Object* registers[REGISTER_AMOUNT]; // registers 44 | Object** rp; // register pointer 45 | Stack stack; // stack structure 46 | Stack* sp; // stack pointer/top item of stack 47 | Opcode* ip; // instruction pointer/program counter 48 | } VM; 49 | extern VM vm; 50 | 51 | // Register names/indexes 52 | static const Byte R0 = 0; 53 | static const Byte R1 = 1; 54 | static const Byte R2 = 2; 55 | static const Byte R3 = 3; 56 | static const Byte R4 = 4; 57 | static const Byte R5 = 5; 58 | static const Byte R6 = 6; 59 | static const Byte R7 = 7; 60 | static const Byte R8 = 8; 61 | static const Byte R9 = 9; 62 | static const Byte R10 = 10; 63 | static const Byte R11 = 11; 64 | static const Byte R12 = 12; 65 | static const Byte R13 = 13; 66 | static const Byte R14 = 14; 67 | static const Byte R15 = 15; 68 | static const Byte RS = 16; // result 69 | extern Opcode** IP; // instruction pointer 70 | extern Object** RP; // register pointer 71 | extern Stack** SP; // stack pointer 72 | 73 | typedef enum 74 | { 75 | RUN_SUCCESS, 76 | RUN_ERROR 77 | } RunCode; 78 | 79 | // Clears instructions 80 | void clear_instructions(void); 81 | 82 | // Reverses instructions since they should be in a LIFO arrangement 83 | void reverse_instructions(size_t ic); 84 | 85 | // Prints register values 86 | void print_registers(void); 87 | 88 | // Initializes virtual machine 89 | void init_vm(void); 90 | 91 | // Stops virtual machine 92 | void stop_vm(void); 93 | 94 | // Interprets code 95 | RunCode interpret(ConcoctHashMap* map); 96 | 97 | #endif // VM_H 98 | -------------------------------------------------------------------------------- /docs/ConcoctGrammar.txt: -------------------------------------------------------------------------------- 1 | (* Grammar draft for Concoct *) 2 | digit = "0" - "9"; 3 | letter = "a" - "z" | "A" - "Z"; 4 | any_char = ?any character?; 5 | 6 | identifier = letter | "_", {letter | digit | "_"}; 7 | 8 | int_expr = digit, {digit}; 9 | float_expr = digit, {digit}, ".", digit, {digit}; 10 | string_expr = '"', {any_char}, '"'; 11 | char_expr = "'", any_char, "'"; 12 | bool_expr = "true" | "false"; 13 | 14 | add_expr = expr, "+", expr; 15 | subtract_expr = expr, "-", expr; 16 | multiply_expr = expr, "*", expr; 17 | divide_expr = expr, "/", expr; 18 | modulus_expr = expr, "%", expr; 19 | exp_expr = expr, "**", expr; 20 | post_inc_expr = expr, "++"; 21 | pre_inc_expr = "++", expr; 22 | post_dec_expr = expr, "--"; 23 | pre_dec_expr = "--", expr; 24 | 25 | greater_expr = expr, ">", expr; 26 | less_expr = expr, "<", expr; 27 | equal_expr = expr, "==", expr; 28 | not_equal_expr = expr, "!=", expr; 29 | greater_equal_expr = expr, ">=", expr; 30 | less_equal_expr = expr, "<=", expr; 31 | strlen_equal_expr = expr, "$=", expr; 32 | strlen_not_equal_expr = expr, "$!", expr; 33 | 34 | bin_and_expr = expr, "&", expr; 35 | bin_or_expr = expr, "|", expr; 36 | bin_xor_expr = expr, "^", expr; 37 | bin_lsh_expr = expr, "<<", expr; 38 | bin_rsh_expr = expr, ">>", expr; 39 | 40 | not_expr = "!", expr; 41 | and_expr = expr, "&&", expr; 42 | or_expr = expr, "||", expr; 43 | 44 | logical_expr = not_expr | and_expr | or_expr; 45 | binary_expr = bin_and_expr | bin_or_expr | bin_xor_expr | bin_lsh_expr | bin_rsh_expr; 46 | compare_expr = greater_expr | less_expr | greater_equal_expr | less_equal_expr | equal_expr | not_equal_expr 47 | | strlen_equal_expr | strlen_not_equal_expr; 48 | math_expr = add_expr | subtract_expr | multiply_expr | divide_expr | modulus_expr | exp_expr 49 | | post_inc_expr | pre_inc_expr | post_dec_expr | pre_dec_expr; 50 | access_expr = identifier, {".", identifier}; 51 | null_expr = "null"; 52 | parentheses_expr = "(", expr, ")"; 53 | array_expr = "[", {expr}, "]"; 54 | 55 | expr = float_expr | int_expr | string_expr | char_expr | bool_expr | math_expr | access_expr | compare_expr | logical_expr 56 | | binary_expr | null_expr | parentheses_expr | array_expr; 57 | 58 | compound_statement = "{", {statement}, "}"; 59 | if_statement = "if", expr, statement, {"elif", statement}, ["else", statement]; 60 | switch_statement = "switch", expr, "{", {"if", expr, ":", {statement}}, ["else", ":", {statement}] "}"; 61 | while_statement = "while", expr, statement; 62 | break_statement = "break"; 63 | continue_statement = "continue"; 64 | do_while_statement = "do", statement, "while", expr; 65 | for_statement = "for", identifier, "in", expr, statement 66 | func_statement = "func", identifier, "(", [identifier, {",", identifier}], ")", [":", "(", [expr, {",", expr}], ")"], compound_statement; 67 | return_statement = "return", expr; 68 | goto_statement = "goto", identifier; 69 | label_statement = identifier, ":"; 70 | assign_statement = access_expr, ("=" | "+=" | "-=" | "*=" | "/=" | "%=" | "**="), expr; 71 | member_assign_statement = identifier, "=", expr; 72 | enum_declaration = "enum", identifier, "{, {identifier, ["=", int_expr]}, "}"; 73 | class_declaration = "class", identifier, [":", access_expr], "{", {func_statement | member_assign_statement}, "}"; 74 | namespace_declaration = "namespace", identifier, "{", {namespace_declaration, class_declaration | enum_declaration | func_statement | member_assign_statement}, "}"; 75 | use_declaration = "use", access_expr; 76 | statement = compound_statement | if_statement | while_statement | for_statement | do_while_statement | func_statement 77 | | return_statement | break_statement | continue_statement | assign_statement | switch_statement 78 | | label_statement | goto_statement; 79 | program = {statement | class_declaration | enum_declaration | namespace_declaration | use_declaration}; 80 | -------------------------------------------------------------------------------- /src/tests/hash_map_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // printf() 29 | #include "debug.h" // debug_mode, debug_print() 30 | #include "hash_map.h" 31 | 32 | #define KEYWORD_COUNT ((uint8_t)23) 33 | #define HASH_MAP_BUCKET_COUNT ((uint16_t)24) 34 | 35 | const char* cct_keywords[KEYWORD_COUNT] = { 36 | "break", 37 | "continue", 38 | "case", 39 | "class", 40 | "do", 41 | "default", 42 | "else", 43 | "enum", 44 | "false", 45 | "for", 46 | "func", 47 | "goto", 48 | "if", 49 | "namespace", 50 | "null", 51 | "return", 52 | "switch", 53 | "super", 54 | "true", 55 | "use", 56 | "var", 57 | "while", 58 | "in" 59 | }; 60 | 61 | int main(void) 62 | { 63 | debug_mode = true; 64 | ConcoctHashMap* map = cct_new_hash_map(HASH_MAP_BUCKET_COUNT); 65 | 66 | // Print out the results of hashing 67 | printf("\nHASH MAP TEST\n\n"); 68 | printf("%-9s : %-10s : %s\n\n", "KEY", "HASH_CODE", "BUCKET_INDEX"); 69 | 70 | for(size_t i = 0; i < KEYWORD_COUNT; i++) 71 | { 72 | const char* key = cct_keywords[i]; 73 | uint32_t hash = cct_get_hash_code(key); 74 | uint32_t bucket = hash % HASH_MAP_BUCKET_COUNT; 75 | printf("%-9s : %10u : %d\n", key, hash, bucket); 76 | } 77 | printf("\n"); 78 | 79 | // Some hash map tests 80 | cct_hash_map_set(map, "var", NULL); 81 | cct_hash_map_set(map, "thisIsACrazyLongString", NULL); 82 | cct_hash_map_set(map, "deleteMe", NULL); 83 | cct_hash_map_delete_entry(map, "deleteMe"); 84 | cct_hash_map_set(map, "five", (void*)5); 85 | 86 | if(!cct_hash_map_has_key(map, "var")) 87 | printf("TEST 1 FAILED: Error while setting and testing a hash map key.\n"); 88 | else if(!cct_hash_map_has_key(map, "thisIsACrazyLongString")) 89 | printf("TEST 2 FAILED: Error while setting and testing a hash map key.\n"); 90 | else if(cct_hash_map_has_key(map, "thisKeyDoesNotExist")) 91 | printf("TEST 3 FAILED: Error while testing for key that does not exist.\n"); 92 | else if(cct_hash_map_has_key(map, "deleteMe")) 93 | printf("TEST 4 FAILED: Error while testing for key that was deleted.\n"); 94 | else if(cct_hash_map_get(map, "five") != (void*)5) 95 | printf("TEST 5 FAILED: Error while retrieving value\n"); 96 | else 97 | printf("ALL TESTS PASSED\n"); 98 | 99 | cct_delete_hash_map(map); 100 | } 101 | -------------------------------------------------------------------------------- /include/vm/opcodes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef OPCODES_H 29 | #define OPCODES_H 30 | 31 | #include // bool 32 | 33 | // Supported instruction set 34 | typedef enum opcode 35 | { 36 | OP_ADD, // add (+) 37 | OP_AND, // logical and (&&) 38 | OP_ASN, // assign (=) 39 | OP_BND, // bitwise and (&) 40 | OP_BNT, // bitwise not/ones' complement (~) 41 | OP_BOR, // bitwise or (|) 42 | OP_CAL, // call 43 | OP_CLR, // clear registers 44 | OP_CLS, // clear stack 45 | OP_CMP, // compare 46 | OP_DEC, // decrement (--) 47 | OP_DIV, // divide (/) 48 | OP_END, // marks end of VM instructions 49 | OP_ENT, // entry point 50 | OP_EQL, // equal to (==) 51 | OP_EXT, // exit 52 | OP_GT, // greater than (>) 53 | OP_GTE, // greater than or equal to (>=) 54 | OP_HLT, // halt 55 | OP_INC, // increment (++) 56 | OP_JMC, // jump conditional 57 | OP_JMP, // jump 58 | OP_JMZ, // jump zero 59 | OP_LNE, // loop not equal 60 | OP_LNZ, // loop not zero 61 | OP_LOD, // load (from memory to register) 62 | OP_LOE, // loop equal 63 | OP_LOP, // loop 64 | OP_LOZ, // loop zero 65 | OP_LT, // less than (<) 66 | OP_LTE, // less than or equal to (<=) 67 | OP_MOD, // modulo (%) 68 | OP_MOV, // move (from register to register) 69 | OP_MUL, // multiply (*) 70 | OP_NEG, // negative (unary -) 71 | OP_NEQ, // not equal to (!=) 72 | OP_NOP, // no op 73 | OP_NOT, // logical not/negation (!) 74 | OP_NUL, // null 75 | OP_OR, // logical or (||) 76 | OP_POP, // pop 77 | OP_POS, // positive 78 | OP_POW, // power/exponent (**) 79 | OP_PSH, // push 80 | OP_RET, // return 81 | OP_SHL, // bitshift left (<<) 82 | OP_SHR, // bitshift right (>>) 83 | OP_SLE, // string length equal to ($=) 84 | OP_SLN, // string length not equal to ($!) 85 | OP_STR, // store (to memory from register) 86 | OP_SUB, // subtract (-) 87 | OP_SYS, // system 88 | OP_TST, // test 89 | OP_XCG, // exchange/swap 90 | OP_XOR // bitwise exclusive or (^) 91 | } Opcode; 92 | 93 | // Returns opcode constant based on numeric ID 94 | const char* get_mnemonic(Opcode oc); 95 | 96 | // Returns true if opcode is a unary operation 97 | bool is_unary_operation(Opcode oc); 98 | 99 | // Returns true if opcode is a binary operation 100 | bool is_binary_operation(Opcode oc); 101 | 102 | #endif // OPCODES_H 103 | -------------------------------------------------------------------------------- /lib/linenoise/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "linenoise.h" 6 | 7 | void completion(const char *buf, linenoiseCompletions *lc) { 8 | if (buf[0] == 'h') { 9 | linenoiseAddCompletion(lc,"hello"); 10 | linenoiseAddCompletion(lc,"hello there"); 11 | } 12 | } 13 | 14 | char *hints(const char *buf, int *color, int *bold) { 15 | if (!strcasecmp(buf,"hello")) { 16 | *color = 35; 17 | *bold = 0; 18 | return " World"; 19 | } 20 | return NULL; 21 | } 22 | 23 | int main(int argc, char **argv) { 24 | char *line; 25 | char *prgname = argv[0]; 26 | int async = 0; 27 | 28 | /* Parse options, with --multiline we enable multi line editing. */ 29 | while(argc > 1) { 30 | argc--; 31 | argv++; 32 | if (!strcmp(*argv,"--multiline")) { 33 | linenoiseSetMultiLine(1); 34 | printf("Multi-line mode enabled.\n"); 35 | } else if (!strcmp(*argv,"--keycodes")) { 36 | linenoisePrintKeyCodes(); 37 | exit(0); 38 | } else if (!strcmp(*argv,"--async")) { 39 | async = 1; 40 | } else { 41 | fprintf(stderr, "Usage: %s [--multiline] [--keycodes] [--async]\n", prgname); 42 | exit(1); 43 | } 44 | } 45 | 46 | /* Set the completion callback. This will be called every time the 47 | * user uses the key. */ 48 | linenoiseSetCompletionCallback(completion); 49 | linenoiseSetHintsCallback(hints); 50 | 51 | /* Load history from file. The history file is just a plain text file 52 | * where entries are separated by newlines. */ 53 | linenoiseHistoryLoad("history.txt"); /* Load the history at startup */ 54 | 55 | /* Now this is the main loop of the typical linenoise-based application. 56 | * The call to linenoise() will block as long as the user types something 57 | * and presses enter. 58 | * 59 | * The typed string is returned as a malloc() allocated string by 60 | * linenoise, so the user needs to free() it. */ 61 | 62 | while(1) { 63 | if (!async) { 64 | line = linenoise("hello> "); 65 | if (line == NULL) break; 66 | } else { 67 | /* Asynchronous mode using the multiplexing API: wait for 68 | * data on stdin, and simulate async data coming from some source 69 | * using the select(2) timeout. */ 70 | struct linenoiseState ls; 71 | char buf[1024]; 72 | linenoiseEditStart(&ls,-1,-1,buf,sizeof(buf),"hello> "); 73 | while(1) { 74 | fd_set readfds; 75 | struct timeval tv; 76 | int retval; 77 | 78 | FD_ZERO(&readfds); 79 | FD_SET(ls.ifd, &readfds); 80 | tv.tv_sec = 1; // 1 sec timeout 81 | tv.tv_usec = 0; 82 | 83 | retval = select(ls.ifd+1, &readfds, NULL, NULL, &tv); 84 | if (retval == -1) { 85 | perror("select()"); 86 | exit(1); 87 | } else if (retval) { 88 | line = linenoiseEditFeed(&ls); 89 | /* A NULL return means: line editing is continuing. 90 | * Otherwise the user hit enter or stopped editing 91 | * (CTRL+C/D). */ 92 | if (line != linenoiseEditMore) break; 93 | } else { 94 | // Timeout occurred 95 | static int counter = 0; 96 | linenoiseHide(&ls); 97 | printf("Async output %d.\n", counter++); 98 | linenoiseShow(&ls); 99 | } 100 | } 101 | linenoiseEditStop(&ls); 102 | if (line == NULL) exit(0); /* Ctrl+D/C. */ 103 | } 104 | 105 | /* Do something with the string. */ 106 | if (line[0] != '\0' && line[0] != '/') { 107 | printf("echo: '%s'\n", line); 108 | linenoiseHistoryAdd(line); /* Add to the history. */ 109 | linenoiseHistorySave("history.txt"); /* Save the history on disk. */ 110 | } else if (!strncmp(line,"/historylen",11)) { 111 | /* The "/historylen" command will change the history len. */ 112 | int len = atoi(line+11); 113 | linenoiseHistorySetMaxLen(len); 114 | } else if (!strncmp(line, "/mask", 5)) { 115 | linenoiseMaskModeEnable(); 116 | } else if (!strncmp(line, "/unmask", 7)) { 117 | linenoiseMaskModeDisable(); 118 | } else if (line[0] == '/') { 119 | printf("Unreconized command: %s\n", line); 120 | } 121 | free(line); 122 | } 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /include/lexer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef LEXER_H 29 | #define LEXER_H 30 | 31 | #include // uint8_t 32 | #include "char_stream.h" 33 | #include "hash_map.h" 34 | 35 | #define MAX_TOKEN_TEXT_LENGTH ((size_t)1024) 36 | #define MAX_ERROR_STRING_LENGTH ((uint8_t)64) 37 | 38 | typedef enum concoct_token_type 39 | { 40 | CCT_TOKEN_IDENTIFIER, 41 | CCT_TOKEN_INT, 42 | CCT_TOKEN_FLOAT, 43 | CCT_TOKEN_STRING, 44 | CCT_TOKEN_CHAR, 45 | CCT_TOKEN_ASSIGN, 46 | CCT_TOKEN_ADD_ASSIGN, 47 | CCT_TOKEN_SUB_ASSIGN, 48 | CCT_TOKEN_MUL_ASSIGN, 49 | CCT_TOKEN_DIV_ASSIGN, 50 | CCT_TOKEN_MOD_ASSIGN, 51 | CCT_TOKEN_EXP_ASSIGN, 52 | CCT_TOKEN_ADD, 53 | CCT_TOKEN_SUB, 54 | CCT_TOKEN_MUL, 55 | CCT_TOKEN_DIV, 56 | CCT_TOKEN_MOD, 57 | CCT_TOKEN_EXP, 58 | CCT_TOKEN_INC, 59 | CCT_TOKEN_DEC, 60 | CCT_TOKEN_UNARY_MINUS, 61 | CCT_TOKEN_EQUAL, 62 | CCT_TOKEN_NOT_EQUAL, 63 | CCT_TOKEN_STRLEN_EQUAL, 64 | CCT_TOKEN_STRLEN_NOT_EQUAL, 65 | CCT_TOKEN_GREATER, 66 | CCT_TOKEN_LESS, 67 | CCT_TOKEN_GREATER_EQUAL, 68 | CCT_TOKEN_LESS_EQUAL, 69 | CCT_TOKEN_AND, 70 | CCT_TOKEN_OR, 71 | CCT_TOKEN_NOT, 72 | CCT_TOKEN_BIN_AND, 73 | CCT_TOKEN_BIN_OR, 74 | CCT_TOKEN_BIN_XOR, 75 | CCT_TOKEN_BIN_NOT, 76 | CCT_TOKEN_SHL, 77 | CCT_TOKEN_SHR, 78 | CCT_TOKEN_DOT, 79 | CCT_TOKEN_COMMA, 80 | CCT_TOKEN_LEFT_BRACE, 81 | CCT_TOKEN_RIGHT_BRACE, 82 | CCT_TOKEN_LEFT_BRACKET, 83 | CCT_TOKEN_RIGHT_BRACKET, 84 | CCT_TOKEN_LEFT_PAREN, 85 | CCT_TOKEN_RIGHT_PAREN, 86 | CCT_TOKEN_NEWLINE, 87 | CCT_TOKEN_EOF, 88 | CCT_TOKEN_BREAK, 89 | CCT_TOKEN_CONTINUE, 90 | CCT_TOKEN_CASE, 91 | CCT_TOKEN_CLASS, 92 | CCT_TOKEN_DO, 93 | CCT_TOKEN_DEFAULT, 94 | CCT_TOKEN_ELSE, 95 | CCT_TOKEN_ENUM, 96 | CCT_TOKEN_FALSE, 97 | CCT_TOKEN_FOR, 98 | CCT_TOKEN_FUNC, 99 | CCT_TOKEN_GOTO, 100 | CCT_TOKEN_IF, 101 | CCT_TOKEN_NAMESPACE, 102 | CCT_TOKEN_NULL, 103 | CCT_TOKEN_RETURN, 104 | CCT_TOKEN_SWITCH, 105 | CCT_TOKEN_SUPER, 106 | CCT_TOKEN_TRUE, 107 | CCT_TOKEN_USE, 108 | CCT_TOKEN_VAR, 109 | CCT_TOKEN_WHILE, 110 | CCT_TOKEN_IN, 111 | CCT_TOKEN_ERROR, 112 | } ConcoctTokenType; 113 | 114 | #define CCT_KEYWORD_COUNT ((uint8_t)23) 115 | extern const char* cct_keywords[CCT_KEYWORD_COUNT]; 116 | 117 | extern ConcoctTokenType cct_keyword_types[CCT_KEYWORD_COUNT]; 118 | 119 | typedef struct concoct_token 120 | { 121 | ConcoctTokenType type; 122 | size_t line_number; 123 | } ConcoctToken; 124 | 125 | typedef struct concoct_lexer 126 | { 127 | ConcoctCharStream* source; 128 | ConcoctHashMap* keyword_map; 129 | char next_char; 130 | size_t line_number; 131 | char* token_text; 132 | char* error; 133 | } ConcoctLexer; 134 | 135 | ConcoctToken cct_new_token(ConcoctTokenType type, size_t line_number); 136 | 137 | // Helper function for getting type names 138 | const char* cct_token_type_to_string(ConcoctTokenType type); 139 | 140 | ConcoctLexer* cct_new_lexer(ConcoctCharStream* source); 141 | void cct_delete_lexer(ConcoctLexer* lexer); 142 | 143 | int cct_lexer_is_eof(const ConcoctLexer* lexer); 144 | 145 | void cct_set_error(ConcoctLexer* lexer, const char* message); 146 | 147 | char cct_next_char(ConcoctLexer* lexer); 148 | ConcoctToken cct_next_token(ConcoctLexer* lexer); 149 | 150 | #endif // LEXER_H 151 | -------------------------------------------------------------------------------- /lib/linenoise/linenoise.h: -------------------------------------------------------------------------------- 1 | /* linenoise.h -- VERSION 1.0 2 | * 3 | * Guerrilla line editing library against the idea that a line editing lib 4 | * needs to be 20,000 lines of C code. 5 | * 6 | * See linenoise.c for more information. 7 | * 8 | * ------------------------------------------------------------------------ 9 | * 10 | * Copyright (c) 2010-2023, Salvatore Sanfilippo 11 | * Copyright (c) 2010-2013, Pieter Noordhuis 12 | * 13 | * All rights reserved. 14 | * 15 | * Redistribution and use in source and binary forms, with or without 16 | * modification, are permitted provided that the following conditions are 17 | * met: 18 | * 19 | * * Redistributions of source code must retain the above copyright 20 | * notice, this list of conditions and the following disclaimer. 21 | * 22 | * * Redistributions in binary form must reproduce the above copyright 23 | * notice, this list of conditions and the following disclaimer in the 24 | * documentation and/or other materials provided with the distribution. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 32 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | 39 | #ifndef __LINENOISE_H 40 | #define __LINENOISE_H 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | #include /* For size_t. */ 47 | 48 | extern char *linenoiseEditMore; 49 | 50 | /* The linenoiseState structure represents the state during line editing. 51 | * We pass this state to functions implementing specific editing 52 | * functionalities. */ 53 | struct linenoiseState { 54 | int in_completion; /* The user pressed TAB and we are now in completion 55 | * mode, so input is handled by completeLine(). */ 56 | size_t completion_idx; /* Index of next completion to propose. */ 57 | int ifd; /* Terminal stdin file descriptor. */ 58 | int ofd; /* Terminal stdout file descriptor. */ 59 | char *buf; /* Edited line buffer. */ 60 | size_t buflen; /* Edited line buffer size. */ 61 | const char *prompt; /* Prompt to display. */ 62 | size_t plen; /* Prompt length. */ 63 | size_t pos; /* Current cursor position. */ 64 | size_t oldpos; /* Previous refresh cursor position. */ 65 | size_t len; /* Current edited line length. */ 66 | size_t cols; /* Number of columns in terminal. */ 67 | size_t oldrows; /* Rows used by last refrehsed line (multiline mode) */ 68 | int history_index; /* The history index we are currently editing. */ 69 | }; 70 | 71 | typedef struct linenoiseCompletions { 72 | size_t len; 73 | char **cvec; 74 | } linenoiseCompletions; 75 | 76 | /* Non blocking API. */ 77 | int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt); 78 | char *linenoiseEditFeed(struct linenoiseState *l); 79 | void linenoiseEditStop(struct linenoiseState *l); 80 | void linenoiseHide(struct linenoiseState *l); 81 | void linenoiseShow(struct linenoiseState *l); 82 | 83 | /* Blocking API. */ 84 | char *linenoise(const char *prompt); 85 | void linenoiseFree(void *ptr); 86 | 87 | /* Completion API. */ 88 | typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); 89 | typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); 90 | typedef void(linenoiseFreeHintsCallback)(void *); 91 | void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); 92 | void linenoiseSetHintsCallback(linenoiseHintsCallback *); 93 | void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); 94 | void linenoiseAddCompletion(linenoiseCompletions *, const char *); 95 | 96 | /* History API. */ 97 | int linenoiseHistoryAdd(const char *line); 98 | int linenoiseHistorySetMaxLen(int len); 99 | int linenoiseHistorySave(const char *filename); 100 | int linenoiseHistoryLoad(const char *filename); 101 | 102 | /* Other utilities. */ 103 | void linenoiseClearScreen(void); 104 | void linenoiseSetMultiLine(int ml); 105 | void linenoisePrintKeyCodes(void); 106 | void linenoiseMaskModeEnable(void); 107 | void linenoiseMaskModeDisable(void); 108 | 109 | #ifdef __cplusplus 110 | } 111 | #endif 112 | 113 | #endif /* __LINENOISE_H */ 114 | -------------------------------------------------------------------------------- /.github/README.md: -------------------------------------------------------------------------------- 1 | [![AppVeyor build status](https://img.shields.io/appveyor/ci/ldilley/concoct?label=AppVeyor%20build%20status)](https://ci.appveyor.com/project/ldilley/concoct) 2 | [![CodeFactor grade](https://img.shields.io/codefactor/grade/github/Concoctist/concoct?label=CodeFactor%20quality)](https://www.codefactor.io/repository/github/Concoctist/concoct) 3 | [![Matrix](https://img.shields.io/matrix/concoct%3Amatrix.org?label=Matrix)](https://matrix.concoct.ist/) 4 | 5 | Concoct 🧪 6 | ======= 7 | Concoct is an imperative, dynamically-typed, interpreted, general-purpose programming language written in C. 8 | 9 | :construction: **Note:** This project is very much a work in progress. The interpreter is not yet functional. Have a look at the [roadmap](https://github.com/Concoctist/concoct/wiki/Roadmap). 10 | 11 | For more information about Concoct, please see the [wiki](https://github.com/Concoctist/concoct/wiki). 12 | 13 | ### Building :hammer_and_wrench: 14 | #### Requirements 15 | * A C compiler that supports the [C99](http://en.wikipedia.org/wiki/C99) standard ([Clang](http://clang.llvm.org/), [GCC](http://gcc.gnu.org/), [MinGW](https://osdn.net/projects/mingw), [MSVC](http://visualstudio.microsoft.com/) >=2015) 16 | * [CMake](http://cmake.org/) (>=3.1.0) 17 | * make/[gmake](http://www.gnu.org/software/make/) (if using Linux/Unix, [Cygwin](http://www.cygwin.com/), or [MinGW](https://osdn.net/projects/mingw)) 18 | 19 | #### Linux/Unix and Cygwin Steps 20 | 1. Install prerequisites: 21 | Debian/Ubuntu: 22 | ``` 23 | apt-get install cmake gcc git make 24 | ``` 25 | CentOS/Fedora/RHEL: 26 | ``` 27 | yum install cmake gcc git make 28 | ``` 29 | FreeBSD: 30 | ``` 31 | pkg install clang cmake git make 32 | ``` 33 | 34 | 2. Obtain the source code via `git` or download a zip archive: 35 | ``` 36 | git clone https://github.com/Concoctist/concoct.git 37 | ``` 38 | Or: 39 | ``` 40 | wget https://github.com/Concoctist/concoct/archive/master.zip && unzip master.zip 41 | ``` 42 | 43 | 3. In the top-level directory where `CMakeLists.txt` exists, create a build directory: 44 | ``` 45 | mkdir bld && cd bld 46 | ``` 47 | 48 | 4. Generate the `Makefile` (you can alternatively use `ccmake` here if you prefer): 49 | ``` 50 | cmake .. 51 | ``` 52 | 53 | 5. Build Concoct: 54 | ``` 55 | make 56 | ``` 57 | 58 | 6. There should now be a `concoct` executable under the `bin` directory if the build was successful: 59 | ``` 60 | $ ./concoct -v 61 | Concoct v0.1.0 rev 148 (d976be2) (64-bit Linux) (Debug) built at 00:46 on 01-02-2022 62 | ``` 63 | 64 | #### Windows Steps 65 | 1. Install the prerequisite software. This includes [Visual Studio](https://visualstudio.microsoft.com/) >=2015 with the "_Desktop development with C++_" workload as 66 | shown below. The aforementioned workload should include CMake. You can also optionally install [Git](https://git-scm.com/downloads) and add the executable to your path: 67 | 68 | ![image](https://user-images.githubusercontent.com/3323717/147863961-50856b16-9b4a-4dea-a4eb-fff7e6a18000.png) 69 | 70 | 2. Clone the Concoct source code. This can be done via Visual Studio or by using a git client of your choice: 71 | 72 | ![image](https://user-images.githubusercontent.com/3323717/147864080-fcb8c232-d975-4a99-b060-435232c9346f.png) 73 | 74 | Or: 75 | 76 | ![image](https://user-images.githubusercontent.com/3323717/147864037-62854ead-43fc-4b90-abbf-99d1162f2a8f.png) 77 | 78 | ![image](https://user-images.githubusercontent.com/3323717/147864136-0581bdd1-cfda-4f47-9e30-b59f5a058aa1.png) 79 | 80 | 3. Generate the CMake cache: 81 | 82 | ![image](https://user-images.githubusercontent.com/3323717/147864180-ee480075-de55-4b86-9f79-7486b2237c90.png) 83 | 84 | 4. Build Concoct: 85 | 86 | ![image](https://user-images.githubusercontent.com/3323717/147864201-8d8d2ac9-78fe-4efa-a6b7-3c4b7dc3c3d0.png) 87 | 88 | 5. If the build was successful, `concoct.exe` should now exist in your output directory: 89 | 90 | ![image](https://user-images.githubusercontent.com/3323717/147864334-5c8d44f3-136f-47f9-a1d5-434826e6572d.png) 91 | 92 | #### Docker/Podman Steps 93 | 1. Build image: 94 | ``` 95 | podman build -t concoct -f Containerfile 96 | ``` 97 | 98 | 2. Run container: 99 | ``` 100 | podman run -it localhost/concoct 101 | ``` 102 | 103 | ### Contributing and Support :octocat: 104 | Feel free to [submit an issue](https://github.com/Concoctist/concoct/issues/new) if you require assistance or would like to 105 | make a feature request. You are also welcome to join us on Matrix at https://matrix.concoct.ist/. Any contributions such as build testing, creating bug reports 106 | or feature requests, and submitting pull requests are appreciated. Our code style guidelines can be found in the "Coding Convention" section of 107 | [CONTRIBUTING.md](https://github.com/Concoctist/concoct/blob/master/.github/CONTRIBUTING.md). Please see the 108 | [fork and pull guide](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork) 109 | for direction if you are not certain how to submit a pull request. 110 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Concoct CMake Configuration 2 | cmake_minimum_required(VERSION 3.1...3.5) 3 | set(PROJECT concoct) 4 | set(HASH_MAP_TEST hash_map_test) 5 | set(INTERPRET_TEST interpret_test) 6 | set(OBJECT_TEST object_test) 7 | set(STACK_TEST stack_test) 8 | set(INTERPRET_TEST interpret_test) 9 | set(UNIT_TESTS unit_tests) 10 | project(${PROJECT}) 11 | if(WIN32) 12 | include_directories(include) 13 | else() 14 | include_directories(include lib/linenoise) 15 | endif() 16 | file(GLOB SOURCES src/*.c src/vm/*.c) 17 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin") 18 | set(CMAKE_C_STANDARD 99) 19 | set(CMAKE_C_STANDARD_REQUIRED ON) 20 | set(HASH_MAP_TEST_SOURCES src/debug.c src/hash_map.c src/seconds.c src/tests/hash_map_test.c) 21 | set(INTERPRET_TEST_SOURCES src/debug.c src/hash_map.c src/memory.c src/seconds.c src/stack.c 22 | src/types.c src/vm/instructions.c src/vm/opcodes.c src/vm/vm.c src/tests/interpret_test.c) 23 | set(OBJECT_TEST_SOURCES src/debug.c src/memory.c src/seconds.c src/types.c src/tests/object_test.c) 24 | set(STACK_TEST_SOURCES src/debug.c src/hash_map.c src/memory.c src/seconds.c src/stack.c 25 | src/types.c src/vm/instructions.c src/tests/stack_test.c) 26 | set(UNIT_TESTS_SOURCES src/debug.c src/memory.c src/seconds.c src/types.c src/tests/unit_tests.c) 27 | 28 | if(MSVC) 29 | set(CMAKE_C_FLAGS "/W4 /WX /D_CRT_SECURE_NO_WARNINGS") 30 | else() 31 | set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -O0") 32 | endif() 33 | 34 | if(NOT WIN32) 35 | add_library(linenoise STATIC lib/linenoise/linenoise.c lib/linenoise/linenoise.h) 36 | endif() 37 | 38 | add_executable(${PROJECT} ${SOURCES}) 39 | add_executable(${HASH_MAP_TEST} ${HASH_MAP_TEST_SOURCES}) 40 | add_executable(${INTERPRET_TEST} ${INTERPRET_TEST_SOURCES}) 41 | add_executable(${OBJECT_TEST} ${OBJECT_TEST_SOURCES}) 42 | add_executable(${STACK_TEST} ${STACK_TEST_SOURCES}) 43 | add_executable(${UNIT_TESTS} ${UNIT_TESTS_SOURCES}) 44 | 45 | # Set default build type 46 | if(NOT CMAKE_BUILD_TYPE) 47 | message("Setting default build type to: Debug") 48 | set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) 49 | endif() 50 | 51 | # Populate git revision, git hash, build time, build date, and build type variables for improved version information 52 | find_package(Git) 53 | execute_process(COMMAND "${GIT_EXECUTABLE}" rev-list --count HEAD WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" 54 | OUTPUT_VARIABLE GIT_REV ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) 55 | execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse --short HEAD WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" 56 | OUTPUT_VARIABLE GIT_HASH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) 57 | string(TIMESTAMP BUILD_TIME "%H:%M") 58 | string(TIMESTAMP BUILD_DATE "%m-%d-%Y") 59 | set(BUILD_TYPE "${CMAKE_BUILD_TYPE}") 60 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/include/version.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/include/version.h" @ONLY) 61 | 62 | # Check for math library availability 63 | include(CheckFunctionExists) 64 | if(NOT ROUND_FUNCTION_EXISTS AND NOT NEED_LINKING_AGAINST_LIBM) 65 | CHECK_FUNCTION_EXISTS(round ROUND_FUNCTION_EXISTS) 66 | if(NOT ROUND_FUNCTION_EXISTS) 67 | unset(ROUND_FUNCTION_EXISTS CACHE) 68 | list(APPEND CMAKE_REQUIRED_LIBRARIES m) 69 | CHECK_FUNCTION_EXISTS(round ROUND_FUNCTION_EXISTS) 70 | if(ROUND_FUNCTION_EXISTS) 71 | set(NEED_LINKING_AGAINST_LIBM True CACHE BOOL "" FORCE) 72 | else() 73 | message(FATAL_ERROR "round() function is not available!") 74 | endif() 75 | endif() 76 | endif() 77 | if(NEED_LINKING_AGAINST_LIBM) 78 | target_link_libraries(${PROJECT} m linenoise) 79 | target_link_libraries(${HASH_MAP_TEST} m) 80 | target_link_libraries(${INTERPRET_TEST} m) 81 | target_link_libraries(${OBJECT_TEST} m) 82 | target_link_libraries(${STACK_TEST} m) 83 | target_link_libraries(${UNIT_TESTS} m) 84 | else() 85 | if(WIN32) 86 | target_link_libraries(${PROJECT}) 87 | else() 88 | target_link_libraries(${PROJECT} linenoise) 89 | endif() 90 | target_link_libraries(${HASH_MAP_TEST}) 91 | target_link_libraries(${INTERPRET_TEST}) 92 | target_link_libraries(${OBJECT_TEST}) 93 | target_link_libraries(${STACK_TEST}) 94 | target_link_libraries(${UNIT_TESTS}) 95 | endif() 96 | 97 | # Strip binary for release builds 98 | if(CMAKE_BUILD_TYPE STREQUAL Release) 99 | add_custom_command(TARGET ${PROJECT} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT}) 100 | add_custom_command(TARGET ${HASH_MAP_TEST} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${HASH_MAP_TEST}) 101 | add_custom_command(TARGET ${INTERPRET_TEST} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${INTERPRET_TEST}) 102 | add_custom_command(TARGET ${OBJECT_TEST} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${OBJECT_TEST}) 103 | add_custom_command(TARGET ${STACK_TEST} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${STACK_TEST}) 104 | add_custom_command(TARGET ${UNIT_TESTS} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${UNIT_TESTS}) 105 | endif() 106 | 107 | message("Thank you for using Concoct!") 108 | -------------------------------------------------------------------------------- /include/memory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MEMORY_H 29 | #define MEMORY_H 30 | 31 | #include "types.h" 32 | #include "stack.h" 33 | 34 | // Initial free store capacity 35 | static const size_t INITIAL_STORE_CAPACITY = 128; 36 | // Percentage to increase object store by during expansion 37 | static const uint8_t STORE_GROWTH_FACTOR = 50; 38 | // Percentage of free slots remaining in object store before triggering expansion 39 | static const uint8_t STORE_GROWTH_THRESHOLD = 10; 40 | // Percentage to decrease object store by during compaction 41 | static const uint8_t STORE_SHRINK_FACTOR = 25; 42 | // Percentage of free slots remaining in object store before triggering compaction 43 | static const uint8_t STORE_SHRINK_THRESHOLD = 75; 44 | 45 | // Byte size limits for conversions 46 | static const size_t KILOBYTE_BOUNDARY = 1024; 47 | static const size_t MEGABYTE_BOUNDARY = 1048576; 48 | static const size_t GIGABYTE_BOUNDARY = 1073741824; 49 | 50 | // Object store 51 | typedef struct objstore 52 | { 53 | size_t capacity; 54 | Object** objects; 55 | } ObjectStore; 56 | extern ObjectStore object_store; 57 | 58 | // Initializes object store 59 | void init_store(void); 60 | 61 | // Reallocates memory for object store 62 | void realloc_store(size_t new_size); 63 | 64 | // Frees object store 65 | void free_store(void); 66 | 67 | // Returns size of object store 68 | static inline size_t get_store_capacity(void) { return object_store.capacity; } 69 | 70 | // Returns free slots of object store 71 | size_t get_store_free_slots(void); 72 | 73 | // Returns used slots of object store 74 | size_t get_store_used_slots(void); 75 | 76 | // Returns size of object in bytes 77 | size_t get_object_size(const Object* object); 78 | 79 | // Returns total size of objects in object store in bytes 80 | size_t get_store_objects_size(void); 81 | 82 | // Returns total size of object store in bytes 83 | static inline size_t get_store_total_size(void) { return get_store_objects_size() + sizeof(ObjectStore) + sizeof(Object *) * get_store_capacity(); } 84 | 85 | // Prints total size of objects in object store 86 | void print_store_objects_size(void); 87 | 88 | // Prints total size of object store 89 | void print_store_total_size(void); 90 | 91 | // Converts bytes to kilobytes 92 | size_t convert_kilobytes(size_t bytes); 93 | 94 | // Converts bytes to megabytes 95 | size_t convert_megabytes(size_t bytes); 96 | 97 | // Converts bytes to gigabytes 98 | size_t convert_gigabytes(size_t bytes); 99 | 100 | // Adds object to store 101 | void add_store_object(Object* object); 102 | 103 | // Populates String struct 104 | void new_string(String* strobj, char* str); 105 | 106 | // Reallocates memory for string 107 | void realloc_string(String* strobj, const char* new_string); 108 | 109 | // Frees string 110 | void free_string(String* strobj); 111 | 112 | // Populates Object struct 113 | Object* new_object(char* value); 114 | 115 | // Creates a new global object 116 | Object* new_global(char* value); 117 | 118 | // Creates a new constant object 119 | Object* new_constant(char* value, char* name); 120 | 121 | // Populates Object struct based on datatype 122 | Object* new_object_by_type(void* data, DataType datatype); 123 | 124 | // Frees object 125 | void free_object(Object** object); 126 | 127 | // Clones object 128 | Object* clone_object(Object* object); 129 | 130 | // Converts numeric data to string 131 | void stringify(char** str, void* data, DataType datatype); 132 | 133 | // Flags objects to prevent them from being garbage collected and returns number of objects flagged 134 | size_t flag_objects(Stack* stack); 135 | 136 | // Collects garbage and returns number of objects collected 137 | size_t collect_garbage(void); 138 | 139 | #endif // MEMORY_H 140 | -------------------------------------------------------------------------------- /src/types.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // errno, EINVAL, ERANGE 29 | #include // PRId32, PRId64 30 | #include // INT32_MIN, INT32_MAX 31 | #include // printf(), puts() 32 | #include // strtod(), strtol(), strtoll() 33 | #include // strcasecmp() 34 | #include "memory.h" 35 | #include "types.h" 36 | 37 | // Returns string representation of data type 38 | const char* get_type(DataType datatype) 39 | { 40 | switch(datatype) 41 | { 42 | case CCT_TYPE_NIL: return "null"; 43 | case CCT_TYPE_BOOL: return "boolean"; 44 | case CCT_TYPE_BYTE: return "byte"; 45 | case CCT_TYPE_NUMBER: return "number"; 46 | case CCT_TYPE_BIGNUM: return "big number"; 47 | case CCT_TYPE_DECIMAL: return "decimal"; 48 | case CCT_TYPE_STRING: return "string"; 49 | default: return "unknown"; 50 | } 51 | } 52 | 53 | // Returns string representation of data type from object 54 | const char* get_data_type(const Object* object) 55 | { 56 | switch(object->datatype) 57 | { 58 | case CCT_TYPE_NIL: return "null"; 59 | case CCT_TYPE_BOOL: return "boolean"; 60 | case CCT_TYPE_BYTE: return "byte"; 61 | case CCT_TYPE_NUMBER: return "number"; 62 | case CCT_TYPE_BIGNUM: return "big number"; 63 | case CCT_TYPE_DECIMAL: return "decimal"; 64 | case CCT_TYPE_STRING: return "string"; 65 | default: return "unknown"; 66 | } 67 | } 68 | 69 | // Returns value of object 70 | void* get_object_value(Object* object) 71 | { 72 | switch(object->datatype) 73 | { 74 | case CCT_TYPE_NIL: 75 | return NULL; 76 | case CCT_TYPE_BOOL: 77 | return &object->value.boolval; 78 | case CCT_TYPE_BYTE: 79 | return &object->value.byteval; 80 | case CCT_TYPE_NUMBER: 81 | return &object->value.numval; 82 | case CCT_TYPE_BIGNUM: 83 | return &object->value.bignumval; 84 | case CCT_TYPE_DECIMAL: 85 | return &object->value.decimalval; 86 | case CCT_TYPE_STRING: 87 | return object->value.strobj.strval; 88 | } 89 | return NULL; 90 | } 91 | 92 | // Displays value of object 93 | void print_object_value(Object* object) 94 | { 95 | switch(object->datatype) 96 | { 97 | case CCT_TYPE_NIL: 98 | puts("null"); 99 | break; 100 | case CCT_TYPE_BOOL: 101 | if(object->value.boolval == false) 102 | puts("false"); 103 | else 104 | puts("true"); 105 | break; 106 | case CCT_TYPE_BYTE: 107 | printf("%u\n", object->value.byteval); 108 | break; 109 | case CCT_TYPE_NUMBER: 110 | printf("%" PRId32 "\n", object->value.numval); 111 | break; 112 | case CCT_TYPE_BIGNUM: 113 | printf("%" PRId64 "\n", object->value.bignumval); 114 | break; 115 | case CCT_TYPE_DECIMAL: 116 | printf("%f\n", object->value.decimalval); 117 | break; 118 | case CCT_TYPE_STRING: 119 | printf("%s\n", object->value.strobj.strval); 120 | break; 121 | } 122 | } 123 | 124 | // Converts string to applicable type 125 | void convert_type(Object* object, char* value) 126 | { 127 | // Handle null 128 | #ifdef _WIN32 129 | if(_stricmp(value, "null") == 0) 130 | #else 131 | if(strcasecmp(value, "null") == 0) 132 | #endif 133 | { 134 | object->datatype = CCT_TYPE_NIL; 135 | return; 136 | } 137 | 138 | // Handle booleans 139 | #ifdef _WIN32 140 | if(_stricmp(value, "true") == 0) 141 | #else 142 | if(strcasecmp(value, "true") == 0) 143 | #endif 144 | { 145 | object->datatype = CCT_TYPE_BOOL; 146 | object->value.boolval = true; 147 | return; 148 | } 149 | #ifdef _WIN32 150 | if(_stricmp(value, "false") == 0) 151 | #else 152 | if(strcasecmp(value, "false") == 0) 153 | #endif 154 | { 155 | object->datatype = CCT_TYPE_BOOL; 156 | object->value.boolval = false; 157 | return; 158 | } 159 | 160 | // Handle integers 161 | char *end = NULL; 162 | errno = 0; 163 | BigNum bignum = strtol(value, &end, 10); 164 | if(*end == '\0' && errno != ERANGE && errno != EINVAL) 165 | { 166 | if(bignum > INT32_MIN && bignum < INT32_MAX) 167 | { 168 | object->datatype = CCT_TYPE_NUMBER; 169 | object->value.numval = (Number)bignum; 170 | return; 171 | } 172 | } 173 | 174 | // Handle big integers 175 | end = NULL; 176 | errno = 0; 177 | bignum = strtoll(value, &end, 10); 178 | if(*end == '\0' && errno != ERANGE && errno != EINVAL) 179 | { 180 | object->datatype = CCT_TYPE_BIGNUM; 181 | object->value.bignumval = bignum; 182 | return; 183 | } 184 | 185 | // Handle floats 186 | end = NULL; 187 | errno = 0; 188 | Decimal dec = strtod(value, &end); 189 | if(*end == '\0' && errno != ERANGE && errno != EINVAL) 190 | { 191 | object->datatype = CCT_TYPE_DECIMAL; 192 | object->value.decimalval = dec; 193 | return; 194 | } 195 | 196 | // Default to string otherwise 197 | object->datatype = CCT_TYPE_STRING; 198 | new_string(&object->value.strobj, value); 199 | return; 200 | } 201 | -------------------------------------------------------------------------------- /src/tests/object_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include // rand(), srand() 31 | #include // time() 32 | #include "concoct.h" 33 | #include "debug.h" 34 | #include "memory.h" 35 | #include "types.h" 36 | 37 | // Proof of concept to demonstrate garbage collection 38 | size_t mark_objects(void) 39 | { 40 | size_t mark_count = 0; 41 | if(debug_mode) 42 | debug_print("GC: Marking objects..."); 43 | for(size_t slot = 0; slot < get_store_capacity(); slot++) 44 | { 45 | if(object_store.objects[slot] != NULL && rand() % 2) // each object has a 50% chance of being marked 46 | { 47 | object_store.objects[slot]->is_flagged = true; 48 | mark_count++; 49 | } 50 | } 51 | if(debug_mode) 52 | debug_print("GC: %zu objects marked.", mark_count); 53 | return mark_count; 54 | } 55 | 56 | int main(void) 57 | { 58 | debug_mode = true; 59 | uint8_t randnum = 0; 60 | srand((unsigned int)time(0)); 61 | init_store(); 62 | Object* objects[1024]; 63 | UNUSED(objects); 64 | for(size_t i = 0; i < 1024; i++) 65 | { 66 | printf("Object store free/capacity: %zu/%zu\n", get_store_free_slots(), get_store_capacity()); 67 | printf("Used slots: %zu/%zu\n\n", get_store_used_slots(), get_store_capacity()); 68 | print_store_total_size(); 69 | randnum = rand() % 100 + 1; 70 | if(randnum <= 10) 71 | objects[i] = new_constant("100", "TEMP"); 72 | else if(randnum > 10 && randnum <= 20) 73 | objects[i] = new_global("200"); 74 | else 75 | objects[i] = new_object("7"); 76 | } 77 | 78 | for(size_t i = 0; i < 1024; i++) 79 | { 80 | printf("Object of data type %s at slot #%zu is %zu bytes.\n", get_data_type(object_store.objects[i]), i, get_object_size(object_store.objects[i])); 81 | } 82 | mark_objects(); 83 | collect_garbage(); // free only non-marked objects 84 | printf("Object store free/capacity: %zu/%zu\n", get_store_free_slots(), get_store_capacity()); 85 | printf("Used slots: %zu/%zu\n\n", get_store_used_slots(), get_store_capacity()); 86 | 87 | Object* object = new_object("null"); 88 | printf("Data type: %s\n", get_data_type(object)); 89 | print_object_value(object); 90 | printf("%s\n\n", (char *)get_object_value(object)); 91 | 92 | object = new_object("true"); 93 | printf("Data type: %s\n", get_data_type(object)); 94 | print_object_value(object); 95 | printf("%d\n\n", *(Bool *)get_object_value(object)); 96 | 97 | object = new_object("100"); 98 | printf("Data type: %s\n", get_data_type(object)); 99 | print_object_value(object); 100 | printf("%" PRId32 "\n\n", *(Number *)get_object_value(object)); 101 | 102 | object = new_object("5721452096347253"); 103 | printf("Data type: %s\n", get_data_type(object)); 104 | print_object_value(object); 105 | printf("%" PRId64 "\n\n", *(BigNum *)get_object_value(object)); 106 | 107 | object = new_object("77.715"); 108 | printf("Data type: %s\n", get_data_type(object)); 109 | print_object_value(object); 110 | printf("%f\n\n", *(Decimal *)get_object_value(object)); 111 | 112 | object = new_object("Greetings, Concocter!"); 113 | printf("Data type: %s\nString value: %s\nString length: %zu\n", get_data_type(object), object->value.strobj.strval, object->value.strobj.length); 114 | print_object_value(object); 115 | printf("Object size: %zu bytes\n", get_object_size(object)); 116 | printf("%s\n", (char *)get_object_value(object)); 117 | 118 | puts("\nAfter realloc_string():"); 119 | realloc_string(&object->value.strobj, "Farewell, Concocter!"); 120 | printf("Data type: %s\nString value: %s\nString length: %zu\n", get_data_type(object), object->value.strobj.strval, object->value.strobj.length); 121 | print_object_value(object); 122 | printf("%s\n", (char *)get_object_value(object)); 123 | Object *object2 = clone_object(object); 124 | 125 | puts("\nCloned object:"); 126 | printf("Data type: %s\nString value: %s\nString length: %zu\n", get_data_type(object2), object2->value.strobj.strval, object2->value.strobj.length); 127 | free_store(); 128 | 129 | puts("\nStringify tests..."); 130 | char* str = NULL; 131 | Number numval = 42; 132 | void* vptr = &numval; 133 | stringify(&str, vptr, CCT_TYPE_NUMBER); 134 | printf("%s\n", str); 135 | free(str); 136 | Decimal decimalval = 57.05; 137 | vptr = &decimalval; 138 | stringify(&str, vptr, CCT_TYPE_DECIMAL); 139 | printf("%s\n", str); 140 | free(str); 141 | 142 | char* testval = "Foo bar baz"; 143 | vptr = &testval; 144 | stringify(&str, vptr, CCT_TYPE_STRING); 145 | printf("%s\n", str); 146 | free(str); 147 | 148 | Bool boolval = false; 149 | vptr = &boolval; 150 | stringify(&str, vptr, CCT_TYPE_BOOL); 151 | printf("%s\n", str); 152 | free(str); 153 | 154 | boolval = true; 155 | vptr = &boolval; 156 | stringify(&str, vptr, CCT_TYPE_BOOL); 157 | printf("%s\n", str); 158 | free(str); 159 | 160 | char* nullval = NULL; 161 | vptr = &nullval; 162 | stringify(&str, vptr, CCT_TYPE_NIL); 163 | printf("%s\n", str); 164 | free(str); 165 | 166 | return 0; 167 | } 168 | -------------------------------------------------------------------------------- /src/vm/opcodes.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "vm/opcodes.h" 29 | 30 | // Returns opcode constant based on numeric ID 31 | const char* get_mnemonic(Opcode oc) 32 | { 33 | switch(oc) 34 | { 35 | case OP_ADD: return "OP_ADD"; // add (+) 36 | case OP_AND: return "OP_AND"; // logical and (&&) 37 | case OP_ASN: return "OP_ASN"; // assign (=) 38 | case OP_BND: return "OP_BND"; // bitwise and (&) 39 | case OP_BNT: return "OP_BNT"; // bitwise not/ones' complement (~) 40 | case OP_BOR: return "OP_BOR"; // bitwise or (|) 41 | case OP_CAL: return "OP_CAL"; // call 42 | case OP_CLR: return "OP_CLR"; // clear registers 43 | case OP_CLS: return "OP_CLS"; // clear stack 44 | case OP_CMP: return "OP_CMP"; // compare 45 | case OP_DEC: return "OP_DEC"; // decrement (--) 46 | case OP_DIV: return "OP_DIV"; // divide (/) 47 | case OP_END: return "OP_END"; // marks end of VM instructions 48 | case OP_ENT: return "OP_ENT"; // entry point 49 | case OP_EQL: return "OP_EQL"; // equal to (==) 50 | case OP_EXT: return "OP_EXT"; // exit 51 | case OP_GT: return "OP_GT"; // greater than (>) 52 | case OP_GTE: return "OP_GTE"; // greater than or equal to (>=) 53 | case OP_HLT: return "OP_HLT"; // halt 54 | case OP_INC: return "OP_INC"; // increment (++) 55 | case OP_JMC: return "OP_JMC"; // jump conditional 56 | case OP_JMP: return "OP_JMP"; // jump 57 | case OP_JMZ: return "OP_JMZ"; // jump zero 58 | case OP_LNE: return "OP_LNE"; // loop not equal 59 | case OP_LNZ: return "OP_LNZ"; // loop not zero 60 | case OP_LOD: return "OP_LOD"; // load (from memory to register) 61 | case OP_LOE: return "OP_LOE"; // loop equal 62 | case OP_LOP: return "OP_LOP"; // loop 63 | case OP_LOZ: return "OP_LOZ"; // loop zero 64 | case OP_LT: return "OP_LT"; // less than (<) 65 | case OP_LTE: return "OP_LTE"; // less than or equal to (<=) 66 | case OP_MOD: return "OP_MOD"; // modulo (%) 67 | case OP_MOV: return "OP_MOV"; // move (from register to register) 68 | case OP_MUL: return "OP_MUL"; // multiply (*) 69 | case OP_NEG: return "OP_NEG"; // negative (unary -) 70 | case OP_NEQ: return "OP_NEQ"; // not equal to (!=) 71 | case OP_NOP: return "OP_NOP"; // no op 72 | case OP_NOT: return "OP_NOT"; // logical not/negation (!) 73 | case OP_NUL: return "OP_NUL"; // null 74 | case OP_OR: return "OP_OR"; // logical or (||) 75 | case OP_POP: return "OP_POP"; // pop 76 | case OP_POS: return "OP_POS"; // positive 77 | case OP_POW: return "OP_POW"; // power/exponent (**) 78 | case OP_PSH: return "OP_PSH"; // push 79 | case OP_RET: return "OP_RET"; // return 80 | case OP_SHL: return "OP_SHL"; // bitshift left (<<) 81 | case OP_SHR: return "OP_SHR"; // bitshift right (>>) 82 | case OP_SLE: return "OP_SLE"; // string length equal to ($=) 83 | case OP_SLN: return "OP_SLN"; // string length not equal to ($!) 84 | case OP_STR: return "OP_STR"; // store (to memory from register) 85 | case OP_SUB: return "OP_SUB"; // subtract (-) 86 | case OP_SYS: return "OP_SYS"; // system 87 | case OP_TST: return "OP_TST"; // test 88 | case OP_XCG: return "OP_XCG"; // exchange/swap 89 | case OP_XOR: return "OP_XOR"; // bitwise exclusive or (^) 90 | default: return "UNDEFINED"; // unsupported opcode 91 | } 92 | } 93 | 94 | // Returns true if opcode is a unary operation 95 | bool is_unary_operation(Opcode oc) 96 | { 97 | switch(oc) 98 | { 99 | case OP_BNT: // bitwise not/ones' complement (~) 100 | case OP_DEC: // decrement (--) 101 | case OP_INC: // increment (++) 102 | case OP_NEG: // negative (unary -) 103 | case OP_NOT: // logical not/negation (!) 104 | case OP_POS: // positive 105 | return true; 106 | default: 107 | return false; 108 | } 109 | } 110 | 111 | // Returns true if opcode is a binary operation 112 | bool is_binary_operation(Opcode oc) 113 | { 114 | switch(oc) 115 | { 116 | case OP_ADD: // add (+) 117 | case OP_AND: // logical and (&&) 118 | case OP_ASN: // assign (=) 119 | case OP_BND: // bitwise and (&) 120 | case OP_BOR: // bitwise or (|) 121 | case OP_DIV: // divide (/) 122 | case OP_EQL: // equal to (==) 123 | case OP_GT: // greater than (>) 124 | case OP_GTE: // greater than or equal to (>=) 125 | case OP_LT: // less than (<) 126 | case OP_LTE: // less than or equal to (<=) 127 | case OP_MOD: // modulo (%) 128 | case OP_MUL: // multiply (*) 129 | case OP_NEQ: // not equal to (!=) 130 | case OP_OR: // logical or (||) 131 | case OP_POW: // power/exponent (**) 132 | case OP_SHL: // bitshift left (<<) 133 | case OP_SHR: // bitshift right (>>) 134 | case OP_SLE: // string length equal to ($=) 135 | case OP_SLN: // string length not equal to ($!) 136 | case OP_SUB: // subtract (-) 137 | case OP_XOR: // bitwise exclusive or (^) 138 | return true; 139 | default: 140 | return false; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/hash_map.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // errno 29 | #include // PRIu32 30 | #include // fprintf(), stderr 31 | #include // free(), malloc() 32 | #include // strcmp(), strerror(), strlen() 33 | #include "debug.h" // debug_mode, debug_print() 34 | #include "hash_map.h" 35 | 36 | ConcoctHashMap* cct_new_hash_map(uint32_t bucket_count) 37 | { 38 | ConcoctHashMap* map = malloc(sizeof(ConcoctHashMap)); 39 | if(map == NULL) 40 | { 41 | fprintf(stderr, "Failed to allocate memory for a hash map: %s\n", strerror(errno)); 42 | return NULL; 43 | } 44 | 45 | map->bucket_count = bucket_count; 46 | map->buckets = malloc(bucket_count * sizeof(ConcoctHashMapNode*)); 47 | if(map->buckets == NULL) 48 | { 49 | fprintf(stderr, "Failed to allocate memory for hash map buckets: %s\n", strerror(errno)); 50 | return NULL; 51 | } 52 | 53 | for(size_t i = 0; i < bucket_count; i++) 54 | { 55 | ConcoctHashMapNode** bucket = map->buckets + i; 56 | *bucket = NULL; 57 | } 58 | 59 | if(debug_mode) 60 | debug_print("Hash map created with %zu buckets.", bucket_count); 61 | 62 | return map; 63 | } 64 | 65 | void cct_delete_hash_map(ConcoctHashMap* map) 66 | { 67 | for(size_t i = 0; i < map->bucket_count; i++) 68 | { 69 | if(map->buckets[i] != NULL) 70 | { 71 | cct_delete_hash_map_node(map->buckets[i]); 72 | } 73 | } 74 | free(map->buckets); 75 | free(map); 76 | 77 | if(debug_mode) 78 | debug_print("Freed hash map."); 79 | 80 | return; 81 | } 82 | 83 | ConcoctHashMapNode* cct_new_hash_map_node(const char* key, void* value, uint32_t hash) 84 | { 85 | ConcoctHashMapNode* node = malloc(sizeof(ConcoctHashMapNode)); 86 | if(node == NULL) 87 | { 88 | fprintf(stderr, "Failed to allocate memory for hash map key: %s\n", strerror(errno)); 89 | return NULL; 90 | } 91 | 92 | node->hash = hash; 93 | node->key = key; 94 | node->value = value; 95 | node->next = NULL; 96 | 97 | if(debug_mode) 98 | debug_print("Hash map node created with key: %s (hash: %" PRIu32 ")", key, hash); 99 | 100 | return node; 101 | } 102 | 103 | void cct_delete_hash_map_node(ConcoctHashMapNode* node) 104 | { 105 | if(node->next != NULL) 106 | { 107 | cct_delete_hash_map_node(node->next); 108 | } 109 | free(node); 110 | 111 | return; 112 | } 113 | 114 | bool cct_hash_map_has_key(const ConcoctHashMap* map, const char* key) 115 | { 116 | // Finds the bucket that this key could be in and checks each entry in it 117 | uint32_t hash = cct_get_hash_code(key); 118 | uint32_t bucket_index = hash % map->bucket_count; 119 | ConcoctHashMapNode* node = map->buckets[bucket_index]; 120 | 121 | while(node != NULL) 122 | { 123 | // Checks hash first for efficiency. Checks the string itself in case of hash collisions 124 | if(node->hash == hash && strcmp(node->key, key) == 0) 125 | return true; 126 | node = node->next; 127 | } 128 | 129 | return false; 130 | } 131 | 132 | void cct_hash_map_set(ConcoctHashMap* map, const char* key, void* value) 133 | { 134 | unsigned int hash = cct_get_hash_code(key); 135 | int bucket_index = hash % map->bucket_count; 136 | ConcoctHashMapNode* node = map->buckets[bucket_index]; 137 | 138 | if(node == NULL) 139 | { 140 | // Starts the first node in the bucket 141 | map->buckets[bucket_index] = cct_new_hash_map_node(key, value, hash); 142 | } 143 | else 144 | { 145 | // Traverses the linked list to find where to put new node 146 | while(node->next != NULL) 147 | { 148 | node = node->next; 149 | } 150 | node->next = cct_new_hash_map_node(key, value, hash); 151 | } 152 | 153 | return; 154 | } 155 | 156 | void* cct_hash_map_get(const ConcoctHashMap* map, const char* key) 157 | { 158 | // Finds the bucket that this key could be in and checks each entry in it 159 | uint32_t hash = cct_get_hash_code(key); 160 | uint32_t bucket_index = hash % map->bucket_count; 161 | ConcoctHashMapNode* node = map->buckets[bucket_index]; 162 | 163 | while(node != NULL) 164 | { 165 | // Checks hash first for efficiency. Checks the string itself in case of hash collisions 166 | if(node->hash == hash && strcmp(node->key, key) == 0) 167 | { 168 | return node->value; 169 | } 170 | node = node->next; 171 | } 172 | 173 | return NULL; 174 | } 175 | 176 | void cct_hash_map_delete_entry(ConcoctHashMap* map, const char* key) 177 | { 178 | uint32_t hash = cct_get_hash_code(key); 179 | uint32_t bucket_index = hash % map->bucket_count; 180 | ConcoctHashMapNode* node = map->buckets[bucket_index]; 181 | ConcoctHashMapNode* previous_node = NULL; 182 | 183 | while(node) 184 | { 185 | if(node->hash == hash && strcmp(node->key, key) == 0) 186 | { 187 | if(previous_node) 188 | { 189 | previous_node->next = node->next; 190 | // Must set to NULL or the delete function will free node->next as well 191 | node->next = NULL; 192 | } 193 | else 194 | { 195 | // This branch means this is the first node of the bucket so we remove the reference to it 196 | map->buckets[bucket_index] = NULL; 197 | } 198 | cct_delete_hash_map_node(node); 199 | return; 200 | } 201 | previous_node = node; 202 | node = node->next; 203 | } 204 | 205 | return; 206 | } 207 | 208 | ConcoctHashMapNode* get_first_node_in_bucket(const ConcoctHashMap* map, uint32_t bucket) 209 | { 210 | return map->buckets[bucket]; 211 | } 212 | 213 | uint32_t cct_get_hash_code(const char* str) 214 | { 215 | uint32_t hash = CCT_HASH_OFFSET; 216 | char* first_byte = (char*)&hash; 217 | size_t str_length = strlen(str); 218 | 219 | for(size_t i = 0; i < str_length; i++) 220 | { 221 | *first_byte ^= str[i]; 222 | hash *= CCT_HASH_PRIME; 223 | } 224 | 225 | return hash; 226 | } 227 | -------------------------------------------------------------------------------- /src/compiler.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // fprintf() 29 | #include "compiler.h" 30 | #include "debug.h" // debug_mode, debug_print() 31 | #include "memory.h" // new_object(), new_object_by_type() 32 | #include "queue.h" 33 | #include "vm/vm.h" // interpret(), reverse_instructions() 34 | 35 | // Swap last 2 stack objects to fix order if first instruction is a binary operation 36 | void swap_last_operands(Opcode oc) 37 | { 38 | Object* object1 = NULL; 39 | Object* object2 = NULL; 40 | if(!is_binary_operation(oc)) 41 | return; 42 | if(debug_mode) 43 | debug_print("Swapping top 2 objects of stack..."); 44 | object1 = pop(vm.sp); 45 | object2 = pop(vm.sp); 46 | push(vm.sp, object1); 47 | push(vm.sp, object2); 48 | return; 49 | } 50 | 51 | // Translates lexer/parser tokens to VM instructions 52 | void compile(ConcoctNodeTree* tree, ConcoctHashMap* map) 53 | { 54 | ConcoctNode* root = tree->root; 55 | Queue queue; 56 | Queue* pqueue = &queue; 57 | size_t ic = 0; // instruction count 58 | 59 | if(root == NULL) 60 | return; 61 | 62 | // Use a level-order algorithm to walk the parser tree 63 | init_queue(pqueue); 64 | enqueue(pqueue, root); 65 | while(!is_empty(pqueue)) 66 | { 67 | ConcoctNode* current = NULL; 68 | dequeue(pqueue, (void **)¤t); 69 | // ToDo: Add remaining cases 70 | switch(current->token.type) 71 | { 72 | case CCT_TOKEN_ADD: 73 | vm.instructions[ic] = OP_ADD; 74 | ic++; 75 | break; 76 | case CCT_TOKEN_ADD_ASSIGN: 77 | vm.instructions[ic] = OP_ADD; 78 | ic++; 79 | vm.instructions[ic] = OP_ASN; 80 | ic++; 81 | break; 82 | case CCT_TOKEN_AND: 83 | vm.instructions[ic] = OP_AND; 84 | ic++; 85 | break; 86 | case CCT_TOKEN_ASSIGN: 87 | vm.instructions[ic] = OP_ASN; // this operation should precede an identifier (CCT_TOKEN_IDENTIFIER) 88 | ic++; 89 | break; 90 | case CCT_TOKEN_BIN_AND: 91 | vm.instructions[ic] = OP_BND; 92 | ic++; 93 | break; 94 | case CCT_TOKEN_BIN_OR: 95 | vm.instructions[ic] = OP_BOR; 96 | ic++; 97 | break; 98 | case CCT_TOKEN_BIN_XOR: 99 | vm.instructions[ic] = OP_XOR; 100 | ic++; 101 | break; 102 | case CCT_TOKEN_CHAR: 103 | push(vm.sp, new_object_by_type(current->text, CCT_TYPE_BYTE)); 104 | break; 105 | case CCT_TOKEN_DEC: 106 | vm.instructions[ic] = OP_DEC; 107 | ic++; 108 | break; 109 | case CCT_TOKEN_DIV: 110 | vm.instructions[ic] = OP_DIV; 111 | ic++; 112 | break; 113 | case CCT_TOKEN_DIV_ASSIGN: 114 | // OP_DIV + OP_ASN 115 | break; 116 | case CCT_TOKEN_EQUAL: 117 | vm.instructions[ic] = OP_EQL; 118 | ic++; 119 | break; 120 | case CCT_TOKEN_EXP: 121 | vm.instructions[ic] = OP_POW; 122 | ic++; 123 | break; 124 | case CCT_TOKEN_EXP_ASSIGN: 125 | // OP_POW + OP_ASN 126 | break; 127 | case CCT_TOKEN_FALSE: 128 | push(vm.sp, new_object("false")); 129 | break; 130 | case CCT_TOKEN_FLOAT: 131 | push(vm.sp, new_object_by_type(current->text, CCT_TYPE_DECIMAL)); 132 | break; 133 | case CCT_TOKEN_GREATER: 134 | vm.instructions[ic] = OP_GT; 135 | ic++; 136 | break; 137 | case CCT_TOKEN_GREATER_EQUAL: 138 | vm.instructions[ic] = OP_GTE; 139 | ic++; 140 | break; 141 | case CCT_TOKEN_IDENTIFIER: 142 | if(!cct_hash_map_has_key(map, current->text)) 143 | { 144 | push(vm.sp, new_object_by_type(current->text, CCT_TYPE_STRING)); 145 | } 146 | else 147 | { 148 | // Identifier already exists. Flag the original object for garbage collection and delete the value. 149 | Object* object = cct_hash_map_get(map, current->text); 150 | object->is_flagged=true; 151 | cct_hash_map_delete_entry(map, current->text); 152 | } 153 | break; 154 | case CCT_TOKEN_INC: 155 | vm.instructions[ic] = OP_INC; 156 | ic++; 157 | break; 158 | case CCT_TOKEN_INT: 159 | push(vm.sp, new_object(current->text)); 160 | break; 161 | case CCT_TOKEN_LESS: 162 | vm.instructions[ic] = OP_LT; 163 | ic++; 164 | break; 165 | case CCT_TOKEN_LESS_EQUAL: 166 | vm.instructions[ic] = OP_LTE; 167 | ic++; 168 | break; 169 | case CCT_TOKEN_MOD: 170 | vm.instructions[ic] = OP_MOD; 171 | ic++; 172 | break; 173 | case CCT_TOKEN_MOD_ASSIGN: 174 | // OP_MOD + OP_ASN 175 | break; 176 | case CCT_TOKEN_MUL: 177 | vm.instructions[ic] = OP_MUL; 178 | ic++; 179 | break; 180 | case CCT_TOKEN_MUL_ASSIGN: 181 | // OP_MUL + OP_ASN 182 | break; 183 | case CCT_TOKEN_NOT: 184 | vm.instructions[ic] = OP_NOT; 185 | ic++; 186 | break; 187 | case CCT_TOKEN_NOT_EQUAL: 188 | vm.instructions[ic] = OP_NEQ; 189 | ic++; 190 | break; 191 | case CCT_TOKEN_NEWLINE: 192 | // Ignore whitespace 193 | break; 194 | case CCT_TOKEN_OR: 195 | vm.instructions[ic] = OP_OR; 196 | ic++; 197 | break; 198 | case CCT_TOKEN_SHL: 199 | vm.instructions[ic] = OP_SHL; 200 | ic++; 201 | break; 202 | case CCT_TOKEN_SHR: 203 | vm.instructions[ic] = OP_SHR; 204 | ic++; 205 | break; 206 | case CCT_TOKEN_STRING: 207 | push(vm.sp, new_object_by_type(current->text, CCT_TYPE_STRING)); 208 | break; 209 | case CCT_TOKEN_STRLEN_EQUAL: 210 | vm.instructions[ic] = OP_SLE; 211 | ic++; 212 | break; 213 | case CCT_TOKEN_STRLEN_NOT_EQUAL: 214 | vm.instructions[ic] = OP_SLN; 215 | ic++; 216 | break; 217 | case CCT_TOKEN_SUB: 218 | vm.instructions[ic] = OP_SUB; 219 | ic++; 220 | break; 221 | case CCT_TOKEN_SUB_ASSIGN: 222 | // OP_SUB + OP_ASN 223 | break; 224 | case CCT_TOKEN_TRUE: 225 | push(vm.sp, new_object("true")); 226 | break; 227 | case CCT_TOKEN_UNARY_MINUS: 228 | vm.instructions[ic] = OP_NEG; 229 | ic++; 230 | break; 231 | default: 232 | fprintf(stderr, "Unable to handle token: %s\n", cct_token_type_to_string(current->token.type)); 233 | break; 234 | } 235 | for(size_t i = 0; i < current->child_count; i++) 236 | enqueue(pqueue, current->children[i]); 237 | } 238 | 239 | if(ic > 0) 240 | { 241 | reverse_instructions(ic); 242 | swap_last_operands(vm.instructions[0]); 243 | vm.instructions[ic] = OP_END; // append end instruction 244 | interpret(map); 245 | } 246 | 247 | return; 248 | } 249 | -------------------------------------------------------------------------------- /src/vm/vm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // errno 29 | #include // PRIXPTR 30 | #include // fprintf(), printf() 31 | #include // malloc() 32 | #include // memset(), strcmp(), strerror() 33 | #include "debug.h" 34 | #include "memory.h" 35 | #include "vm/instructions.h" 36 | #include "vm/opcodes.h" 37 | #include "vm/vm.h" 38 | 39 | VM vm; 40 | Opcode** IP; 41 | Object** RP; 42 | Stack** SP; 43 | 44 | // Initializes virtual machine 45 | void init_vm(void) 46 | { 47 | memset(vm.registers, 0, sizeof(vm.registers)); 48 | vm.instructions = (Opcode *)malloc(INSTRUCTION_STORE_SIZE * sizeof(Opcode)); 49 | if(vm.instructions == NULL) 50 | { 51 | fprintf(stderr, "Error allocating memory for instruction store: %s\n", strerror(errno)); 52 | return; 53 | } 54 | clear_instructions(); 55 | vm.ip = vm.instructions; 56 | vm.rp = vm.registers; 57 | vm.sp = &vm.stack; 58 | IP = &vm.ip; 59 | RP = vm.rp; 60 | SP = &vm.sp; 61 | init_stack(vm.sp); 62 | init_store(); 63 | if(debug_mode) 64 | debug_print("VM initialized."); 65 | return; 66 | } 67 | 68 | // Stops virtual machine 69 | void stop_vm(void) 70 | { 71 | free(vm.instructions); 72 | free_store(); 73 | if(debug_mode) 74 | debug_print("VM stopped."); 75 | return; 76 | } 77 | 78 | // Clears instructions 79 | void clear_instructions(void) 80 | { 81 | for(size_t i = 0; i < INSTRUCTION_STORE_SIZE; i++) 82 | vm.instructions[i] = 0xFF; 83 | } 84 | 85 | // Reverses instructions since they should be in a LIFO arrangement 86 | void reverse_instructions(size_t ic) 87 | { 88 | if(ic > 0) 89 | { 90 | size_t i = ic - 1; 91 | size_t j = 0; 92 | while(i > j) 93 | { 94 | Opcode tmp = vm.instructions[i]; 95 | vm.instructions[i] = vm.instructions[j]; 96 | vm.instructions[j] = tmp; 97 | i--; 98 | j++; 99 | } 100 | } 101 | } 102 | 103 | // Prints register values 104 | void print_registers(void) 105 | { 106 | char* strval = NULL; 107 | void* vptr = NULL; 108 | Object* object = NULL; 109 | 110 | puts("Register values:"); 111 | for(uint8_t i = 0; i < REGISTER_AMOUNT; i++) 112 | { 113 | object = vm.registers[i]; 114 | if(object == NULL) 115 | { 116 | strval = NULL; 117 | } 118 | else 119 | { 120 | switch(object->datatype) 121 | { 122 | case CCT_TYPE_NIL: 123 | strval = "null"; 124 | break; 125 | case CCT_TYPE_BOOL: 126 | vptr = &object->value.boolval; 127 | stringify(&strval, vptr, CCT_TYPE_BOOL); 128 | break; 129 | case CCT_TYPE_BYTE: 130 | vptr = &object->value.byteval; 131 | stringify(&strval, vptr, CCT_TYPE_BYTE); 132 | break; 133 | case CCT_TYPE_NUMBER: 134 | vptr = &object->value.numval; 135 | stringify(&strval, vptr, CCT_TYPE_NUMBER); 136 | break; 137 | case CCT_TYPE_BIGNUM: 138 | vptr = &object->value.bignumval; 139 | stringify(&strval, vptr, CCT_TYPE_BIGNUM); 140 | break; 141 | case CCT_TYPE_DECIMAL: 142 | vptr = &object->value.decimalval; 143 | stringify(&strval, vptr, CCT_TYPE_DECIMAL); 144 | break; 145 | case CCT_TYPE_STRING: 146 | vptr = &object->value.strobj.strval; 147 | stringify(&strval, vptr, CCT_TYPE_STRING); 148 | break; 149 | default: 150 | strval = "Unknown"; 151 | break; 152 | } 153 | } 154 | 155 | if(i == REGISTER_AMOUNT - 1) 156 | { 157 | if(strval == NULL) 158 | printf("RS: empty\n"); 159 | else 160 | printf("RS: %.64s (%s)\n", strval, get_data_type(object)); 161 | } 162 | else 163 | { 164 | if(strval == NULL) 165 | printf("R%u: empty\n", i); 166 | else 167 | printf("R%u: %.64s (%s)\n", i, strval, get_data_type(object)); 168 | } 169 | 170 | if(strval) 171 | free(strval); 172 | } 173 | 174 | if((*SP)->count > 0) 175 | { 176 | object = (*SP)->objects[(*SP)->top]; 177 | switch(object->datatype) 178 | { 179 | case CCT_TYPE_NIL: 180 | strval = "null"; 181 | break; 182 | case CCT_TYPE_BOOL: 183 | vptr = &object->value.boolval; 184 | stringify(&strval, vptr, CCT_TYPE_BOOL); 185 | break; 186 | case CCT_TYPE_BYTE: 187 | vptr = &object->value.byteval; 188 | stringify(&strval, vptr, CCT_TYPE_BYTE); 189 | break; 190 | case CCT_TYPE_NUMBER: 191 | vptr = &object->value.numval; 192 | stringify(&strval, vptr, CCT_TYPE_NUMBER); 193 | break; 194 | case CCT_TYPE_BIGNUM: 195 | vptr = &object->value.bignumval; 196 | stringify(&strval, vptr, CCT_TYPE_BIGNUM); 197 | break; 198 | case CCT_TYPE_DECIMAL: 199 | vptr = &object->value.decimalval; 200 | stringify(&strval, vptr, CCT_TYPE_DECIMAL); 201 | break; 202 | case CCT_TYPE_STRING: 203 | vptr = &object->value.strobj.strval; 204 | stringify(&strval, vptr, CCT_TYPE_STRING); 205 | break; 206 | default: 207 | strval = "Unknown"; 208 | break; 209 | } 210 | printf("IP: %s (0x%02X)\nRP: 0x%" PRIXPTR "\nSP: %.64s (%s)\n\n", get_mnemonic(**IP), **IP, (uintptr_t)*RP, strval, get_data_type(object)); 211 | } 212 | else 213 | printf("IP: %s (0x%02X)\nRP: 0x%" PRIXPTR "\nSP: empty\n\n", get_mnemonic(**IP), **IP, (uintptr_t)*RP); 214 | if(strval != NULL && (strcmp(strval, "null") != 0)) 215 | free(strval); 216 | return; 217 | } 218 | 219 | // Interprets code 220 | RunCode interpret(ConcoctHashMap* map) 221 | { 222 | char* value = NULL; 223 | Object* object = NULL; 224 | Byte src_reg = R1; 225 | Byte dst_reg = R0; 226 | 227 | while(*vm.ip != OP_END) 228 | { 229 | if(debug_mode) 230 | printf("Instruction: %s (0x%02X)\n", get_mnemonic(*vm.ip), *vm.ip); 231 | switch(*vm.ip) 232 | { 233 | case OP_ADD: 234 | op_add(vm.sp); 235 | if(debug_mode) 236 | print_object_value(peek(vm.sp)); 237 | break; 238 | case OP_AND: 239 | op_and(vm.sp); 240 | if(debug_mode) 241 | print_object_value(peek(vm.sp)); 242 | break; 243 | case OP_ASN: 244 | op_asn(vm.sp, map); 245 | break; 246 | case OP_BND: 247 | op_bnd(vm.sp); 248 | if(debug_mode) 249 | print_object_value(peek(vm.sp)); 250 | break; 251 | case OP_BNT: 252 | op_bnt(vm.sp); 253 | if(debug_mode) 254 | print_object_value(peek(vm.sp)); 255 | break; 256 | case OP_BOR: 257 | op_bor(vm.sp); 258 | if(debug_mode) 259 | print_object_value(peek(vm.sp)); 260 | break; 261 | case OP_CAL: 262 | break; 263 | case OP_CLR: 264 | op_clr(vm.rp); 265 | if(debug_mode) 266 | print_registers(); 267 | break; 268 | case OP_CLS: 269 | op_cls(vm.sp); 270 | break; 271 | case OP_CMP: 272 | break; 273 | case OP_DEC: 274 | op_dec(vm.sp); 275 | if(debug_mode) 276 | print_object_value(peek(vm.sp)); 277 | break; 278 | case OP_DIV: 279 | op_div(vm.sp); 280 | if(debug_mode) 281 | print_object_value(peek(vm.sp)); 282 | break; 283 | case OP_END: 284 | if(debug_mode) 285 | print_registers(); 286 | break; 287 | case OP_ENT: 288 | break; 289 | case OP_EQL: 290 | op_eql(vm.sp); 291 | if(debug_mode) 292 | print_object_value(peek(vm.sp)); 293 | break; 294 | case OP_EXT: 295 | break; 296 | case OP_GT: 297 | op_gt(vm.sp); 298 | if(debug_mode) 299 | print_object_value(peek(vm.sp)); 300 | break; 301 | case OP_GTE: 302 | op_gte(vm.sp); 303 | if(debug_mode) 304 | print_object_value(peek(vm.sp)); 305 | break; 306 | case OP_HLT: 307 | stop_vm(); 308 | break; 309 | case OP_INC: 310 | op_inc(vm.sp); 311 | if(debug_mode) 312 | print_object_value(peek(vm.sp)); 313 | break; 314 | case OP_JMC: 315 | case OP_JMP: 316 | case OP_JMZ: 317 | case OP_LNE: 318 | case OP_LNZ: 319 | break; 320 | case OP_LOD: 321 | op_lod(vm.rp, vm.sp, dst_reg); 322 | if(debug_mode) 323 | print_registers(); 324 | break; 325 | case OP_LOE: 326 | case OP_LOP: 327 | break; 328 | case OP_LOZ: 329 | break; 330 | case OP_LT: 331 | op_lt(vm.sp); 332 | if(debug_mode) 333 | print_object_value(peek(vm.sp)); 334 | break; 335 | case OP_LTE: 336 | op_lte(vm.sp); 337 | if(debug_mode) 338 | print_object_value(peek(vm.sp)); 339 | break; 340 | case OP_MOD: 341 | op_mod(vm.sp); 342 | if(debug_mode) 343 | print_object_value(peek(vm.sp)); 344 | break; 345 | case OP_MOV: 346 | op_mov(vm.rp, object, src_reg, dst_reg); 347 | if(debug_mode) 348 | print_registers(); 349 | break; 350 | case OP_MUL: 351 | op_mul(vm.sp); 352 | if(debug_mode) 353 | print_object_value(peek(vm.sp)); 354 | break; 355 | case OP_NEG: 356 | op_neg(vm.sp); 357 | if(debug_mode) 358 | print_object_value(peek(vm.sp)); 359 | break; 360 | case OP_NEQ: 361 | op_neq(vm.sp); 362 | if(debug_mode) 363 | print_object_value(peek(vm.sp)); 364 | break; 365 | case OP_NOP: 366 | OP_NOOP; 367 | break; 368 | case OP_NOT: 369 | op_not(vm.sp); 370 | if(debug_mode) 371 | print_object_value(peek(vm.sp)); 372 | break; 373 | case OP_NUL: 374 | break; 375 | case OP_OR: 376 | op_or(vm.sp); 377 | if(debug_mode) 378 | print_object_value(peek(vm.sp)); 379 | break; 380 | case OP_POP: 381 | op_pop(vm.sp, object); 382 | if(debug_mode) 383 | print_object_value(peek(vm.sp)); 384 | break; 385 | case OP_POS: 386 | op_pos(vm.sp); 387 | if(debug_mode) 388 | print_object_value(peek(vm.sp)); 389 | break; 390 | case OP_POW: 391 | op_pow(vm.sp); 392 | if(debug_mode) 393 | print_object_value(peek(vm.sp)); 394 | break; 395 | case OP_PSH: 396 | op_psh(vm.sp, value); 397 | if(debug_mode) 398 | print_object_value(peek(vm.sp)); 399 | break; 400 | case OP_RET: 401 | break; 402 | case OP_SHL: 403 | op_shl(vm.sp); 404 | if(debug_mode) 405 | print_object_value(peek(vm.sp)); 406 | break; 407 | case OP_SHR: 408 | op_shr(vm.sp); 409 | if(debug_mode) 410 | print_object_value(peek(vm.sp)); 411 | break; 412 | case OP_SLE: 413 | op_sle(vm.sp); 414 | if(debug_mode) 415 | print_object_value(peek(vm.sp)); 416 | break; 417 | case OP_SLN: 418 | op_sln(vm.sp); 419 | if(debug_mode) 420 | print_object_value(peek(vm.sp)); 421 | break; 422 | case OP_STR: 423 | op_str(vm.rp, vm.sp, src_reg); 424 | if(debug_mode) 425 | { 426 | print_registers(); 427 | print_object_value(peek(vm.sp)); 428 | } 429 | break; 430 | case OP_SUB: 431 | op_sub(vm.sp); 432 | if(debug_mode) 433 | print_object_value(peek(vm.sp)); 434 | break; 435 | case OP_SYS: 436 | //op_sys(vm.sp); 437 | //if(debug_mode) 438 | // print_object_value(pop(vm.sp)); 439 | break; 440 | case OP_TST: 441 | break; 442 | case OP_XCG: 443 | op_xcg(vm.rp, src_reg, dst_reg); 444 | if(debug_mode) 445 | print_registers(); 446 | break; 447 | case OP_XOR: 448 | op_xor(vm.sp); 449 | if(debug_mode) 450 | print_object_value(peek(vm.sp)); 451 | break; 452 | default: 453 | fprintf(stderr, "Illegal instruction: %s (0x%02X)\n", get_mnemonic(*vm.ip), *vm.ip); 454 | return RUN_ERROR; 455 | } 456 | (vm.ip)++; 457 | } 458 | 459 | if(debug_mode) 460 | print_registers(); 461 | 462 | vm.ip = vm.instructions; // reset VM instruction pointer to beginning of instructions 463 | clear_instructions(); 464 | 465 | return RUN_SUCCESS; 466 | } 467 | -------------------------------------------------------------------------------- /lib/linenoise/README.md: -------------------------------------------------------------------------------- 1 | # Linenoise 2 | 3 | A minimal, zero-config, BSD licensed, readline replacement used in Redis, 4 | MongoDB, Android and many other projects. 5 | 6 | * Single and multi line editing mode with the usual key bindings implemented. 7 | * History handling. 8 | * Completion. 9 | * Hints (suggestions at the right of the prompt as you type). 10 | * Multiplexing mode, with prompt hiding/restoring for asynchronous output. 11 | * About ~850 lines (comments and spaces excluded) of BSD license source code. 12 | * Only uses a subset of VT100 escapes (ANSI.SYS compatible). 13 | 14 | ## Can a line editing library be 20k lines of code? 15 | 16 | Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing? 17 | 18 | So what usually happens is either: 19 | 20 | * Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (real world example of this problem: Tclsh). 21 | * Smaller programs not using a configure script not supporting line editing at all (A problem we had with `redis-cli`, for instance). 22 | 23 | The result is a pollution of binaries without line editing support. 24 | 25 | So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not. 26 | 27 | ## Terminals, in 2010. 28 | 29 | Apparently almost every terminal you can happen to use today has some kind of support for basic VT100 escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it, and now can work even on ANSI.SYS compatible terminals, since no 30 | VT220 specific sequences are used anymore. 31 | 32 | The library is currently about 850 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is pretty straightforward. The library supports both a blocking mode and a multiplexing mode, see the API documentation later in this file for more information. 33 | 34 | Linenoise is BSD-licensed code, so you can use both in free software and commercial software. 35 | 36 | ## Tested with... 37 | 38 | * Linux text only console ($TERM = linux) 39 | * Linux KDE terminal application ($TERM = xterm) 40 | * Linux xterm ($TERM = xterm) 41 | * Linux Buildroot ($TERM = vt100) 42 | * Mac OS X iTerm ($TERM = xterm) 43 | * Mac OS X default Terminal.app ($TERM = xterm) 44 | * OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen) 45 | * IBM AIX 6.1 46 | * FreeBSD xterm ($TERM = xterm) 47 | * ANSI.SYS 48 | * Emacs comint mode ($TERM = dumb) 49 | 50 | Please test it everywhere you can and report back! 51 | 52 | ## Let's push this forward! 53 | 54 | Patches should be provided in the respect of Linenoise sensibility for small 55 | easy to understand code. 56 | 57 | Send feedbacks to antirez at gmail 58 | 59 | # The API 60 | 61 | Linenoise is very easy to use, and reading the example shipped with the 62 | library should get you up to speed ASAP. Here is a list of API calls 63 | and how to use them. Let's start with the simple blocking mode: 64 | 65 | char *linenoise(const char *prompt); 66 | 67 | This is the main Linenoise call: it shows the user a prompt with line editing 68 | and history capabilities. The prompt you specify is used as a prompt, that is, 69 | it will be printed to the left of the cursor. The library returns a buffer 70 | with the line composed by the user, or NULL on end of file or when there 71 | is an out of memory condition. 72 | 73 | When a tty is detected (the user is actually typing into a terminal session) 74 | the maximum editable line length is `LINENOISE_MAX_LINE`. When instead the 75 | standard input is not a tty, which happens every time you redirect a file 76 | to a program, or use it in an Unix pipeline, there are no limits to the 77 | length of the line that can be returned. 78 | 79 | The returned line should be freed with the `free()` standard system call. 80 | However sometimes it could happen that your program uses a different dynamic 81 | allocation library, so you may also used `linenoiseFree` to make sure the 82 | line is freed with the same allocator it was created. 83 | 84 | The canonical loop used by a program using Linenoise will be something like 85 | this: 86 | 87 | while((line = linenoise("hello> ")) != NULL) { 88 | printf("You wrote: %s\n", line); 89 | linenoiseFree(line); /* Or just free(line) if you use libc malloc. */ 90 | } 91 | 92 | ## Single line VS multi line editing 93 | 94 | By default, Linenoise uses single line editing, that is, a single row on the 95 | screen will be used, and as the user types more, the text will scroll towards 96 | left to make room. This works if your program is one where the user is 97 | unlikely to write a lot of text, otherwise multi line editing, where multiple 98 | screens rows are used, can be a lot more comfortable. 99 | 100 | In order to enable multi line editing use the following API call: 101 | 102 | linenoiseSetMultiLine(1); 103 | 104 | You can disable it using `0` as argument. 105 | 106 | ## History 107 | 108 | Linenoise supporst history, so that the user does not have to retype 109 | again and again the same things, but can use the down and up arrows in order 110 | to search and re-edit already inserted lines of text. 111 | 112 | The followings are the history API calls: 113 | 114 | int linenoiseHistoryAdd(const char *line); 115 | int linenoiseHistorySetMaxLen(int len); 116 | int linenoiseHistorySave(const char *filename); 117 | int linenoiseHistoryLoad(const char *filename); 118 | 119 | Use `linenoiseHistoryAdd` every time you want to add a new element 120 | to the top of the history (it will be the first the user will see when 121 | using the up arrow). 122 | 123 | Note that for history to work, you have to set a length for the history 124 | (which is zero by default, so history will be disabled if you don't set 125 | a proper one). This is accomplished using the `linenoiseHistorySetMaxLen` 126 | function. 127 | 128 | Linenoise has direct support for persisting the history into an history 129 | file. The functions `linenoiseHistorySave` and `linenoiseHistoryLoad` do 130 | just that. Both functions return -1 on error and 0 on success. 131 | 132 | ## Mask mode 133 | 134 | Sometimes it is useful to allow the user to type passwords or other 135 | secrets that should not be displayed. For such situations linenoise supports 136 | a "mask mode" that will just replace the characters the user is typing 137 | with `*` characters, like in the following example: 138 | 139 | $ ./linenoise_example 140 | hello> get mykey 141 | echo: 'get mykey' 142 | hello> /mask 143 | hello> ********* 144 | 145 | You can enable and disable mask mode using the following two functions: 146 | 147 | void linenoiseMaskModeEnable(void); 148 | void linenoiseMaskModeDisable(void); 149 | 150 | ## Completion 151 | 152 | Linenoise supports completion, which is the ability to complete the user 153 | input when she or he presses the `` key. 154 | 155 | In order to use completion, you need to register a completion callback, which 156 | is called every time the user presses ``. Your callback will return a 157 | list of items that are completions for the current string. 158 | 159 | The following is an example of registering a completion callback: 160 | 161 | linenoiseSetCompletionCallback(completion); 162 | 163 | The completion must be a function returning `void` and getting as input 164 | a `const char` pointer, which is the line the user has typed so far, and 165 | a `linenoiseCompletions` object pointer, which is used as argument of 166 | `linenoiseAddCompletion` in order to add completions inside the callback. 167 | An example will make it more clear: 168 | 169 | void completion(const char *buf, linenoiseCompletions *lc) { 170 | if (buf[0] == 'h') { 171 | linenoiseAddCompletion(lc,"hello"); 172 | linenoiseAddCompletion(lc,"hello there"); 173 | } 174 | } 175 | 176 | Basically in your completion callback, you inspect the input, and return 177 | a list of items that are good completions by using `linenoiseAddCompletion`. 178 | 179 | If you want to test the completion feature, compile the example program 180 | with `make`, run it, type `h` and press ``. 181 | 182 | ## Hints 183 | 184 | Linenoise has a feature called *hints* which is very useful when you 185 | use Linenoise in order to implement a REPL (Read Eval Print Loop) for 186 | a program that accepts commands and arguments, but may also be useful in 187 | other conditions. 188 | 189 | The feature shows, on the right of the cursor, as the user types, hints that 190 | may be useful. The hints can be displayed using a different color compared 191 | to the color the user is typing, and can also be bold. 192 | 193 | For example as the user starts to type `"git remote add"`, with hints it's 194 | possible to show on the right of the prompt a string ` `. 195 | 196 | The feature works similarly to the history feature, using a callback. 197 | To register the callback we use: 198 | 199 | linenoiseSetHintsCallback(hints); 200 | 201 | The callback itself is implemented like this: 202 | 203 | char *hints(const char *buf, int *color, int *bold) { 204 | if (!strcasecmp(buf,"git remote add")) { 205 | *color = 35; 206 | *bold = 0; 207 | return " "; 208 | } 209 | return NULL; 210 | } 211 | 212 | The callback function returns the string that should be displayed or NULL 213 | if no hint is available for the text the user currently typed. The returned 214 | string will be trimmed as needed depending on the number of columns available 215 | on the screen. 216 | 217 | It is possible to return a string allocated in dynamic way, by also registering 218 | a function to deallocate the hint string once used: 219 | 220 | void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); 221 | 222 | The free hint callback will just receive the pointer and free the string 223 | as needed (depending on how the hits callback allocated it). 224 | 225 | As you can see in the example above, a `color` (in xterm color terminal codes) 226 | can be provided together with a `bold` attribute. If no color is set, the 227 | current terminal foreground color is used. If no bold attribute is set, 228 | non-bold text is printed. 229 | 230 | Color codes are: 231 | 232 | red = 31 233 | green = 32 234 | yellow = 33 235 | blue = 34 236 | magenta = 35 237 | cyan = 36 238 | white = 37; 239 | 240 | ## Screen handling 241 | 242 | Sometimes you may want to clear the screen as a result of something the 243 | user typed. You can do this by calling the following function: 244 | 245 | void linenoiseClearScreen(void); 246 | 247 | ## Asyncrhronous API 248 | 249 | Sometimes you want to read from the keyboard but also from sockets or other 250 | external events, and at the same time there could be input to display to the 251 | user *while* the user is typing something. Let's call this the "IRC problem", 252 | since if you want to write an IRC client with linenoise, without using 253 | some fully featured libcurses approach, you will surely end having such an 254 | issue. 255 | 256 | Fortunately now a multiplexing friendly API exists, and it is just what the 257 | blocking calls internally use. To start, we need to initialize a linenoise 258 | context like this: 259 | 260 | struct linenoiseState ls; 261 | char buf[1024]; 262 | linenoiseEditStart(&ls,-1,-1,buf,sizeof(buf),"some prompt> "); 263 | 264 | The two -1 and -1 arguments are the stdin/out descriptors. If they are 265 | set to -1, linenoise will just use the default stdin/out file descriptors. 266 | Now as soon as we have data from stdin (and we know it via select(2) or 267 | some other way), we can ask linenoise to read the next character with: 268 | 269 | linenoiseEditFeed(&ls); 270 | 271 | The function returns a `char` pointer: if the user didn't yet press enter 272 | to provide a line to the program, it will return `linenoiseEditMore`, that 273 | means we need to call `linenoiseEditFeed()` again when more data is 274 | available. If the function returns non NULL, then this is a heap allocated 275 | data (to be freed with `linenoiseFree()`) representing the user input. 276 | When the function returns NULL, than the user pressed CTRL-C or CTRL-D 277 | with an empty line, to quit the program, or there was some I/O error. 278 | 279 | After each line is received (or if you want to quit the program, and exit raw mode), the following function needs to be called: 280 | 281 | linenoiseEditStop(&ls); 282 | 283 | To start reading the next line, a new linenoiseEditStart() must 284 | be called, in order to reset the state, and so forth, so a typical event 285 | handler called when the standard input is readable, will work similarly 286 | to the example below: 287 | 288 | ``` c 289 | void stdinHasSomeData(void) { 290 | char *line = linenoiseEditFeed(&LineNoiseState); 291 | if (line == linenoiseEditMore) return; 292 | linenoiseEditStop(&LineNoiseState); 293 | if (line == NULL) exit(0); 294 | 295 | printf("line: %s\n", line); 296 | linenoiseFree(line); 297 | linenoiseEditStart(&LineNoiseState,-1,-1,LineNoiseBuffer,sizeof(LineNoiseBuffer),"serial> "); 298 | } 299 | ``` 300 | 301 | Now that we have a way to avoid blocking in the user input, we can use 302 | two calls to hide/show the edited line, so that it is possible to also 303 | show some input that we received (from socekts, bluetooth, whatever) on 304 | screen: 305 | 306 | linenoiseHide(&ls); 307 | printf("some data...\n"); 308 | linenoiseShow(&ls); 309 | 310 | To the API calls, the linenoise example C file implements a multiplexing 311 | example using select(2) and the asynchronous API: 312 | 313 | ```c 314 | struct linenoiseState ls; 315 | char buf[1024]; 316 | linenoiseEditStart(&ls,-1,-1,buf,sizeof(buf),"hello> "); 317 | 318 | while(1) { 319 | // Select(2) setup code removed... 320 | retval = select(ls.ifd+1, &readfds, NULL, NULL, &tv); 321 | if (retval == -1) { 322 | perror("select()"); 323 | exit(1); 324 | } else if (retval) { 325 | line = linenoiseEditFeed(&ls); 326 | /* A NULL return means: line editing is continuing. 327 | * Otherwise the user hit enter or stopped editing 328 | * (CTRL+C/D). */ 329 | if (line != linenoiseEditMore) break; 330 | } else { 331 | // Timeout occurred 332 | static int counter = 0; 333 | linenoiseHide(&ls); 334 | printf("Async output %d.\n", counter++); 335 | linenoiseShow(&ls); 336 | } 337 | } 338 | linenoiseEditStop(&ls); 339 | if (line == NULL) exit(0); /* Ctrl+D/C. */ 340 | ``` 341 | 342 | You can test the example by running the example program with the `--async` option. 343 | 344 | ## Related projects 345 | 346 | * [Linenoise NG](https://github.com/arangodb/linenoise-ng) is a fork of Linenoise that aims to add more advanced features like UTF-8 support, Windows support and other features. Uses C++ instead of C as development language. 347 | * [Linenoise-swift](https://github.com/andybest/linenoise-swift) is a reimplementation of Linenoise written in Swift. 348 | -------------------------------------------------------------------------------- /src/tests/stack_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include "debug.h" 30 | #include "memory.h" 31 | #include "stack.h" 32 | #include "types.h" 33 | #include "vm/instructions.h" 34 | 35 | int main(void) 36 | { 37 | debug_mode = true; 38 | Stack stack; 39 | Stack* pstack = &stack; 40 | init_stack(pstack); 41 | init_store(); 42 | 43 | Object* object = new_object("null"); 44 | push(pstack, object); 45 | printf("Data type: %s\n", get_data_type(peek(pstack))); 46 | print_object_value(peek(pstack)); 47 | 48 | object = new_object("true"); 49 | push(pstack, object); 50 | printf("Data type: %s\n", get_data_type(peek(pstack))); 51 | print_object_value(peek(pstack)); 52 | 53 | object = new_object("100"); 54 | push(pstack, object); 55 | printf("Data type: %s\n", get_data_type(peek(pstack))); 56 | print_object_value(peek(pstack)); 57 | 58 | object = new_object("5721452096347253"); 59 | push(pstack, object); 60 | printf("Data type: %s\n", get_data_type(peek(pstack))); 61 | print_object_value(peek(pstack)); 62 | 63 | object = new_object("77.715"); 64 | push(pstack, object); 65 | printf("Data type: %s\n", get_data_type(peek(pstack))); 66 | print_object_value(peek(pstack)); 67 | 68 | object = new_object("Greetings, Concocter!"); 69 | push(pstack, object); 70 | printf("Data type: %s\n", get_data_type(peek(pstack))); 71 | print_object_value(peek(pstack)); 72 | 73 | puts("\nValue of each stack item after pop():"); 74 | for(size_t i = pstack->count; i > 0; i--) 75 | print_object_value(pop(pstack)); 76 | 77 | puts("\nAdding false and false to stack..."); 78 | object = new_object("false"); 79 | push(pstack, object); 80 | object = new_object("false"); 81 | push(pstack, object); 82 | puts("Anding result..."); 83 | op_and(pstack); 84 | puts("Value after anding stack contents:"); 85 | print_object_value(pop(pstack)); 86 | 87 | puts("\nAdding true and false to stack..."); 88 | object = new_object("true"); 89 | push(pstack, object); 90 | object = new_object("false"); 91 | push(pstack, object); 92 | puts("Anding result..."); 93 | op_and(pstack); 94 | puts("Value after anding stack contents:"); 95 | print_object_value(pop(pstack)); 96 | 97 | puts("\nAdding true and true to stack..."); 98 | object = new_object("true"); 99 | push(pstack, object); 100 | object = new_object("true"); 101 | push(pstack, object); 102 | puts("Anding result..."); 103 | op_and(pstack); 104 | puts("Value after anding stack contents:"); 105 | print_object_value(pop(pstack)); 106 | 107 | puts("\nAdding false to stack..."); 108 | object = new_object("false"); 109 | push(pstack, object); 110 | puts("Negating result..."); 111 | op_not(pstack); 112 | puts("Value after negating stack contents:"); 113 | print_object_value(pop(pstack)); 114 | 115 | puts("\nAdding true to stack..."); 116 | object = new_object("true"); 117 | push(pstack, object); 118 | puts("Negating result..."); 119 | op_not(pstack); 120 | puts("Value after negating stack contents:"); 121 | print_object_value(pop(pstack)); 122 | 123 | puts("\nAdding false and false to stack..."); 124 | object = new_object("false"); 125 | push(pstack, object); 126 | object = new_object("false"); 127 | push(pstack, object); 128 | puts("Oring result..."); 129 | op_or(pstack); 130 | puts("Value after oring stack contents:"); 131 | print_object_value(pop(pstack)); 132 | 133 | puts("\nAdding true and false to stack..."); 134 | object = new_object("true"); 135 | push(pstack, object); 136 | object = new_object("false"); 137 | push(pstack, object); 138 | puts("Oring result..."); 139 | op_or(pstack); 140 | puts("Value after oring stack contents:"); 141 | print_object_value(pop(pstack)); 142 | 143 | puts("\nAdding true and true to stack..."); 144 | object = new_object("true"); 145 | push(pstack, object); 146 | object = new_object("true"); 147 | push(pstack, object); 148 | puts("Oring result..."); 149 | op_or(pstack); 150 | puts("Value after oring stack contents:"); 151 | print_object_value(pop(pstack)); 152 | 153 | puts("\nAdding 5 and 5.0 to stack..."); 154 | object = new_object("5"); 155 | push(pstack, object); 156 | object = new_object("5.0"); 157 | push(pstack, object); 158 | puts("Checking equality of result..."); 159 | op_eql(pstack); 160 | puts("Value after checking equality of stack contents:"); 161 | print_object_value(pop(pstack)); 162 | 163 | puts("\nAdding 5 and 5.0 to stack..."); 164 | object = new_object("5"); 165 | push(pstack, object); 166 | object = new_object("5.0"); 167 | push(pstack, object); 168 | puts("Checking inequality of result..."); 169 | op_neq(pstack); 170 | puts("Value after checking inequality of stack contents:"); 171 | print_object_value(pop(pstack)); 172 | 173 | puts("\nAdding 5 and 5.5 to stack..."); 174 | object = new_object("5"); 175 | push(pstack, object); 176 | object = new_object("5.5"); 177 | push(pstack, object); 178 | puts("Checking equality of result..."); 179 | op_eql(pstack); 180 | puts("Value after checking equality of stack contents:"); 181 | print_object_value(pop(pstack)); 182 | 183 | puts("\nAdding 5 and 5.5 to stack..."); 184 | object = new_object("5"); 185 | push(pstack, object); 186 | object = new_object("5.5"); 187 | push(pstack, object); 188 | puts("Checking inequality of result..."); 189 | op_neq(pstack); 190 | puts("Value after checking inequality of stack contents:"); 191 | print_object_value(pop(pstack)); 192 | 193 | puts("\nAdding \"foo\" and \"foo\" to stack..."); 194 | object = new_object("foo"); 195 | push(pstack, object); 196 | object = new_object("foo"); 197 | push(pstack, object); 198 | puts("Checking equality of result..."); 199 | op_eql(pstack); 200 | puts("Value after checking equality of stack contents:"); 201 | print_object_value(pop(pstack)); 202 | 203 | puts("\nAdding \"foo\" and \"foo\" to stack..."); 204 | object = new_object("foo"); 205 | push(pstack, object); 206 | object = new_object("foo"); 207 | push(pstack, object); 208 | puts("Checking inequality of result..."); 209 | op_neq(pstack); 210 | puts("Value after checking inequality of stack contents:"); 211 | print_object_value(pop(pstack)); 212 | 213 | puts("\nAdding \"foo\" and \"bar\" to stack..."); 214 | object = new_object("foo"); 215 | push(pstack, object); 216 | object = new_object("bar"); 217 | push(pstack, object); 218 | puts("Checking equality of result..."); 219 | op_eql(pstack); 220 | puts("Value after checking equality of stack contents:"); 221 | print_object_value(pop(pstack)); 222 | 223 | puts("\nAdding \"foo\" and \"bar\" to stack..."); 224 | object = new_object("foo"); 225 | push(pstack, object); 226 | object = new_object("bar"); 227 | push(pstack, object); 228 | puts("Checking inequality of result..."); 229 | op_neq(pstack); 230 | puts("Value after checking inequality of stack contents:"); 231 | print_object_value(pop(pstack)); 232 | 233 | puts("\nAdding null and null to stack..."); 234 | object = new_object("null"); 235 | push(pstack, object); 236 | object = new_object("null"); 237 | push(pstack, object); 238 | puts("Checking equality of result..."); 239 | op_eql(pstack); 240 | puts("Value after checking equality of stack contents:"); 241 | print_object_value(pop(pstack)); 242 | 243 | puts("\nAdding null and null to stack..."); 244 | object = new_object("null"); 245 | push(pstack, object); 246 | object = new_object("null"); 247 | push(pstack, object); 248 | puts("Checking inequality of result..."); 249 | op_neq(pstack); 250 | puts("Value after checking inequality of stack contents:"); 251 | print_object_value(pop(pstack)); 252 | 253 | puts("\nAdding 3 and 10 to stack..."); 254 | object = new_object("3"); 255 | push(pstack, object); 256 | object = new_object("10"); 257 | push(pstack, object); 258 | puts("Checking >= of result..."); 259 | op_gte(pstack); 260 | puts("Value after checking >= of stack contents:"); 261 | print_object_value(pop(pstack)); 262 | 263 | puts("\nAdding 3 and 10 to stack..."); 264 | object = new_object("3"); 265 | push(pstack, object); 266 | object = new_object("10"); 267 | push(pstack, object); 268 | puts("Checking <= of result..."); 269 | op_lte(pstack); 270 | puts("Value after checking <= of stack contents:"); 271 | print_object_value(pop(pstack)); 272 | 273 | puts("\nAdding 3 and 3 to stack..."); 274 | object = new_object("3"); 275 | push(pstack, object); 276 | object = new_object("3"); 277 | push(pstack, object); 278 | puts("Checking <= of result..."); 279 | op_lte(pstack); 280 | puts("Value after checking <= of stack contents:"); 281 | print_object_value(pop(pstack)); 282 | 283 | puts("\nAdding 20 to stack..."); 284 | object = new_object("20"); 285 | push(pstack, object); 286 | puts("Decrementing stack value..."); 287 | op_dec(pstack); 288 | puts("Value after decrementing stack contents:"); 289 | print_object_value(pop(pstack)); 290 | 291 | puts("\nAdding 20 to stack..."); 292 | object = new_object("20"); 293 | push(pstack, object); 294 | puts("Incrementing stack value..."); 295 | op_inc(pstack); 296 | puts("Value after incrementing stack contents:"); 297 | print_object_value(pop(pstack)); 298 | 299 | puts("\nAdding 3 to stack..."); 300 | object = new_object("3"); 301 | push(pstack, object); 302 | puts("Adding 7 to stack..."); 303 | object = new_object("7"); 304 | push(pstack, object); 305 | puts("Adding stack values..."); 306 | op_add(pstack); 307 | puts("Value after adding stack contents:"); 308 | print_object_value(pop(pstack)); 309 | 310 | puts("\nAdding 10 to stack..."); 311 | object = new_object("10"); 312 | push(pstack, object); 313 | puts("Adding 3 to stack..."); 314 | object = new_object("3"); 315 | push(pstack, object); 316 | puts("Subtracting stack values..."); 317 | op_sub(pstack); 318 | puts("Value after subtracting stack contents:"); 319 | print_object_value(pop(pstack)); 320 | 321 | puts("\nAdding 10 to stack..."); 322 | object = new_object("10"); 323 | push(pstack, object); 324 | puts("Adding 5 to stack..."); 325 | object = new_object("5"); 326 | push(pstack, object); 327 | puts("Dividing stack values..."); 328 | op_div(pstack); 329 | puts("Value after dividing stack contents:"); 330 | print_object_value(pop(pstack)); 331 | 332 | puts("\nAdding 10 to stack..."); 333 | object = new_object("10"); 334 | push(pstack, object); 335 | puts("Adding 0 to stack..."); 336 | object = new_object("0"); 337 | push(pstack, object); 338 | puts("Dividing stack values (zero test)..."); 339 | if(op_div(pstack) == RUN_ERROR) 340 | puts("Runtime error encountered!"); 341 | else 342 | { 343 | puts("Value after dividing stack contents:"); 344 | print_object_value(pop(pstack)); 345 | } 346 | 347 | puts("\nAdding 5 to stack..."); 348 | object = new_object("5"); 349 | push(pstack, object); 350 | puts("Adding 2 to stack..."); 351 | object = new_object("2"); 352 | push(pstack, object); 353 | puts("Multiplying stack values..."); 354 | op_mul(pstack); 355 | puts("Value after multiplying stack contents:"); 356 | print_object_value(pop(pstack)); 357 | 358 | puts("\nAdding 11 to stack..."); 359 | object = new_object("11"); 360 | push(pstack, object); 361 | puts("Adding 5 to stack..."); 362 | object = new_object("5"); 363 | push(pstack, object); 364 | puts("Modulating stack values..."); 365 | op_mod(pstack); 366 | puts("Value after modulating stack contents:"); 367 | print_object_value(pop(pstack)); 368 | 369 | puts("\nAdding 5 to stack..."); 370 | object = new_object("5"); 371 | push(pstack, object); 372 | puts("Adding 2 to stack..."); 373 | object = new_object("2"); 374 | push(pstack, object); 375 | puts("Exponentiating stack values..."); 376 | op_pow(pstack); 377 | puts("Value after exponentiating stack contents:"); 378 | print_object_value(pop(pstack)); 379 | 380 | puts("\nAdding 32 to stack..."); 381 | object = new_object("32"); 382 | push(pstack, object); 383 | puts("Adding 32 to stack..."); 384 | object = new_object("32"); 385 | push(pstack, object); 386 | puts("Performing bitwise and..."); 387 | op_bnd(pstack); 388 | puts("Value after performing bitwise and:"); 389 | print_object_value(pop(pstack)); 390 | 391 | puts("\nAdding 32 to stack..."); 392 | object = new_object("32"); 393 | push(pstack, object); 394 | puts("Adding 32 to stack..."); 395 | object = new_object("32"); 396 | push(pstack, object); 397 | puts("Performing bitwise or..."); 398 | op_bor(pstack); 399 | puts("Value after performing bitwise or:"); 400 | print_object_value(pop(pstack)); 401 | 402 | puts("\nAdding 32 to stack..."); 403 | object = new_object("32"); 404 | push(pstack, object); 405 | puts("Adding 32 to stack..."); 406 | object = new_object("32"); 407 | push(pstack, object); 408 | puts("Performing bitwise xor..."); 409 | op_xor(pstack); 410 | puts("Value after performing bitwise xor:"); 411 | print_object_value(pop(pstack)); 412 | 413 | puts("\nAdding 32 to stack..."); 414 | object = new_object("32"); 415 | push(pstack, object); 416 | puts("Performing bitwise not..."); 417 | op_bnt(pstack); 418 | puts("Value after performing bitwise not:"); 419 | print_object_value(pop(pstack)); 420 | 421 | puts("\nAdding 1 to stack..."); 422 | object = new_object("1"); 423 | push(pstack, object); 424 | puts("Adding 8 to stack..."); 425 | object = new_object("8"); 426 | push(pstack, object); 427 | puts("Bit shifting stack value to the left..."); 428 | op_shl(pstack); 429 | puts("Value after bit shifting stack contents to the left:"); 430 | print_object_value(pop(pstack)); 431 | 432 | puts("\nAdding 256 to stack..."); 433 | object = new_object("256"); 434 | push(pstack, object); 435 | puts("Adding 4 to stack..."); 436 | object = new_object("4"); 437 | push(pstack, object); 438 | puts("Bit shifting stack value to the right..."); 439 | op_shr(pstack); 440 | puts("Value after bit shifting stack contents to the right:"); 441 | print_object_value(pop(pstack)); 442 | 443 | object = new_object("false"); 444 | push(pstack, object); 445 | puts("\nValue after adding \"false\" to stack:"); 446 | print_object_value(pop(pstack)); 447 | 448 | object = new_object("true"); 449 | push(pstack, object); 450 | puts("\nValue after adding \"true\" to stack:"); 451 | print_object_value(pop(pstack)); 452 | 453 | puts("\nAdding 327.98 to stack..."); 454 | object = new_object("327.98"); 455 | push(pstack, object); 456 | op_neg(pstack); 457 | puts("Value after calling op_neg():"); 458 | print_object_value(pop(pstack)); 459 | 460 | puts("\nAdding -327.98 to stack..."); 461 | object = new_object("-327.98"); 462 | push(pstack, object); 463 | op_pos(pstack); 464 | puts("Value after calling op_pos():"); 465 | print_object_value(pop(pstack)); 466 | 467 | puts("\nTesting string object addition of: \"Greetings, \" + \"Concocter!\""); 468 | object = new_object("Concocter!"); 469 | push(pstack, object); 470 | object = new_object("Greetings, "); 471 | push(pstack, object); 472 | op_add(pstack); 473 | puts("Result:"); 474 | print_object_value(pop(pstack)); 475 | 476 | puts("\nTesting string object multiplication of: \"foo\" * 3"); 477 | object = new_object("foo"); 478 | push(pstack, object); 479 | object = new_object("3"); 480 | push(pstack, object); 481 | op_mul(pstack); 482 | puts("Result:"); 483 | print_object_value(pop(pstack)); 484 | 485 | puts("Executing NOP..."); 486 | OP_NOOP; 487 | 488 | free_store(); 489 | 490 | return 0; 491 | } 492 | -------------------------------------------------------------------------------- /src/concoct.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Concoct - An imperative, dynamically-typed, interpreted, general-purpose programming language 3 | * Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley 4 | * http://concoct.ist/ 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include // isspace() 29 | #include // errno 30 | #include // signal(), SIGINT 31 | #include // false, true 32 | #include // size_t 33 | #include // FILE, fclose(), fflush(), fgets(), fprintf(), printf(), puts(), stdin, stderr, stdout 34 | #include // exit(), EXIT_FAILURE, EXIT_SUCCESS 35 | #include // memcpy(), memset(), strcasecmp()/stricmp(), strcspn(), strerror(), strlen() 36 | #include "char_stream.h" 37 | #include "compiler.h" 38 | #include "concoct.h" 39 | #include "debug.h" 40 | #include "hash_map.h" 41 | #include "lexer.h" 42 | #ifndef _WIN32 43 | #include "linenoise.h" 44 | #endif // _WIN32 45 | #include "parser.h" 46 | #include "types.h" 47 | #include "version.h" // VERSION 48 | #include "vm/vm.h" 49 | 50 | int main(int argc, char** argv) 51 | { 52 | char *input_file = NULL; 53 | int nonopt_count = 0; 54 | 55 | if(argc > 1) 56 | { 57 | for(int i = 1; i < argc; i++) 58 | { 59 | // Check for command-line options 60 | if(argv[i][0] == ARG_PREFIX) 61 | handle_options(argc, argv); 62 | else 63 | { 64 | nonopt_count++; 65 | input_file = argv[i]; 66 | } 67 | } 68 | } 69 | 70 | print_version(); 71 | init_vm(); 72 | 73 | if(debug_mode) 74 | { 75 | debug_print("argc: %d", argc); 76 | for(int i = 0; i < argc; i++) 77 | debug_print("argv[%d]: %s", i, argv[i]); 78 | } 79 | 80 | if(nonopt_count > 1) 81 | { 82 | fprintf(stderr, "Ambiguous input!\n"); 83 | clean_exit(EXIT_FAILURE); 84 | } 85 | 86 | if(nonopt_count == 0) 87 | interactive_mode(); 88 | 89 | if(argc > 3) 90 | { 91 | fprintf(stderr, "Too many arguments!\n"); 92 | clean_exit(EXIT_FAILURE); 93 | } 94 | 95 | if(input_file) 96 | { 97 | lex_file(input_file); 98 | parse_file(input_file); 99 | } 100 | else 101 | { 102 | fprintf(stderr, "No input file!\n"); 103 | clean_exit(EXIT_FAILURE); 104 | } 105 | 106 | clean_exit(EXIT_SUCCESS); 107 | return 0; 108 | } 109 | 110 | // Exits gracefully 111 | void clean_exit(int status) 112 | { 113 | stop_vm(); 114 | if(status == EXIT_SUCCESS) 115 | exit(EXIT_SUCCESS); 116 | else 117 | exit(EXIT_FAILURE); 118 | return; 119 | } 120 | 121 | // Parses file 122 | void parse_file(const char* file_name) 123 | { 124 | FILE* input_file = fopen(file_name, "r"); 125 | if(input_file == NULL) 126 | { 127 | fprintf(stderr, "Error opening %s: %s\n", file_name, strerror(errno)); 128 | clean_exit(EXIT_FAILURE); 129 | } 130 | 131 | // Creates a new lexer and iterates through the tokens 132 | ConcoctCharStream* char_stream = cct_new_file_char_stream(input_file); 133 | ConcoctLexer* file_lexer = cct_new_lexer(char_stream); 134 | ConcoctParser* parser = cct_new_parser(file_lexer); 135 | ConcoctNodeTree* node_tree = cct_parse_program(parser); 136 | ConcoctHashMap* map = cct_new_hash_map(INITIAL_BUCKET_AMOUNT); 137 | if(parser->error == NULL) 138 | { 139 | if(debug_mode) 140 | cct_print_node(node_tree->root, 0); 141 | compile(node_tree, map); 142 | } 143 | else 144 | fprintf(stderr, "Parsing error: [%zu] %s, got %s\n", parser->error_line, parser->error, cct_token_type_to_string(parser->current_token.type)); 145 | fclose(input_file); 146 | cct_delete_parser(parser); 147 | cct_delete_char_stream(char_stream); 148 | cct_delete_node_tree(node_tree); 149 | cct_delete_hash_map(map); 150 | return; 151 | } 152 | 153 | // Parses string 154 | void parse_string(const char* input_string) 155 | { 156 | ConcoctCharStream* char_stream = cct_new_string_char_stream(input_string); 157 | ConcoctLexer* string_lexer = cct_new_lexer(char_stream); 158 | ConcoctParser* parser = cct_new_parser(string_lexer); 159 | ConcoctNodeTree* node_tree = cct_parse_program(parser); 160 | ConcoctHashMap* map = cct_new_hash_map(INITIAL_BUCKET_AMOUNT); 161 | if(parser->error == NULL) 162 | { 163 | if(debug_mode) 164 | cct_print_node(node_tree->root, 0); 165 | compile(node_tree, map); 166 | } 167 | else 168 | fprintf(stderr, "Parsing error: [%zu] %s, got %s\n", parser->error_line, parser->error, cct_token_type_to_string(parser->current_token.type)); 169 | cct_delete_parser(parser); 170 | if(debug_mode) 171 | debug_print("Freed parser."); 172 | cct_delete_node_tree(node_tree); 173 | if(debug_mode) 174 | debug_print("Freed node tree."); 175 | cct_delete_char_stream(char_stream); 176 | cct_delete_hash_map(map); 177 | return; 178 | } 179 | 180 | // Lexes file 181 | void lex_file(const char* file_name) 182 | { 183 | FILE* input_file = fopen(file_name, "r"); 184 | if(input_file == NULL) 185 | { 186 | fprintf(stderr, "Error opening %s: %s\n", file_name, strerror(errno)); 187 | clean_exit(EXIT_FAILURE); 188 | } 189 | 190 | // Creates a new lexer and iterates through the tokens 191 | ConcoctCharStream* char_stream = cct_new_file_char_stream(input_file); 192 | ConcoctLexer* file_lexer = cct_new_lexer(char_stream); 193 | if(file_lexer == NULL) 194 | { 195 | fprintf(stderr, "File lexer is NULL!\n"); 196 | fclose(input_file); 197 | cct_delete_char_stream(char_stream); 198 | return; 199 | } 200 | ConcoctToken token = cct_next_token(file_lexer); 201 | // Continues printing until an EOF is reached 202 | printf("Lexing %s:\n", file_name); 203 | while(token.type != CCT_TOKEN_EOF) 204 | { 205 | if(file_lexer->error != NULL) 206 | { 207 | fprintf(stderr, "Error on line %zu:\n", token.line_number); 208 | fprintf(stderr, "%s\n", file_lexer->error); 209 | break; 210 | } 211 | if(debug_mode) 212 | printf("[%zu] %s : %s\n", token.line_number, file_lexer->token_text, cct_token_type_to_string(token.type)); 213 | token = cct_next_token(file_lexer); 214 | } 215 | fclose(input_file); 216 | cct_delete_lexer(file_lexer); 217 | cct_delete_char_stream(char_stream); 218 | return; 219 | } 220 | 221 | // Lexes string 222 | void lex_string(const char* input_string) 223 | { 224 | // Lexer also can be created for strings 225 | //const char* input = "func test() { return a + b }"; 226 | ConcoctCharStream* char_stream = cct_new_string_char_stream(input_string); 227 | ConcoctLexer* string_lexer = cct_new_lexer(char_stream); 228 | 229 | if(string_lexer == NULL) 230 | { 231 | fprintf(stderr, "String lexer is NULL!\n"); 232 | return; 233 | } 234 | 235 | ConcoctToken token = cct_next_token(string_lexer); 236 | 237 | //printf("Lexing string...\n"); 238 | while(token.type != CCT_TOKEN_EOF) 239 | { 240 | if(string_lexer->error != NULL) 241 | { 242 | fprintf(stderr, "Error on line %zu:\n", token.line_number); 243 | fprintf(stderr, "%s\n", string_lexer->error); 244 | break; 245 | } 246 | if(debug_mode) 247 | printf("[%zu] %s : %s\n", token.line_number, string_lexer->token_text, cct_token_type_to_string(token.type)); 248 | token = cct_next_token(string_lexer); 249 | } 250 | cct_delete_lexer(string_lexer); 251 | cct_delete_char_stream(char_stream); 252 | return; 253 | } 254 | 255 | // Handle command-line options 256 | void handle_options(int argc, char *argv[]) 257 | { 258 | for(int i = 1; i < argc; i++) 259 | { 260 | if(argv[i][0] == ARG_PREFIX && strlen(argv[i]) == 2) 261 | { 262 | switch(argv[i][1]) 263 | { 264 | case 'd': 265 | debug_mode = true; 266 | break; 267 | case 'h': 268 | print_usage(); 269 | exit(EXIT_SUCCESS); 270 | break; 271 | case 'l': 272 | print_license(); 273 | exit(EXIT_SUCCESS); 274 | break; 275 | case 'v': 276 | print_version(); 277 | exit(EXIT_SUCCESS); 278 | break; 279 | default: 280 | fprintf(stderr, "Invalid option!\n"); 281 | print_usage(); 282 | exit(EXIT_FAILURE); 283 | break; 284 | } 285 | } 286 | if(argv[i][0] == ARG_PREFIX && strlen(argv[i]) != 2) 287 | { 288 | fprintf(stderr, "Invalid option!\n"); 289 | print_usage(); 290 | exit(EXIT_FAILURE); 291 | } 292 | } 293 | return; 294 | } 295 | 296 | // Displays license 297 | void print_license(void) 298 | { 299 | puts("BSD 2-Clause License\n"); 300 | puts("Copyright (c) 2020-2023 BlakeTheBlock and Lloyd Dilley"); 301 | puts("All rights reserved.\n"); 302 | puts("Redistribution and use in source and binary forms, with or without"); 303 | puts("modification, are permitted provided that the following conditions are met:\n"); 304 | puts("1. Redistributions of source code must retain the above copyright notice, this"); 305 | puts("list of conditions and the following disclaimer.\n"); 306 | puts("2. Redistributions in binary form must reproduce the above copyright notice,"); 307 | puts("this list of conditions and the following disclaimer in the documentation"); 308 | puts("and/or other materials provided with the distribution.\n"); 309 | 310 | puts("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\""); 311 | puts("AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE"); 312 | puts("IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE"); 313 | puts("DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE"); 314 | puts("FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL"); 315 | puts("DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR"); 316 | puts("SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER"); 317 | puts("CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,"); 318 | puts("OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE"); 319 | puts("OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."); 320 | return; 321 | } 322 | 323 | // Displays usage 324 | void print_usage(void) 325 | { 326 | print_version(); 327 | printf("Usage: concoct [%c