From f9996b4fb3d65bae92c8f09aa7c11adcb7330a03 Mon Sep 17 00:00:00 2001 From: Coredex Date: Sat, 8 Nov 2025 20:30:37 +0530 Subject: [PATCH 1/3] [Commit] Nubula-0.0.1-snapshot.1 - Cleared the project and started with rust implementation. - Builds and prints version. --- .github/workflows/build.yml | 154 +++++++ .github/workflows/gcc-compile.yml | 67 --- .gitignore | 19 +- CMakeLists.txt | 46 -- Cargo.toml | 24 ++ LICENSE | 674 ------------------------------ Makefile | 45 -- README.md | 197 +-------- build.bat | 100 +++-- build.ps1 | 68 +++ build.sh | 76 ++++ docs/language_reference.md | 502 ---------------------- examples/arithmetic.nvq | 20 - examples/constants.nvq | 21 - examples/control_flow.nvq | 26 -- examples/hello_world.nvq | 14 - examples/multiline_test.nvq | 40 -- include/arithmetic.h | 27 -- include/comparison.h | 27 -- include/config.h | 29 -- include/control_flow.h | 23 - include/display.h | 35 -- include/error.h | 47 --- include/expressions.h | 39 -- include/input.h | 22 - include/interpreter.h | 15 - include/logical.h | 27 -- include/types.h | 28 -- include/variables.h | 45 -- src/interpreter/expressions.c | 311 -------------- src/interpreter/interpreter.c | 273 ------------ src/interpreter/variables.c | 121 ------ src/main.c | 353 ---------------- src/main.rs | 24 ++ src/operators/arithmetic.c | 69 --- src/operators/comparison.c | 51 --- src/operators/logical.c | 50 --- src/statements/control_flow.c | 142 ------- src/statements/display.c | 95 ----- src/statements/input.c | 105 ----- src/utils/error.c | 52 --- tests/test.nvq | 77 ---- tests/test_string_comments.nvq | 17 - wiki.txt | 297 ------------- 44 files changed, 448 insertions(+), 4046 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/gcc-compile.yml delete mode 100644 CMakeLists.txt create mode 100644 Cargo.toml delete mode 100644 LICENSE delete mode 100644 Makefile create mode 100644 build.ps1 create mode 100755 build.sh delete mode 100644 docs/language_reference.md delete mode 100644 examples/arithmetic.nvq delete mode 100644 examples/constants.nvq delete mode 100644 examples/control_flow.nvq delete mode 100644 examples/hello_world.nvq delete mode 100644 examples/multiline_test.nvq delete mode 100644 include/arithmetic.h delete mode 100644 include/comparison.h delete mode 100644 include/config.h delete mode 100644 include/control_flow.h delete mode 100644 include/display.h delete mode 100644 include/error.h delete mode 100644 include/expressions.h delete mode 100644 include/input.h delete mode 100644 include/interpreter.h delete mode 100644 include/logical.h delete mode 100644 include/types.h delete mode 100644 include/variables.h delete mode 100644 src/interpreter/expressions.c delete mode 100644 src/interpreter/interpreter.c delete mode 100644 src/interpreter/variables.c delete mode 100644 src/main.c create mode 100644 src/main.rs delete mode 100644 src/operators/arithmetic.c delete mode 100644 src/operators/comparison.c delete mode 100644 src/operators/logical.c delete mode 100644 src/statements/control_flow.c delete mode 100644 src/statements/display.c delete mode 100644 src/statements/input.c delete mode 100644 src/utils/error.c delete mode 100644 tests/test.nvq delete mode 100644 tests/test_string_comments.nvq delete mode 100644 wiki.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..c03624d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,154 @@ +name: Build Noviq for All Platforms + +on: + push: + branches: [ main, Rust-reimpl ] + pull_request: + branches: [ main, Rust-reimpl ] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + name: Build on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + artifact_name: noviq + asset_extension: '' + - os: windows-latest + target: x86_64-pc-windows-msvc + artifact_name: noviq.exe + asset_extension: .exe + - os: macos-latest + target: x86_64-apple-darwin + artifact_name: noviq + asset_extension: '' + - os: macos-latest + target: aarch64-apple-darwin + artifact_name: noviq + asset_extension: '' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Cache cargo index + uses: actions/cache@v4 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-index- + + - name: Cache target directory + uses: actions/cache@v4 + with: + path: target + key: ${{ runner.os }}-${{ matrix.target }}-target-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.target }}-target- + + - name: Build snapshot (optimized) + run: | + cargo build --profile snapshot --target ${{ matrix.target }} + env: + SNAPSHOT: 1 + + - name: Get version + id: version + shell: bash + run: | + if [ "${{ runner.os }}" = "Windows" ]; then + VERSION=$(./target/${{ matrix.target }}/snapshot/noviq.exe | grep "Version:" | awk '{print $2}') + else + VERSION=$(./target/${{ matrix.target }}/snapshot/noviq | grep "Version:" | awk '{print $2}') + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Version: $VERSION" + + - name: Prepare artifact name + id: artifact + shell: bash + run: | + OS_NAME="${{ runner.os }}" + OS_LOWER=$(echo "$OS_NAME" | tr '[:upper:]' '[:lower:]') + TARGET="${{ matrix.target }}" + ARCH=$(echo "$TARGET" | cut -d'-' -f1) + VERSION="${{ steps.version.outputs.version }}" + ARTIFACT_NAME="noviq-${VERSION}-${OS_LOWER}-${ARCH}${{ matrix.asset_extension }}" + echo "name=$ARTIFACT_NAME" >> $GITHUB_OUTPUT + echo "Artifact name: $ARTIFACT_NAME" + + - name: Rename and prepare artifact + shell: bash + run: | + mkdir -p artifacts + cp target/${{ matrix.target }}/snapshot/${{ matrix.artifact_name }} artifacts/${{ steps.artifact.outputs.name }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.artifact.outputs.name }} + path: artifacts/${{ steps.artifact.outputs.name }} + if-no-files-found: error + + - name: Display artifact info + shell: bash + run: | + ls -lh artifacts/ + file artifacts/${{ steps.artifact.outputs.name }} || true + + release: + name: Create Release + needs: build + runs-on: ubuntu-latest + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/Rust-reimpl') + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Display downloaded artifacts + run: | + ls -R artifacts/ + + - name: Get current date + id: date + run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + tag_name: snapshot-${{ steps.date.outputs.date }}-${{ github.run_number }} + name: Snapshot Build ${{ steps.date.outputs.date }} + draft: false + prerelease: true + files: artifacts/**/* + body: | + Automated snapshot build from commit ${{ github.sha }} + + Built on: ${{ steps.date.outputs.date }} + Commit: ${{ github.event.head_commit.message }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/gcc-compile.yml b/.github/workflows/gcc-compile.yml deleted file mode 100644 index 6de5bad..0000000 --- a/.github/workflows/gcc-compile.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Build Noviq - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - build: - name: Build on ${{ matrix.os }}${{ matrix.arch != '' && format(' ({0})', matrix.arch) || '' }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - include: - - os: ubuntu-latest - - os: windows-latest - - os: macos-latest - arch: arm64 - - os: macos-latest - arch: x86_64 - - steps: - - uses: actions/checkout@v3 - - - name: Install GCC (Windows) - if: runner.os == 'Windows' - uses: egor-tensin/setup-mingw@v2.2.0 - with: - version: 12.2.0 - - - name: Install GCC (macOS) - if: runner.os == 'macOS' - run: | - brew install gcc - - - name: Build (Unix-like) - if: runner.os != 'Windows' - run: | - if [ "${{ runner.os }}" = "macOS" ]; then - CFLAGS="-arch ${{ matrix.arch }}" make all - else - make all - fi - - - name: Build (Windows) - if: runner.os == 'Windows' - run: | - ./build.bat - - - name: Upload artifact (Windows/Linux) - if: runner.os != 'macOS' - uses: actions/upload-artifact@v4 - with: - name: noviq-${{ runner.os }} - path: | - noviq* - if-no-files-found: error - - - name: Upload artifact (macOS) - if: runner.os == 'macOS' - uses: actions/upload-artifact@v4 - with: - name: noviq-${{ runner.os }}-${{ matrix.arch }} - path: | - noviq - if-no-files-found: error diff --git a/.gitignore b/.gitignore index 3d7ceb1..d07606e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,17 @@ +# Rust build artifacts +/target/ +Cargo.lock + +# Build outputs +/libs/ + +# IDE files +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# OS files .DS_Store -/.vscode -noviq -noviq.exe \ No newline at end of file +Thumbs.db diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 8f91411..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project(Noviq VERSION 2.3.0 LANGUAGES C) - -set(CMAKE_C_STANDARD 99) -set(CMAKE_C_STANDARD_REQUIRED ON) - -# Compiler warnings -if(MSVC) - add_compile_options(/W4 /WX-) -else() - add_compile_options(-Wall -Wextra -Wpedantic) -endif() - -# Include directories -include_directories(${CMAKE_SOURCE_DIR}/include) - -# Source files -set(SOURCES - src/main.c - src/interpreter/interpreter.c - src/interpreter/variables.c - src/interpreter/expressions.c - src/operators/arithmetic.c - src/operators/logical.c - src/operators/comparison.c - src/statements/display.c - src/statements/input.c - src/statements/control_flow.c - src/utils/error.c -) - -# Create executable -add_executable(noviq ${SOURCES}) - -# Link math library (required for pow() function) -if(UNIX) - target_link_libraries(noviq PRIVATE m) -endif() - -# Installation -install(TARGETS noviq DESTINATION bin) - -# Print build information -message(STATUS "Noviq Interpreter v${PROJECT_VERSION}") -message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") -message(STATUS "C Compiler: ${CMAKE_C_COMPILER}") diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c8faefb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "noviq" +version = "0.1.0" +edition = "2021" +authors = ["Noviq Contributors"] +description = "A simple, compiled programming language written in Rust" +license = "GPL-3.0" + +[dependencies] +chrono = "0.4" + +[[bin]] +name = "noviq" +path = "src/main.rs" + +[profile.release] +opt-level = 3 # Maximum optimization +lto = true # Link-time optimization +codegen-units = 1 # Better optimization, slower compile +panic = "abort" # Smaller binary, no unwinding +strip = true # Strip symbols from binary + +[profile.snapshot] +inherits = "release" # Inherits all optimizations from release diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f288702..0000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/Makefile b/Makefile deleted file mode 100644 index 130203d..0000000 --- a/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -# Noviq Interpreter Makefile - -CC = gcc -CFLAGS = -Wall -Wextra -Wpedantic -std=c99 -Iinclude -LDFLAGS = -lm -TARGET = noviq -SRCDIR = src -OBJDIR = obj - -# Source files -SOURCES = $(SRCDIR)/main.c \ - $(SRCDIR)/interpreter/interpreter.c \ - $(SRCDIR)/interpreter/variables.c \ - $(SRCDIR)/interpreter/expressions.c \ - $(SRCDIR)/operators/arithmetic.c \ - $(SRCDIR)/operators/logical.c \ - $(SRCDIR)/operators/comparison.c \ - $(SRCDIR)/statements/display.c \ - $(SRCDIR)/statements/input.c \ - $(SRCDIR)/statements/control_flow.c \ - $(SRCDIR)/utils/error.c - -# Object files -OBJECTS = $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) - -# Create object directory structure -$(shell mkdir -p $(OBJDIR)/interpreter $(OBJDIR)/operators $(OBJDIR)/statements $(OBJDIR)/utils) - -all: $(TARGET) - -$(TARGET): $(OBJECTS) - $(CC) $(OBJECTS) -o $(TARGET) $(LDFLAGS) - @echo "Build complete: $(TARGET)" - -$(OBJDIR)/%.o: $(SRCDIR)/%.c - @mkdir -p $(dir $@) - $(CC) $(CFLAGS) -c $< -o $@ - -clean: - rm -rf $(OBJDIR) $(TARGET) $(TARGET).exe - @echo "Clean complete" - -rebuild: clean all - -.PHONY: all clean rebuild \ No newline at end of file diff --git a/README.md b/README.md index a060f7e..3e466aa 100644 --- a/README.md +++ b/README.md @@ -11,188 +11,20 @@ Noviq- The name Noviq is make up of two words: Nova(which means new) and unique. - So, Nova + unique = Noviq. -**Noviq** = **Nova** (new) + **unique**- Noviq is just a simple interpreter based programming language written in C. +**Noviq** = **Nova** (new) + **unique**- Noviq is just a simple compiled programming language writtin in Rust which aims to be independent in the future, i.e, being writtin in Noviq itself similar to how rust does it. +A simple, compiled programming language written in Rust with a focus on clarity and ease of use. +A language written to have an easy syntax and high performance. +## Building -A simple, interpreted programming language written in C with a focus on clarity and ease of use.## Wiki - -[Click Here To Go To The Wiki](https://coredex-source.github.io/Noviq-site/wiki/introduction.html) - -## Features - -## Building and using - -✅ **Dynamic Typing** - Integers, floats, strings, and booleans ### Requirements: - -✅ **Arithmetic Operations** - `+`, `-`, `*`, `/`, `%`, `**` (power), `//` (integer division) - GCC (Only GCC on windows.) - -✅ **Logical Operations** - `AND`, `OR`, `NOT` (also `&&`, `||`, `!`) - make - -✅ **Comparison Operators** - `>`, `<`, `>=`, `<=`, `==` ### Build using: - -✅ **Control Flow** - `if`/`else` statements with nesting support - MacOS/Linux - -✅ **Constants** - Immutable values with `const` keyword ``` - -✅ **User Input** - Interactive programs with `input()` make all - -✅ **String Formatting** - Variable substitution with `%var1`, `%var2`, etc.``` - - -## Documentation``` - -gcc -o noviq.exe noviq.c lexer/lexer_interpret.c lexer/lexer_display.c -lm - -📚 [Language Wiki](https://coredex-source.github.io/Noviq-site/wiki/introduction.html) ``` - -📖 [Language Reference](docs/language_reference.md)### Run using: - - -## Project Structure -``` -├── src/ -│ ├── main.c # Entry pointnoviq.exe -e filename.nvq -│ ├── interpreter/ # Core interpreter -│ │ ├── interpreter.c # Command interpretation -│ │ ├── variables.c # Variable management -│ │ └── expressions.c # Expression evaluation -│ ├── operators/ # Operator implementations -│ │ ├── arithmetic.c # Arithmetic operations -│ │ ├── logical.c # Logical operations -│ │ └── comparison.c # Comparison operations -│ ├── statements/ # Statement handlers -│ │ ├── display.c # Output functionality -│ │ ├── input.c # Input handling -│ │ └── control_flow.c # If/else statements -│ └── utils/ -│ └── error.c # Error reporting -├── include/ # Header files -├── examples/ # Example programs -├── tests/ # Test files -├── docs/ # Documentation -├── CMakeLists.txt # CMake build configuration -├── Makefile # Make build configuration -└── README.md -``` - -## Quick Start - -### Requirements - -- **GCC** or compatible C compiler -- **Make** (optional, for Makefile build) -- **CMake** 3.10+ (optional, for CMake build) - -### Build Options - -#### Option 1: Using Make (Recommended for Unix-like systems) - -```bash -make all -``` - -#### Option 2: Using CMake (Cross-platform) - -```bash -mkdir build -cd build -cmake .. -cmake --build . -``` - -#### Option 3: Direct compilation (Windows) - -```cmd -gcc -o noviq.exe -Iinclude src\main.c src\interpreter\*.c src\operators\*.c src\statements\*.c src\utils\*.c -lm -``` - -### Run - -```bash -# Unix/Linux/macOS -./noviq -e examples/hello_world.nvq - -# Windows -noviq.exe -e examples\hello_world.nvq -``` - -### Command-Line Options - -``` -noviq [options] - -Options: - -e Execute a Noviq script file (.nvq) - --help Display help message - --version Display version information -``` - -## Examples - -### Hello World - -```noviq -display("Hello, World!") -``` - -### Variables and Arithmetic - -```noviq -x = 10 -y = 5 -result = x + y -display("Result: %var1", result) -``` - -### Control Flow - -```noviq -age = 20 -if(age >= 18){ - display("You are an adult") -} else { - display("You are a minor") -} -``` - -### Constants - -```noviq -const PI = 3.14159 -radius = 5 -area = PI * radius * radius -display("Area: %var1", area) -``` - -More examples available in the `examples/` directory. - -## Development - -### Building from Source - -1. Clone the repository -```bash -git clone https://github.com/coredex-source/Noviq.git -cd Noviq -``` - -2. Build using your preferred method -```bash -make all -``` - -3. Test the build ```bash -./noviq -e examples/hello_world.nvq +./build.sh # Debug build +./build.sh --release # Release build +./build.sh --snapshot # Optimized snapshot build ``` -### Clean Build - -```bash -make clean -make all -``` +Windows: Use `build.ps1` or `build.bat` instead. Binaries are output to `libs/` folder. ## Contributing @@ -202,10 +34,17 @@ Contributions are welcome! Please feel free to submit issues or pull requests. GPL-3.0 - See [LICENSE](LICENSE) for details. -## Version +## Version Scheme: + +- **Pre-alpha (Nebula)**: `nebula-x.x.x` +- **Alpha (Protostar)**: `protostar-x.x.x` +- **Beta (Nova)**: `nova-x.x.x` +- **Release (Supernova)**: `supernova-x.x.x` +- **Snapshots (Pulsar)**: `type-x.x.x-pulsar.YYMMDD` -Current version: **alpha-v1.0.0** +Current version: **nebula-0.0.0** +Snapshot/Github Actions Build version scheme: **nebula-0.0.1-pulsar.YYMMDD** --- -*Note: This project is in alpha development. Features may change, and bugs are expected.* +*Note: This project is in pre-alpha development. Features may change, and bugs are expected.* diff --git a/build.bat b/build.bat index 4d5c567..90314cc 100644 --- a/build.bat +++ b/build.bat @@ -1,33 +1,69 @@ @echo off -REM Build script for Noviq on Windows -REM Requires GCC (MinGW or similar) to be installed and in PATH - -echo Building Noviq... - -gcc -o noviq.exe -Iinclude ^ - src\main.c ^ - src\interpreter\interpreter.c ^ - src\interpreter\variables.c ^ - src\interpreter\expressions.c ^ - src\operators\arithmetic.c ^ - src\operators\logical.c ^ - src\operators\comparison.c ^ - src\statements\display.c ^ - src\statements\input.c ^ - src\statements\control_flow.c ^ - src\utils\error.c ^ - -lm - -if %ERRORLEVEL% EQU 0 ( - echo. - echo Build successful! Executable: noviq.exe - echo. - echo Usage: - echo noviq.exe -e ^ Execute a Noviq script - echo noviq.exe -h Show help - echo. -) else ( - echo. - echo Build failed with error code %ERRORLEVEL% - exit /b %ERRORLEVEL% -) +REM Unified build script for Noviq (Windows CMD) + +if not exist libs mkdir libs + +if "%1"=="--release" goto release +if "%1"=="--snapshot" goto snapshot +if "%1"=="" goto debug +goto usage + +:debug +echo Building Noviq (Debug)... +cargo build +if errorlevel 1 exit /b 1 + +for /f "tokens=2" %%i in ('target\debug\noviq.exe ^| findstr "Version:"') do set VERSION=%%i +set OUTPUT_NAME=noviq-%VERSION%-windows-x86_64.exe +copy target\debug\noviq.exe libs\%OUTPUT_NAME% >nul + +echo. +echo Debug build complete! +echo Binary copied to: libs\%OUTPUT_NAME% +echo. +target\debug\noviq.exe +goto end + +:release +echo Building Noviq (Release)... +cargo build --release +if errorlevel 1 exit /b 1 + +for /f "tokens=2" %%i in ('target\release\noviq.exe ^| findstr "Version:"') do set VERSION=%%i +set OUTPUT_NAME=noviq-%VERSION%-windows-x86_64.exe +copy target\release\noviq.exe libs\%OUTPUT_NAME% >nul + +echo. +echo Release build complete! +echo Binary copied to: libs\%OUTPUT_NAME% +echo. +target\release\noviq.exe +goto end + +:snapshot +echo Building Noviq (Snapshot)... +set SNAPSHOT=1 +cargo build --profile snapshot +if errorlevel 1 exit /b 1 + +for /f "tokens=2" %%i in ('target\snapshot\noviq.exe ^| findstr "Version:"') do set VERSION=%%i +set OUTPUT_NAME=noviq-%VERSION%-windows-x86_64.exe +copy target\snapshot\noviq.exe libs\%OUTPUT_NAME% >nul + +echo. +echo Snapshot build complete! +echo Binary copied to: libs\%OUTPUT_NAME% +echo. +target\snapshot\noviq.exe +goto end + +:usage +echo Usage: build.bat [--release^|--snapshot] +echo. +echo Options: +echo (no flag) Build in debug mode with snapshot version +echo --release Build optimized release with clean version +echo --snapshot Build optimized snapshot with dated version +exit /b 1 + +:end diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..2366a8f --- /dev/null +++ b/build.ps1 @@ -0,0 +1,68 @@ +# Unified build script for Noviq (Windows) +param( + [Parameter(Position=0)] + [string]$BuildType +) + +# Create libs directory if it doesn't exist +New-Item -ItemType Directory -Force -Path libs | Out-Null + +function Build-Noviq { + param($Type, $Profile, $EnvVars = @{}) + + Write-Host "Building Noviq ($Type)..." -ForegroundColor Green + + # Set environment variables if any + foreach ($key in $EnvVars.Keys) { + $env:($key) = $EnvVars[$key] + } + + # Build + if ($Profile -eq "dev") { + cargo build + $BinaryPath = "target\debug\noviq.exe" + } else { + cargo build --profile $Profile + $BinaryPath = "target\$Profile\noviq.exe" + } + + # Get version + $VersionOutput = & $BinaryPath + $Version = ($VersionOutput | Select-String "Version:").ToString().Split()[1] + + # Determine architecture + $Arch = if ([Environment]::Is64BitOperatingSystem) { "x86_64" } else { "x86" } + $OutputName = "noviq-$Version-windows-$Arch.exe" + + # Copy to libs + Copy-Item $BinaryPath "libs\$OutputName" -Force + + Write-Host "" + Write-Host "$Type build complete!" -ForegroundColor Green + Write-Host "Binary copied to: libs\$OutputName" -ForegroundColor Cyan + Write-Host "" + + # Run the binary + & $BinaryPath +} + +switch ($BuildType) { + "--release" { + Build-Noviq -Type "Release" -Profile "release" + } + "--snapshot" { + Build-Noviq -Type "Snapshot" -Profile "snapshot" -EnvVars @{SNAPSHOT="1"} + } + "" { + Build-Noviq -Type "Debug" -Profile "dev" + } + default { + Write-Host "Usage: .\build.ps1 [--release|--snapshot]" + Write-Host "" + Write-Host "Options:" + Write-Host " (no flag) Build in debug mode with snapshot version" + Write-Host " --release Build optimized release with clean version" + Write-Host " --snapshot Build optimized snapshot with dated version" + exit 1 + } +} diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..5aa4bc3 --- /dev/null +++ b/build.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# Unified build script for Noviq + +set -e + +# Create libs directory if it doesn't exist +mkdir -p libs + +# Detect OS and set extension +EXT="" +if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then + EXT=".exe" +fi + +case "$1" in + --release) + echo "Building Noviq (Release)..." + cargo build --release + + # Get version for filename + VERSION=$(./target/release/noviq${EXT} | grep "Version:" | awk '{print $2}') + OUTPUT_NAME="noviq-${VERSION}-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)${EXT}" + + # Copy to libs + cp "target/release/noviq${EXT}" "libs/${OUTPUT_NAME}" + + echo "" + echo "Release build complete!" + echo "Binary copied to: libs/${OUTPUT_NAME}" + echo "" + ./target/release/noviq${EXT} + ;; + --snapshot) + echo "Building Noviq (Snapshot)..." + env SNAPSHOT=1 cargo build --profile snapshot + + # Get version for filename + VERSION=$(./target/snapshot/noviq${EXT} | grep "Version:" | awk '{print $2}') + OUTPUT_NAME="noviq-${VERSION}-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)${EXT}" + + # Copy to libs + cp "target/snapshot/noviq${EXT}" "libs/${OUTPUT_NAME}" + + echo "" + echo "Snapshot build complete!" + echo "Binary copied to: libs/${OUTPUT_NAME}" + echo "" + ./target/snapshot/noviq${EXT} + ;; + "") + echo "Building Noviq (Debug)..." + cargo build + + # Get version for filename + VERSION=$(./target/debug/noviq${EXT} | grep "Version:" | awk '{print $2}') + OUTPUT_NAME="noviq-${VERSION}-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)${EXT}" + + # Copy to libs + cp "target/debug/noviq${EXT}" "libs/${OUTPUT_NAME}" + + echo "" + echo "Debug build complete!" + echo "Binary copied to: libs/${OUTPUT_NAME}" + echo "" + ./target/debug/noviq${EXT} + ;; + *) + echo "Usage: ./build.sh [--release|--snapshot]" + echo "" + echo "Options:" + echo " (no flag) Build in debug mode with snapshot version" + echo " --release Build optimized release with clean version" + echo " --snapshot Build optimized snapshot with dated version" + exit 1 + ;; +esac diff --git a/docs/language_reference.md b/docs/language_reference.md deleted file mode 100644 index f3a43df..0000000 --- a/docs/language_reference.md +++ /dev/null @@ -1,502 +0,0 @@ -# Noviq Language Reference - -**Version:** 2.3.0 (Pre-Alpha) - -## Table of Contents - -1. [Introduction](#introduction) -2. [Data Types](#data-types) -3. [Variables](#variables) -4. [Constants](#constants) -5. [Operators](#operators) -6. [Control Flow](#control-flow) -7. [Input/Output](#inputoutput) -8. [Comments](#comments) -9. [Error Handling](#error-handling) - -## Introduction - -Noviq is a dynamically-typed interpreted programming language with a simple syntax designed for clarity and ease of use. Files use the `.nvq` extension. - -### Basic Syntax Rules - -- Statements are executed line by line -- No semicolons required -- Case-sensitive identifiers -- Whitespace is generally ignored (except in strings) - -## Data Types - -Noviq supports four primary data types: - -### Integer (`INT`) - -Whole numbers without decimal points. - -```noviq -x = 42 -y = -15 -z = 0 -``` - -### Float (`FLOAT`) - -Numbers with decimal points. - -```noviq -pi = 3.14159 -temperature = -273.15 -ratio = 0.5 -``` - -### String (`STRING`) - -Text enclosed in double quotes `"` or single quotes `'`. - -```noviq -name = "Alice" -message = 'Hello, World!' -empty = "" -``` - -### Boolean (`BOOLEAN`) - -Logical true/false values. - -```noviq -is_active = true -is_complete = false -``` - -## Variables - -Variables are dynamically typed and don't require declaration. - -### Variable Assignment - -```noviq -x = 10 # Integer -name = "John" # String -score = 95.5 # Float -active = true # Boolean -``` - -### Variable Names - -- Must start with a letter -- Can contain letters, numbers, and underscores -- Case-sensitive (`myVar` ≠ `myvar`) -- Cannot be keywords - -**Valid names:** `x`, `user_name`, `score2`, `_temp` -**Invalid names:** `2x`, `my-var`, `for`, `if` - -## Constants - -Constants are immutable variables declared with the `const` keyword. - -```noviq -const PI = 3.14159 -const MAX_USERS = 100 -const APP_NAME = "Noviq" -``` - -**Note:** Attempting to modify a constant will result in an error. - -```noviq -const X = 10 -X = 20 # Error: Cannot modify constant 'X' -``` - -## Operators - -### Arithmetic Operators - -| Operator | Description | Example | Result | -|----------|-------------|---------|--------| -| `+` | Addition | `5 + 3` | `8` | -| `-` | Subtraction | `5 - 3` | `2` | -| `*` | Multiplication | `5 * 3` | `15` | -| `/` | Division | `5 / 2` | `2.5` | -| `%` | Modulo | `5 % 2` | `1` | -| `**` | Exponentiation | `2 ** 3` | `8` | -| `//` | Integer Division | `5 // 2` | `2` | - -**Examples:** - -```noviq -x = 10 + 5 # 15 -y = 20 - 8 # 12 -z = 4 * 7 # 28 -result = 15 / 4 # 3.75 -remainder = 17 % 5 # 2 -power = 2 ** 8 # 256 -int_div = 17 // 5 # 3 -``` - -### Comparison Operators - -| Operator | Description | Example | Result | -|----------|-------------|---------|--------| -| `>` | Greater than | `5 > 3` | `true` | -| `<` | Less than | `5 < 3` | `false` | -| `>=` | Greater or equal | `5 >= 5` | `true` | -| `<=` | Less or equal | `5 <= 3` | `false` | -| `==` | Equal | `5 == 5` | `true` | - -**Examples:** - -```noviq -x = 10 -y = 20 -result1 = x > y # false -result2 = x < y # true -result3 = x >= 10 # true -result4 = y <= 20 # true -result5 = x == y # false -``` - -### Logical Operators - -| Operator | Description | Example | -|----------|-------------|---------| -| `AND` or `&&` | Logical AND | `true AND false` → `false` | -| `OR` or `||` | Logical OR | `true OR false` → `true` | -| `NOT` or `!` | Logical NOT | `NOT true` → `false` | - -**Examples:** - -```noviq -a = true -b = false - -result1 = a AND b # false -result2 = a OR b # true -result3 = NOT a # false -result4 = a && b # false (alternative syntax) -result5 = a || b # true (alternative syntax) -result6 = !a # false (alternative syntax) -``` - -### Operator Precedence - -1. `**` (Exponentiation) -2. `*`, `/`, `%`, `//` (Multiplication, Division, Modulo) -3. `+`, `-` (Addition, Subtraction) -4. `>`, `<`, `>=`, `<=`, `==` (Comparison) -5. `NOT`, `!` (Logical NOT) -6. `AND`, `&&` (Logical AND) -7. `OR`, `||` (Logical OR) - -## Control Flow - -### If Statements - -Execute code conditionally based on a boolean expression. - -**Syntax:** - -```noviq -if(condition){ - # code to execute if condition is true -} -``` - -**Example:** - -```noviq -x = 10 -if(x > 5){ - display("x is greater than 5") -} -``` - -### If-Else Statements - -Provide an alternative branch when the condition is false. - -**Syntax:** - -```noviq -if(condition){ - # code if condition is true -} else { - # code if condition is false -} -``` - -**Example:** - -```noviq -age = 18 -if(age >= 18){ - display("You are an adult") -} else { - display("You are a minor") -} -``` - -### Nested If Statements - -If statements can be nested within each other. - -**Example:** - -```noviq -x = 10 -y = 5 - -if(x > 5){ - if(y < 10){ - display("Both conditions are true") - } else { - display("Only first condition is true") - } -} else { - display("First condition is false") -} -``` - -## Input/Output - -### Display Output - -The `display()` function outputs text to the console. - -**Simple display:** - -```noviq -display("Hello, World!") -``` - -**Display variables:** - -```noviq -name = "Alice" -age = 25 -display("Name: %var1, Age: %var2", name, age) -``` - -**Variable placeholders:** -- `%var1` - First variable -- `%var2` - Second variable -- `%var3` - Third variable -- etc. - -**Display expressions:** - -```noviq -x = 10 -y = 5 -display("Sum: %var1", x + y) -display("Product: %var1", x * y) -``` - -### Input - -The `input()` function reads user input. - -**Single input:** - -```noviq -input("Enter your name: ", name) -display("Hello, %var1!", name) -``` - -**Multiple inputs with one prompt:** - -```noviq -input("Enter two numbers: ", x, y) -display("You entered: %var1 and %var2", x, y) -``` - -**Multiple inputs with separate prompts:** - -```noviq -input("Enter x: ", x, "Enter y: ", y) -display("x = %var1, y = %var2", x, y) -``` - -**Type inference:** - -The `input()` function automatically detects the type: -- Integers: `42`, `-15` -- Floats: `3.14`, `-0.5` -- Booleans: `true`, `false` -- Strings: Everything else - -## Comments - -### Single-Line Comments - -Use `#` for single-line comments. - -```noviq -# This is a comment -x = 10 # This is also a comment -``` - -### Multi-Line Comments - -Use `##` to start and end multi-line comments. - -```noviq -## -This is a multi-line comment -It can span multiple lines -## - -x = 5 -``` - -## Error Handling - -Noviq provides detailed error messages with line numbers and error types. - -### Error Types - -1. **Syntax Error** - Invalid syntax - ``` - Syntax Error on line 5: Missing closing parenthesis - ``` - -2. **Type Error** - Invalid type operation - ``` - Type Error on line 10: Cannot perform arithmetic operations with strings - ``` - -3. **Undefined Variable Error** - Variable not found - ``` - Undefined Variable Error on line 15: Variable 'x' not found - ``` - -4. **Constant Modification Error** - Attempting to modify a constant - ``` - Constant Modification Error on line 20: Cannot modify constant 'PI' - ``` - -5. **Division by Zero Error** - ``` - Division by Zero Error on line 25: Division by zero - ``` - -## Best Practices - -### 1. Use Meaningful Variable Names - -```noviq -# Good -user_age = 25 -total_score = 100 - -# Avoid -x = 25 -t = 100 -``` - -### 2. Use Constants for Fixed Values - -```noviq -const MAX_ATTEMPTS = 3 -const PI = 3.14159 -``` - -### 3. Add Comments - -```noviq -# Calculate circle area -const PI = 3.14159 -radius = 5 -area = PI * radius * radius -``` - -### 4. Format Output Clearly - -```noviq -name = "Alice" -score = 95 - -display("Student Report") -display("Name: %var1", name) -display("Score: %var1", score) -``` - -## Limitations (Pre-Alpha) - -Current limitations of Noviq: - -- No loops (`for`, `while`) -- No functions/procedures -- No arrays or lists -- No file I/O operations -- No string manipulation functions -- No import/module system -- Maximum variable limit: 1000 -- Maximum if-nesting: 32 levels -- Maximum line length: 4096 characters - -These features are planned for future releases. - -## Example Programs - -### Calculator - -```noviq -input("Enter first number: ", a) -input("Enter second number: ", b) - -display("Addition: %var1", a + b) -display("Subtraction: %var1", a - b) -display("Multiplication: %var1", a * b) -display("Division: %var1", a / b) -``` - -### Grade Calculator - -```noviq -input("Enter your score: ", score) - -if(score >= 90){ - display("Grade: A") -} else { - if(score >= 80){ - display("Grade: B") - } else { - if(score >= 70){ - display("Grade: C") - } else { - if(score >= 60){ - display("Grade: D") - } else { - display("Grade: F") - } - } - } -} -``` - -### Circle Calculator - -```noviq -const PI = 3.14159 - -input("Enter radius: ", radius) - -area = PI * radius * radius -circumference = 2 * PI * radius - -display("Circle Properties:") -display("Radius: %var1", radius) -display("Area: %var1", area) -display("Circumference: %var1", circumference) -``` - -## Further Resources - -- [Noviq Wiki](https://coredex-source.github.io/Noviq-site/wiki/introduction.html) -- [GitHub Repository](https://github.com/coredex-source/Noviq) -- [Example Programs](../examples/) - ---- - -*Last updated: October 2025* diff --git a/examples/arithmetic.nvq b/examples/arithmetic.nvq deleted file mode 100644 index f1ba9c2..0000000 --- a/examples/arithmetic.nvq +++ /dev/null @@ -1,20 +0,0 @@ -# Arithmetic Operations Example - -# Basic arithmetic -x = 10 -y = 3 - -display("Addition: %var1", x + y) -display("Subtraction: %var1", x - y) -display("Multiplication: %var1", x * y) -display("Division: %var1", x / y) -display("Modulo: %var1", x % y) -display("Power: %var1", x ** y) -display("Integer Division: %var1", x // y) - -# Using float numbers -a = 15.5 -b = 2.5 - -display("Float addition: %var1", a + b) -display("Float multiplication: %var1", a * b) diff --git a/examples/constants.nvq b/examples/constants.nvq deleted file mode 100644 index 7c05b49..0000000 --- a/examples/constants.nvq +++ /dev/null @@ -1,21 +0,0 @@ -# Constants Example - -# Define constants that cannot be modified -const PI = 3.14159 -const APP_NAME = "Noviq" -const MAX_USERS = 100 -const IS_PRODUCTION = true - -display("Constants:") -display("PI = %var1", PI) -display("App Name = %var1", APP_NAME) -display("Max Users = %var1", MAX_USERS) -display("Production = %var1", IS_PRODUCTION) - -# Calculate circle area -radius = 5 -area = PI * radius * radius -display("Circle area with radius %var1: %var2", radius, area) - -# This would cause an error (uncommenterror to test): -## PI = 3.14 # Error: Cannot modify constant diff --git a/examples/control_flow.nvq b/examples/control_flow.nvq deleted file mode 100644 index 18c1ca4..0000000 --- a/examples/control_flow.nvq +++ /dev/null @@ -1,26 +0,0 @@ -# Control Flow Example -# Simple if statement -x = 5 -if(x > 3){ display("x is greater than 3") } - -# If-else statement -age = 18 -if(age >= 18){ display("You are an adult") } else { display("You are a minor") } - -# Nested if statements -score = 85 -if(score >= 90){ display("Grade: A") } else { if(score >= 80){ display("Grade: B") } else { if(score >= 70){ display("Grade: C") } else { display("Grade: F") } } } - -# Comparison operators -a = 10 -b = 20 -c1 = a > b -c2 = a < b -c3 = a >= 10 -c4 = b <= 20 -c5 = a == 10 -display("a > b: %var1", c1) -display("a < b: %var1", c2) -display("a >= 10: %var1", c3) -display("b <= 20: %var1", c4) -display("a == 10: %var1", c5) diff --git a/examples/hello_world.nvq b/examples/hello_world.nvq deleted file mode 100644 index 9971f26..0000000 --- a/examples/hello_world.nvq +++ /dev/null @@ -1,14 +0,0 @@ -# Hello World Example -# This is the simplest Noviq program - -display("Hello, World!") -display("Welcome to Noviq programming language!") - -# Variables -name = "Noviq" -version = 2.3 -year = 2025 - -display("Language: %var1", name) -display("Version: %var1", version) -display("Year: %var1", year) diff --git a/examples/multiline_test.nvq b/examples/multiline_test.nvq deleted file mode 100644 index fb18bca..0000000 --- a/examples/multiline_test.nvq +++ /dev/null @@ -1,40 +0,0 @@ -# Multi-line Statement Test -# This file demonstrates that Noviq now supports multi-line if statements - -x = 10 - -# Single-line if statement (still works) -if(x > 5){display("x is greater than 5")} - -# Multi-line if statement with one line of code -if(x > 5){ - display("x is still greater than 5") -} - -# Multi-line if statement with multiple lines -if(x == 10){ - display("x equals 10") - display("This is the second line") - display("And this is the third line") -} - -# Multi-line if-else (else must be on same line as closing brace) -age = 25 -if(age >= 18){ - display("You are an adult") - display("You can vote") -} else { - display("You are a minor") - display("Wait a few more years") -} - -# Simple nested if -score = 85 -if(score >= 80){ - display("Grade: B or better") - if(score >= 90){ - display("Actually, it's an A!") - } -} - -display("All tests passed!") diff --git a/include/arithmetic.h b/include/arithmetic.h deleted file mode 100644 index d9e860f..0000000 --- a/include/arithmetic.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef ARITHMETIC_H -#define ARITHMETIC_H - -#include "types.h" - -/** - * @file arithmetic.h - * @brief Arithmetic operations for Noviq interpreter - */ - -/** - * @brief Perform an arithmetic operation - * @param left Left operand - * @param right Right operand - * @param operator The arithmetic operator (+, -, *, /, %, **, //) - * @return Result of the operation - */ -Variable performArithmeticOperation(Variable *left, Variable *right, const char *operator); - -/** - * @brief Check if a character is an operator - * @param c The character to check - * @return 1 if it's an operator, 0 otherwise - */ -int isOperator(char c); - -#endif // ARITHMETIC_H diff --git a/include/comparison.h b/include/comparison.h deleted file mode 100644 index 8d37117..0000000 --- a/include/comparison.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef COMPARISON_H -#define COMPARISON_H - -#include "types.h" - -/** - * @file comparison.h - * @brief Comparison operations for Noviq interpreter - */ - -/** - * @brief Perform a comparison operation - * @param left Left operand - * @param right Right operand - * @param operator The comparison operator (>, <, >=, <=, ==) - * @return Result of the comparison - */ -Variable performComparison(Variable *left, Variable *right, const char *operator); - -/** - * @brief Check if a string is a comparison operator - * @param str The string to check - * @return 1 if it's a comparison operator, 0 otherwise - */ -int isComparisonOperator(const char *str); - -#endif // COMPARISON_H diff --git a/include/config.h b/include/config.h deleted file mode 100644 index df7e351..0000000 --- a/include/config.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H - -/** - * @file config.h - * @brief Configuration constants for Noviq interpreter - */ - -// Version information -#define NOVIQ_VERSION "1.0.0" -#define NOVIQ_VERSION_STAGE "alpha" -#define NOVIQ_VERSION_FULL NOVIQ_VERSION_STAGE "-v" NOVIQ_VERSION - -// Interpreter limits -#define NOVIQ_MAX_LINE_LENGTH 4096 -#define NOVIQ_MAX_VARIABLES 1000 -#define NOVIQ_MAX_IF_NESTING 32 -#define NOVIQ_MAX_VAR_NAME_LENGTH 256 -#define NOVIQ_MAX_STRING_LENGTH 1024 - -// File extension -#define NOVIQ_FILE_EXTENSION ".nvq" - -// Buffer sizes -#define BUFFER_SIZE_SMALL 128 -#define BUFFER_SIZE_MEDIUM 512 -#define BUFFER_SIZE_LARGE 1024 - -#endif // CONFIG_H diff --git a/include/control_flow.h b/include/control_flow.h deleted file mode 100644 index 002b91a..0000000 --- a/include/control_flow.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef CONTROL_FLOW_H -#define CONTROL_FLOW_H - -/** - * @file control_flow.h - * @brief Control flow statements for Noviq interpreter - */ - -/** - * @brief Evaluate a condition and return boolean result - * @param condition The condition string to evaluate - * @return 1 if condition is true, 0 if false - */ -int evaluateCondition(const char *condition); - -/** - * @brief Execute an if/else block - * @param condition The condition to evaluate - * @param block The complete block containing if and optional else branches - */ -void executeIfBlock(const char *condition, const char *block); - -#endif // CONTROL_FLOW_H diff --git a/include/display.h b/include/display.h deleted file mode 100644 index 968469b..0000000 --- a/include/display.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef DISPLAY_H -#define DISPLAY_H - -/** - * @file display.h - * @brief Display/output functions for Noviq interpreter - */ - -/** - * @brief Display a string - * @param text The text to display - */ -void display(const char *text); - -/** - * @brief Display an integer - * @param value The integer to display - */ -void displayInt(int value); - -/** - * @brief Display a float - * @param value The float to display - */ -void displayFloat(float value); - -/** - * @brief Display formatted text with variable substitution - * @param format The format string containing %var1, %var2, etc. - * @param vars Array of variable names/expressions to substitute - * @param varCount Number of variables - */ -void displayFormatted(const char *format, char **vars, int varCount); - -#endif // DISPLAY_H diff --git a/include/error.h b/include/error.h deleted file mode 100644 index cae35c3..0000000 --- a/include/error.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef ERROR_H -#define ERROR_H - -/** - * @file error.h - * @brief Error handling system for Noviq interpreter - */ - -typedef enum { - ERROR_SYNTAX, - ERROR_RUNTIME, - ERROR_TYPE, - ERROR_UNDEFINED_VAR, - ERROR_CONST_MODIFY, - ERROR_DIV_BY_ZERO, - ERROR_FILE, - ERROR_MEMORY -} ErrorType; - -/** - * @brief Report an error and exit the program - * @param type The type of error - * @param lineNumber The line number where the error occurred (0 if not applicable) - * @param format The error message format string - * @param ... Variable arguments for the format string - */ -void reportError(ErrorType type, int lineNumber, const char *format, ...); - -/** - * @brief Set the current file context for error reporting - * @param filename The name of the file being processed - */ -void setErrorFile(const char *filename); - -/** - * @brief Get the current line number - * @return The current line number - */ -int getCurrentLine(void); - -/** - * @brief Set the current line number - * @param lineNumber The line number to set - */ -void setCurrentLine(int lineNumber); - -#endif // ERROR_H diff --git a/include/expressions.h b/include/expressions.h deleted file mode 100644 index 73fc5e4..0000000 --- a/include/expressions.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef EXPRESSIONS_H -#define EXPRESSIONS_H - -#include "types.h" - -/** - * @file expressions.h - * @brief Expression evaluation for Noviq interpreter - */ - -/** - * @brief Evaluate an expression - * @param expr The expression string to evaluate - * @return Pointer to the result variable (must be freed by caller) - */ -Variable *evaluateExpression(const char *expr); - -/** - * @brief Check if a string is a float - * @param str The string to check - * @return 1 if it's a float, 0 otherwise - */ -int isFloat(const char *str); - -/** - * @brief Parse a string as a float - * @param str The string to parse - * @return The float value - */ -float parseFloat(const char *str); - -/** - * @brief Check if a string is an expression (contains operators) - * @param str The string to check - * @return 1 if it's an expression, 0 otherwise - */ -int isExpression(const char *str); - -#endif // EXPRESSIONS_H diff --git a/include/input.h b/include/input.h deleted file mode 100644 index 6fa0141..0000000 --- a/include/input.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef INPUT_H -#define INPUT_H - -/** - * @file input.h - * @brief Input handling functions for Noviq interpreter - */ - -/** - * @brief Process user input and store in a variable - * @param prompt The prompt to display to the user - * @param varName The name of the variable to store the input - */ -void processInput(const char *prompt, const char *varName); - -/** - * @brief Handle the input command with multiple variables and prompts - * @param args The arguments string from the input() command - */ -void handleInputCommand(const char *args); - -#endif // INPUT_H diff --git a/include/interpreter.h b/include/interpreter.h deleted file mode 100644 index d1184fc..0000000 --- a/include/interpreter.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef INTERPRETER_H -#define INTERPRETER_H - -/** - * @file interpreter.h - * @brief Main interpreter for Noviq commands - */ - -/** - * @brief Interpret and execute a command - * @param command The command string to execute - */ -void interpretCommand(const char *command); - -#endif // INTERPRETER_H diff --git a/include/logical.h b/include/logical.h deleted file mode 100644 index 2bb5f53..0000000 --- a/include/logical.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef LOGICAL_H -#define LOGICAL_H - -#include "types.h" - -/** - * @file logical.h - * @brief Logical operations for Noviq interpreter - */ - -/** - * @brief Perform a logical operation - * @param left Left operand (NULL for NOT operation) - * @param right Right operand - * @param operator The logical operator (AND, OR, NOT, &&, ||, !) - * @return Result of the operation - */ -Variable performLogicalOperation(Variable *left, Variable *right, const char *operator); - -/** - * @brief Check if a string is a logical operator - * @param str The string to check - * @return 1 if it's a logical operator, 0 otherwise - */ -int isLogicalOperator(const char *str); - -#endif // LOGICAL_H diff --git a/include/types.h b/include/types.h deleted file mode 100644 index 2d5dfea..0000000 --- a/include/types.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TYPES_H -#define TYPES_H - -/** - * @file types.h - * @brief Type definitions for Noviq interpreter - */ - -typedef enum { - INT, - STRING, - FLOAT, - BOOLEAN -} VarType; - -typedef struct { - char *name; - VarType type; - int isConstant; - union { - int intValue; - char *stringValue; - float floatValue; - int boolValue; - } value; -} Variable; - -#endif // TYPES_H diff --git a/include/variables.h b/include/variables.h deleted file mode 100644 index 6bb8120..0000000 --- a/include/variables.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef VARIABLES_H -#define VARIABLES_H - -#include -#include "types.h" - -/** - * @file variables.h - * @brief Variable management for Noviq interpreter - */ - -/** - * @brief Find a variable by name - * @param name The name of the variable - * @return Pointer to the variable if found, NULL otherwise - */ -Variable *findVariable(const char *name); - -/** - * @brief Add or update a variable - * @param name The name of the variable - * @param type The type of the variable - * @param value Pointer to the value - * @param isConstant Whether the variable is a constant - */ -void addVariable(const char *name, VarType type, void *value, int isConstant); - -/** - * @brief Free all allocated variables - */ -void freeAllVariables(void); - -/** - * @brief Free a single variable - * @param var The variable to free - */ -void freeVariable(Variable *var); - -/** - * @brief Get the count of variables - * @return The number of variables - */ -size_t getVariableCount(void); - -#endif // VARIABLES_H diff --git a/src/interpreter/expressions.c b/src/interpreter/expressions.c deleted file mode 100644 index b77e929..0000000 --- a/src/interpreter/expressions.c +++ /dev/null @@ -1,311 +0,0 @@ -#include "expressions.h" -#include "variables.h" -#include "arithmetic.h" -#include "logical.h" -#include "comparison.h" -#include "error.h" -#include -#include -#include -#include - -int isFloat(const char *str) { - int dots = 0; - int i = 0; - - // Handle negative numbers - if (str[0] == '-') i = 1; - - for (; str[i] != '\0'; i++) { - if (str[i] == '.') { - dots++; - } else if (!isdigit(str[i])) { - return 0; - } - } - return dots == 1; -} - -float parseFloat(const char *str) { - return atof(str); -} - -int isExpression(const char *str) { - // Skip leading whitespace - while (*str == ' ' || *str == '\t') str++; - - // Check if it's just a negative number - if (*str == '-' && isdigit(*(str + 1))) { - str++; // Skip the minus - while (isdigit(*str) || *str == '.') str++; // Skip the number - // If we've reached the end, it's just a negative number - return *str != '\0'; - } - - for (int i = 0; str[i]; i++) { - // Check for two-character operators - if (str[i] == '*' && str[i+1] == '*') return 1; // ** - if (str[i] == '/' && str[i+1] == '/') return 1; // // - // Check for single character operators except leading minus - if (i > 0 && strchr("+-*/%", str[i])) return 1; - // Check for operator at start only if not a negative number - if (i == 0 && strchr("+*/%", str[i])) return 1; - } - return 0; -} - -Variable *evaluateExpression(const char *expr) { - char *trimmed = strdup(expr); - char *ptr = trimmed; - - while (*ptr == ' ' || *ptr == '\t') ptr++; - - // Check if it's just a negative number - if (ptr[0] == '-' && isdigit(ptr[1]) && !strchr(ptr + 1, '+') && - !strchr(ptr + 1, '-') && !strchr(ptr + 1, '*') && !strchr(ptr + 1, '/')) { - Variable *result = malloc(sizeof(Variable)); - if (isFloat(ptr)) { - result->type = FLOAT; - result->value.floatValue = parseFloat(ptr); - } else { - result->type = INT; - result->value.intValue = atoi(ptr); - } - result->name = NULL; - result->isConstant = 0; - free(trimmed); - return result; - } - - // First check for comparison operators - char *gtOp = strstr(ptr, ">="); - char *ltOp = strstr(ptr, "<="); - char *eqOp = strstr(ptr, "=="); - char *gt = strstr(ptr, ">"); - char *lt = strstr(ptr, "<"); - - char *op = NULL; - const char *opStr = NULL; - int opLen = 1; - - // Find the leftmost operator - if (gtOp && (!op || gtOp < op)) { op = gtOp; opStr = ">="; opLen = 2; } - if (ltOp && (!op || ltOp < op)) { op = ltOp; opStr = "<="; opLen = 2; } - if (eqOp && (!op || eqOp < op)) { op = eqOp; opStr = "=="; opLen = 2; } - if (gt && (!op || gt < op) && (gt != gtOp)) { op = gt; opStr = ">"; opLen = 1; } - if (lt && (!op || lt < op) && (lt != ltOp)) { op = lt; opStr = "<"; opLen = 1; } - - if (op) { - *op = '\0'; - char *leftStr = ptr; - char *rightStr = op + opLen; - - Variable *left = evaluateExpression(leftStr); - Variable *right = evaluateExpression(rightStr); - - if (left && right) { - Variable *result = malloc(sizeof(Variable)); - *result = performComparison(left, right, opStr); - free(left); - free(right); - free(trimmed); - return result; - } - if (left) free(left); - if (right) free(right); - free(trimmed); - return NULL; - } - - // Check for NOT/! operator first - if (strncmp(ptr, "NOT ", 4) == 0 || strncmp(ptr, "! ", 2) == 0 || ptr[0] == '!') { - // Skip the minus if it's followed by a number (negative number) - if (ptr[0] == '-' && isdigit(ptr[1])) { - // Do nothing, let it fall through to number parsing - } else { - Variable *operand = evaluateExpression(ptr + (ptr[0] == 'N' ? 4 : (ptr[0] == '!' && ptr[1] == ' ' ? 2 : 1))); - if (operand) { - Variable *result = malloc(sizeof(Variable)); - *result = performLogicalOperation(operand, NULL, "NOT"); - free(operand); - free(trimmed); - return result; - } - } - } - - // Check for AND/OR/&&/|| operators - char *andOp = strstr(ptr, " AND "); - char *orOp = strstr(ptr, " OR "); - char *andSymbolOp = strstr(ptr, "&&"); - char *orSymbolOp = strstr(ptr, "||"); - op = NULL; - opStr = NULL; - opLen = 0; - - // Find the leftmost operator - if (andOp && (!op || andOp < op)) { op = andOp; opStr = "AND"; opLen = 5; } - if (orOp && (!op || orOp < op)) { op = orOp; opStr = "OR"; opLen = 4; } - if (andSymbolOp && (!op || andSymbolOp < op)) { op = andSymbolOp; opStr = "AND"; opLen = 2; } - if (orSymbolOp && (!op || orSymbolOp < op)) { op = orSymbolOp; opStr = "OR"; opLen = 2; } - - if (op) { - *op = '\0'; - char *leftStr = ptr; - char *rightStr = op + opLen; - - // Trim spaces around operators - while (*rightStr == ' ') rightStr++; - - Variable *left = evaluateExpression(leftStr); - Variable *right = evaluateExpression(rightStr); - - if (left && right) { - Variable *result = malloc(sizeof(Variable)); - *result = performLogicalOperation(left, right, opStr); - free(left); - free(right); - free(trimmed); - return result; - } - if (left) free(left); - if (right) free(right); - free(trimmed); - return NULL; - } - - // Check for two-character operators first - op = NULL; - for (char *c = ptr; *c; c++) { - if ((c[0] == '*' && c[1] == '*') || - (c[0] == '/' && c[1] == '/')) { - op = c; - break; - } - // Check for modulo or single operators - if (*c == '%' || (*c == '*' || *c == '/' || *c == '+' || *c == '-')) { - if (*c == '-' && (c == ptr || isOperator(*(c-1)))) { - continue; // Skip leading minus or minus after operator - } - if (!op) op = c; - } - } - - if (!op) { - // First check if it's a simple variable or value - if (!strchr(ptr, '+') && !strchr(ptr + 1, '-') && !strchr(ptr, '*') && !strchr(ptr, '/')) { - Variable *var = findVariable(ptr); - if (var) { - Variable *result = malloc(sizeof(Variable)); - *result = *var; - // Deep copy string if needed - if (var->type == STRING) { - result->value.stringValue = strdup(var->value.stringValue); - } - result->name = NULL; - free(trimmed); - return result; - } - // Try parsing as number (including negative numbers) - if (isdigit(*ptr) || (*ptr == '-' && isdigit(*(ptr + 1)))) { - Variable *result = malloc(sizeof(Variable)); - if (isFloat(ptr)) { - result->type = FLOAT; - result->value.floatValue = parseFloat(ptr); - } else { - result->type = INT; - result->value.intValue = atoi(ptr); - } - result->name = NULL; - result->isConstant = 0; - free(trimmed); - return result; - } - free(trimmed); - return NULL; - } - } - - // Handle two-character operators - char operator[3] = {0}; - char *rightStr; - if ((op[0] == '*' && op[1] == '*') || (op[0] == '/' && op[1] == '/')) { - operator[0] = op[0]; - operator[1] = op[1]; - *op = '\0'; - rightStr = op + 2; - } else { - operator[0] = *op; - *op = '\0'; - rightStr = op + 1; - } - char *leftStr = ptr; - - // Split the expression and save the operator - char *left_trimmed = strdup(leftStr); - char *right_trimmed = strdup(rightStr); - - char *left_ptr = left_trimmed; - char *right_ptr = right_trimmed; - - // Trim left operand - while (*left_ptr == ' ' || *left_ptr == '\t') left_ptr++; - char *left_end = left_ptr + strlen(left_ptr) - 1; - while (left_end > left_ptr && (*left_end == ' ' || *left_end == '\t')) { - *left_end = '\0'; - left_end--; - } - - // Trim right operand - while (*right_ptr == ' ' || *right_ptr == '\t') right_ptr++; - char *right_end = right_ptr + strlen(right_ptr) - 1; - while (right_end > right_ptr && (*right_end == ' ' || *right_end == '\t')) { - *right_end = '\0'; - right_end--; - } - - // Get operands - Variable *left = findVariable(left_ptr); - Variable *right = findVariable(right_ptr); - Variable temp_left, temp_right; - - // Handle numeric literals for left operand - if (!left && (isdigit(*left_ptr) || (*left_ptr == '-' && isdigit(*(left_ptr + 1))))) { - temp_left.type = isFloat(left_ptr) ? FLOAT : INT; - if (temp_left.type == FLOAT) - temp_left.value.floatValue = parseFloat(left_ptr); - else - temp_left.value.intValue = atoi(left_ptr); - temp_left.name = NULL; - temp_left.isConstant = 0; - left = &temp_left; - } - - // Handle numeric literals for right operand - if (!right && (isdigit(*right_ptr) || (*right_ptr == '-' && isdigit(*(right_ptr + 1))))) { - temp_right.type = isFloat(right_ptr) ? FLOAT : INT; - if (temp_right.type == FLOAT) - temp_right.value.floatValue = parseFloat(right_ptr); - else - temp_right.value.intValue = atoi(right_ptr); - temp_right.name = NULL; - temp_right.isConstant = 0; - right = &temp_right; - } - - // Clean up - free(left_trimmed); - free(right_trimmed); - - if (!left || !right) { - free(trimmed); - return NULL; - } - - // Create operator string and perform operation - Variable *result = malloc(sizeof(Variable)); - *result = performArithmeticOperation(left, right, operator); - - free(trimmed); - return result; -} diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c deleted file mode 100644 index 8211e77..0000000 --- a/src/interpreter/interpreter.c +++ /dev/null @@ -1,273 +0,0 @@ -#include "interpreter.h" -#include "variables.h" -#include "expressions.h" -#include "display.h" -#include "input.h" -#include "control_flow.h" -#include "error.h" -#include -#include -#include -#include - -void interpretCommand(const char *command) { - if (strncmp(command, "if", 2) == 0) { - char *openParen = strchr(command, '('); - if (!openParen) { - reportError(ERROR_SYNTAX, getCurrentLine(), "Missing opening parenthesis"); - } - - // Find the matching closing parenthesis for the if condition - // Need to count nested parentheses, but skip parentheses inside strings - char *closeParen = NULL; - int parenCount = 1; - int inString = 0; - char stringChar = 0; - char *ptr = openParen + 1; - - while (*ptr && parenCount > 0) { - // Handle string literals - if ((*ptr == '"' || *ptr == '\'') && !inString) { - inString = 1; - stringChar = *ptr; - } else if (inString && *ptr == stringChar) { - inString = 0; - } - - // Only count parentheses outside of strings - if (!inString) { - if (*ptr == '(') { - parenCount++; - } else if (*ptr == ')') { - parenCount--; - if (parenCount == 0) { - closeParen = ptr; - break; - } - } - } - ptr++; - } - - if (!closeParen) { - reportError(ERROR_SYNTAX, getCurrentLine(), "Missing closing parenthesis"); - } - - char *openBrace = strchr(closeParen, '{'); - if (!openBrace) { - reportError(ERROR_SYNTAX, getCurrentLine(), "Missing opening brace"); - } - - char *closeBrace = strrchr(command, '}'); - if (!closeBrace) { - reportError(ERROR_SYNTAX, getCurrentLine(), "Missing closing brace"); - } - - // Extract condition - size_t conditionLen = closeParen - (openParen + 1); - char *condition = (char *)malloc(conditionLen + 1); - strncpy(condition, openParen + 1, conditionLen); - condition[conditionLen] = '\0'; - - // Extract block content - char *block = malloc(strlen(openBrace) + 1); - strcpy(block, openBrace); - executeIfBlock(condition, block); - - free(condition); - free(block); - return; - } - - if (strncmp(command, "const ", 6) == 0) { - // Handle constant declaration - const char *declaration = command + 6; - char *equalsSign = strchr(declaration, '='); - if (!equalsSign) { - reportError(ERROR_SYNTAX, getCurrentLine(), - "Constant declaration requires initialization"); - } - - size_t nameLength = equalsSign - declaration; - char *name = (char *)malloc(nameLength + 1); - strncpy(name, declaration, nameLength); - name[nameLength] = '\0'; - - // Trim name - char *end = name + nameLength - 1; - while (end > name && (*end == ' ' || *end == '\t')) { - *end = '\0'; - end--; - } - char *start = name; - while (*start == ' ' || *start == '\t') start++; - if (start != name) { - memmove(name, start, strlen(start) + 1); - } - - char *value = equalsSign + 1; - while (*value == ' ') value++; - - // Evaluate and add constant - Variable *result = evaluateExpression(value); - if (result) { - if (result->type == FLOAT) { - float floatVal = result->value.floatValue; - addVariable(name, FLOAT, &floatVal, 1); - } else if (result->type == BOOLEAN) { - int boolVal = result->value.boolValue; - addVariable(name, BOOLEAN, &boolVal, 1); - } else { - int intVal = result->value.intValue; - addVariable(name, INT, &intVal, 1); - } - free(result); - } else { - // Handle literal values - if (strcmp(value, "true") == 0) { - int boolValue = 1; - addVariable(name, BOOLEAN, &boolValue, 1); - } else if (strcmp(value, "false") == 0) { - int boolValue = 0; - addVariable(name, BOOLEAN, &boolValue, 1); - } else if (isdigit(value[0]) || (value[0] == '-' && isdigit(value[1]))) { - if (isFloat(value)) { - float floatValue = parseFloat(value); - addVariable(name, FLOAT, &floatValue, 1); - } else { - int intValue = atoi(value); - addVariable(name, INT, &intValue, 1); - } - } else if (value[0] == '"' || value[0] == '\'') { - size_t valueLength = strlen(value); - value[valueLength - 1] = '\0'; - addVariable(name, STRING, value + 1, 1); - } - } - free(name); - } else if (strncmp(command, "input(", 6) == 0) { - const char *closingParenthesis = strchr(command + 6, ')'); - if (closingParenthesis) { - char *content = (char *)malloc(closingParenthesis - (command + 6) + 1); - strncpy(content, command + 6, closingParenthesis - (command + 6)); - content[closingParenthesis - (command + 6)] = '\0'; - handleInputCommand(content); - free(content); - } else { - reportError(ERROR_SYNTAX, getCurrentLine(), "Missing closing parenthesis"); - } - } else if (strncmp(command, "display(", 8) == 0) { - const char *closingParenthesis = strchr(command + 8, ')'); - if (closingParenthesis) { - char *content = (char *)malloc(closingParenthesis - (command + 8) + 1); - strncpy(content, command + 8, closingParenthesis - (command + 8)); - content[closingParenthesis - (command + 8)] = '\0'; - - // First check if it's a formatted string (starts with quote and contains %var) - if (content[0] == '"' && strstr(content, "%var") != NULL && strchr(content, ',') != NULL) { - char *format = strtok(content, ","); - format[strlen(format)-1] = '\0'; - format++; - - // Parse variables - char *vars[10]; // Maximum 10 variables - int varCount = 0; - char *var; - while ((var = strtok(NULL, ",")) != NULL) { - while (*var == ' ') var++; // Skip leading spaces - vars[varCount++] = var; - } - - displayFormatted(format, vars, varCount); - } else { - // Handle regular display cases - char *endptr; - long intValue = strtol(content, &endptr, 10); - if (*endptr == '\0') { - displayInt((int)intValue); - } else if (isFloat(content)) { - displayFloat(parseFloat(content)); - } else if ((content[0] == '"' && content[strlen(content)-1] == '"') || - (content[0] == '\'' && content[strlen(content)-1] == '\'')) { - content[strlen(content)-1] = '\0'; - display(content + 1); - } else { - reportError(ERROR_SYNTAX, getCurrentLine(), "Invalid display format"); - } - } - free(content); - } else { - reportError(ERROR_SYNTAX, getCurrentLine(), "Missing closing parenthesis"); - } - } else if (strchr(command, '=') != NULL) { - char *equalsSign = strchr(command, '='); - size_t nameLength = equalsSign - command; - char *name = (char *)malloc(nameLength + 1); - strncpy(name, command, nameLength); - name[nameLength] = '\0'; - - // Trim trailing whitespace from variable name - char *end = name + nameLength - 1; - while (end > name && (*end == ' ' || *end == '\t')) { - *end = '\0'; - end--; - } - - // Trim leading whitespace from variable name - char *start = name; - while (*start == ' ' || *start == '\t') start++; - if (start != name) { - memmove(name, start, strlen(start) + 1); - } - - char *value = equalsSign + 1; - while (*value == ' ') value++; - - // Check for arithmetic expression first - Variable *result = evaluateExpression(value); - if (result) { - if (result->type == FLOAT) { - float floatVal = result->value.floatValue; - addVariable(name, FLOAT, &floatVal, 0); - } else if (result->type == BOOLEAN) { - int boolVal = result->value.boolValue; - addVariable(name, BOOLEAN, &boolVal, 0); - } else { - int intVal = result->value.intValue; - addVariable(name, INT, &intVal, 0); - } - free(result); - } else { - // Handle non-expression assignments - if (strcmp(value, "true") == 0) { - int boolValue = 1; - addVariable(name, BOOLEAN, &boolValue, 0); - } else if (strcmp(value, "false") == 0) { - int boolValue = 0; - addVariable(name, BOOLEAN, &boolValue, 0); - } else if (isdigit(value[0]) || (value[0] == '-' && isdigit(value[1]))) { - if (isFloat(value)) { - float floatValue = parseFloat(value); - addVariable(name, FLOAT, &floatValue, 0); - } else { - int intValue = atoi(value); - addVariable(name, INT, &intValue, 0); - } - } else if (value[0] == '"' || value[0] == '\'') { - char quoteChar = value[0]; - char *endQuote = strchr(value + 1, quoteChar); - if (endQuote) { - *endQuote = '\0'; - addVariable(name, STRING, value + 1, 0); - } else { - // No closing quote found - treat as error or use whole string - reportError(ERROR_SYNTAX, getCurrentLine(), "Missing closing quote in string"); - } - } - } - - free(name); - } else { - reportError(ERROR_SYNTAX, getCurrentLine(), "Unknown command: %s", command); - } -} diff --git a/src/interpreter/variables.c b/src/interpreter/variables.c deleted file mode 100644 index fe04e5f..0000000 --- a/src/interpreter/variables.c +++ /dev/null @@ -1,121 +0,0 @@ -#include "variables.h" -#include "error.h" -#include "config.h" -#include -#include -#include -#include - -static Variable *variables = NULL; -static size_t variableCount = 0; - -Variable *findVariable(const char *name) { - char *trimmed = strdup(name); - char *ptr = trimmed; - - // Trim leading whitespace - while (*ptr == ' ' || *ptr == '\t') ptr++; - - // Trim trailing whitespace - char *end = ptr + strlen(ptr) - 1; - while (end > ptr && (*end == ' ' || *end == '\t')) { - *end = '\0'; - end--; - } - - for (size_t i = 0; i < variableCount; i++) { - if (strcmp(variables[i].name, ptr) == 0) { - free(trimmed); - return &variables[i]; - } - } - free(trimmed); - return NULL; -} - -void addVariable(const char *name, VarType type, void *value, int isConstant) { - // Check if we've reached max variables - if (variableCount >= NOVIQ_MAX_VARIABLES) { - reportError(ERROR_MEMORY, getCurrentLine(), - "Maximum number of variables (%d) exceeded", NOVIQ_MAX_VARIABLES); - } - - // First check if variable already exists - for (size_t i = 0; i < variableCount; i++) { - if (strcmp(variables[i].name, name) == 0) { - // Check if the variable is a constant - if (variables[i].isConstant) { - reportError(ERROR_CONST_MODIFY, getCurrentLine(), - "Cannot modify constant '%s'", name); - } - - // Update value - if (variables[i].type == STRING) { - free(variables[i].value.stringValue); - } - variables[i].type = type; - if (type == INT) { - variables[i].value.intValue = *(int *)value; - } else if (type == FLOAT) { - variables[i].value.floatValue = *(float *)value; - } else if (type == BOOLEAN) { - variables[i].value.boolValue = *(int *)value; - } else { - variables[i].value.stringValue = strdup((char *)value); - if (!variables[i].value.stringValue) { - reportError(ERROR_MEMORY, getCurrentLine(), "Failed to allocate memory for string"); - } - } - return; - } - } - - // If variable doesn't exist, add new one - variables = realloc(variables, (variableCount + 1) * sizeof(Variable)); - if (!variables) { - reportError(ERROR_MEMORY, getCurrentLine(), "Failed to allocate memory for variables"); - } - - variables[variableCount].name = strdup(name); - if (!variables[variableCount].name) { - reportError(ERROR_MEMORY, getCurrentLine(), "Failed to allocate memory for variable name"); - } - - variables[variableCount].type = type; - variables[variableCount].isConstant = isConstant; - - if (type == INT) { - variables[variableCount].value.intValue = *(int *)value; - } else if (type == FLOAT) { - variables[variableCount].value.floatValue = *(float *)value; - } else if (type == BOOLEAN) { - variables[variableCount].value.boolValue = *(int *)value; - } else { - variables[variableCount].value.stringValue = strdup((char *)value); - if (!variables[variableCount].value.stringValue) { - reportError(ERROR_MEMORY, getCurrentLine(), "Failed to allocate memory for string value"); - } - } - variableCount++; -} - -void freeVariable(Variable *var) { - if (var && var->type == STRING && var->value.stringValue) { - free(var->value.stringValue); - var->value.stringValue = NULL; - } -} - -void freeAllVariables(void) { - for (size_t i = 0; i < variableCount; i++) { - free(variables[i].name); - freeVariable(&variables[i]); - } - free(variables); - variables = NULL; - variableCount = 0; -} - -size_t getVariableCount(void) { - return variableCount; -} diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 6a8f710..0000000 --- a/src/main.c +++ /dev/null @@ -1,353 +0,0 @@ -#include "config.h" -#include "interpreter.h" -#include "error.h" -#include "variables.h" -#include -#include -#include - -#ifdef _WIN32 -#include -#define F_OK 0 -#define access _access -#else -#include -#endif - -// Windows implementation of getline -#ifdef _WIN32 -ssize_t getline(char **lineptr, size_t *n, FILE *stream) { - char *bufptr = NULL; - char *p = bufptr; - size_t size; - int c; - - if (lineptr == NULL || stream == NULL || n == NULL) { - return -1; - } - bufptr = *lineptr; - size = *n; - - c = fgetc(stream); - if (c == EOF) { - return -1; - } - if (bufptr == NULL) { - size = 128; - bufptr = (char *)malloc(size); - if (bufptr == NULL) { - return -1; - } - } - p = bufptr; - while (c != EOF) { - if ((p - bufptr) > (size - 1)) { - size = size + 128; - bufptr = realloc(bufptr, size); - if (bufptr == NULL) { - return -1; - } - p = bufptr + (p - bufptr); - } - *p++ = c; - if (c == '\n') { - break; - } - c = fgetc(stream); - } - - *p++ = '\0'; - *lineptr = bufptr; - *n = size; - - return p - bufptr - 1; -} -#endif - -typedef struct { - char condition[NOVIQ_MAX_VAR_NAME_LENGTH]; - char content[NOVIQ_MAX_LINE_LENGTH]; - char elseContent[NOVIQ_MAX_LINE_LENGTH]; - int braceCount; - int hasElse; -} IfBlock; - -void displayHelp(const char *programName) { - printf("Noviq Interpreter %s\n", NOVIQ_VERSION_FULL); - printf("Usage: %s [options] or %s -e \n\n", programName, programName); - printf("Options:\n"); - printf(" -e Execute a Noviq script file\n"); - printf(" -h, --h, --help Display this help message\n"); - printf(" -v, --v, --version Display Noviq version\n"); -} - -void executeFile(const char *filename) { - // Check file extension - const char *dot = strrchr(filename, '.'); - if (!dot || strcmp(dot, NOVIQ_FILE_EXTENSION) != 0) { - fprintf(stderr, "Error: File must have %s extension\n", NOVIQ_FILE_EXTENSION); - exit(EXIT_FAILURE); - } - - // Check if file exists - if (access(filename, F_OK) != 0) { - reportError(ERROR_FILE, 0, "File '%s' not found", filename); - } - - FILE *file = fopen(filename, "r"); - if (!file) { - reportError(ERROR_FILE, 0, "Cannot open file '%s'", filename); - } - - setErrorFile(filename); - - char *line = NULL; - size_t len = 0; - ssize_t read; - int lineNumber = 0; - int inMultilineComment = 0; - - // Stack of if blocks - IfBlock ifStack[NOVIQ_MAX_IF_NESTING]; - int ifStackPtr = -1; - - while ((read = getline(&line, &len, file)) != -1) { - lineNumber++; - setCurrentLine(lineNumber); - - if (line[read - 1] == '\n') { - line[read - 1] = '\0'; - } - - // Skip empty lines or lines with only whitespace - char *trimmed = line; - while (*trimmed == ' ' || *trimmed == '\t') trimmed++; - if (*trimmed == '\0') continue; - - // Handle comments - if (strncmp(trimmed, "##", 2) == 0) { - inMultilineComment = !inMultilineComment; - continue; - } - if (inMultilineComment) continue; - if (strncmp(trimmed, "#", 1) == 0) continue; - - char *commentStart = strstr(trimmed, "#"); - if (commentStart != NULL) { - *commentStart = '\0'; - if (*trimmed == '\0') continue; - } - - // Handle if blocks and other commands - if (ifStackPtr >= 0) { - // We're inside an if block - for (char *c = trimmed; *c; c++) { - if (*c == '{') { - ifStack[ifStackPtr].braceCount++; - } - else if (*c == '}') { - ifStack[ifStackPtr].braceCount--; - if (ifStack[ifStackPtr].braceCount == 0) { - // Check for else - char *elseStart = strstr(c + 1, "else"); - if (elseStart && elseStart[-1] <= ' ' && elseStart[4] <= ' ') { - // Found else, look for its opening brace - char *elseBrace = strchr(elseStart, '{'); - if (!elseBrace) { - reportError(ERROR_SYNTAX, lineNumber, "Missing opening brace for else block"); - } - ifStack[ifStackPtr].hasElse = 1; - *c = '\0'; // End if block here - break; - } else { - // No else, execute the if block - char *fullCmd = malloc(strlen(ifStack[ifStackPtr].condition) + - strlen(ifStack[ifStackPtr].content) + 32); - sprintf(fullCmd, "if(%s){%s}", - ifStack[ifStackPtr].condition, - ifStack[ifStackPtr].content); - interpretCommand(fullCmd); - free(fullCmd); - ifStackPtr--; - *c = '\0'; - break; - } - } - } - } - - if (ifStackPtr >= 0) { - if (ifStack[ifStackPtr].hasElse) { - // Accumulating else block content - if (ifStack[ifStackPtr].elseContent[0] != '\0') { - strcat(ifStack[ifStackPtr].elseContent, " "); - } - strcat(ifStack[ifStackPtr].elseContent, trimmed); - - // Check if else block is complete - if (strchr(trimmed, '}')) { - char *fullCmd = malloc(strlen(ifStack[ifStackPtr].condition) + - strlen(ifStack[ifStackPtr].content) + - strlen(ifStack[ifStackPtr].elseContent) + 64); - sprintf(fullCmd, "if(%s){%s}else{%s}", - ifStack[ifStackPtr].condition, - ifStack[ifStackPtr].content, - ifStack[ifStackPtr].elseContent); - interpretCommand(fullCmd); - free(fullCmd); - ifStackPtr--; - } - } else { - // Still in if block, append this line - if (ifStack[ifStackPtr].content[0] != '\0') { - strcat(ifStack[ifStackPtr].content, " "); - } - strcat(ifStack[ifStackPtr].content, trimmed); - } - } - } else if (strncmp(trimmed, "if(", 3) == 0) { - // Start new if block - ifStackPtr++; - if (ifStackPtr >= NOVIQ_MAX_IF_NESTING) { - reportError(ERROR_RUNTIME, lineNumber, - "Maximum if-statement nesting depth (%d) exceeded", NOVIQ_MAX_IF_NESTING); - } - - ifStack[ifStackPtr].braceCount = 0; - ifStack[ifStackPtr].content[0] = '\0'; - ifStack[ifStackPtr].elseContent[0] = '\0'; - ifStack[ifStackPtr].hasElse = 0; - - // Extract condition - need to handle nested parentheses and strings - char *openParen = strchr(trimmed, '('); - if (!openParen) { - reportError(ERROR_SYNTAX, lineNumber, "Missing opening parenthesis"); - } - - // Find the matching closing parenthesis - char *closeParen = NULL; - int parenCount = 1; - int inString = 0; - char stringChar = 0; - char *ptr = openParen + 1; - - while (*ptr && parenCount > 0) { - // Handle string literals - if ((*ptr == '"' || *ptr == '\'') && !inString) { - inString = 1; - stringChar = *ptr; - } else if (inString && *ptr == stringChar) { - inString = 0; - } - - // Only count parentheses outside of strings - if (!inString) { - if (*ptr == '(') { - parenCount++; - } else if (*ptr == ')') { - parenCount--; - if (parenCount == 0) { - closeParen = ptr; - break; - } - } - } - ptr++; - } - - if (!closeParen) { - reportError(ERROR_SYNTAX, lineNumber, "Missing closing parenthesis"); - } - - strncpy(ifStack[ifStackPtr].condition, - openParen + 1, - closeParen - (openParen + 1)); - ifStack[ifStackPtr].condition[closeParen - (openParen + 1)] = '\0'; - - // Get content after condition - char *content = strchr(closeParen, '{'); - if (content) { - ifStack[ifStackPtr].braceCount = 1; - - // Extract content after opening brace, trimming whitespace - char *contentStart = content + 1; - while (*contentStart == ' ' || *contentStart == '\t') contentStart++; - - // Check if the if statement is complete on this line - // Count remaining braces to see if it closes - int braceCount = 1; - char *ptr = content + 1; - while (*ptr) { - if (*ptr == '{') braceCount++; - else if (*ptr == '}') { - braceCount--; - if (braceCount == 0) { - // Found the matching closing brace - single line if - char *fullCmd = malloc(strlen(trimmed) + 1); - strcpy(fullCmd, trimmed); - interpretCommand(fullCmd); - free(fullCmd); - ifStackPtr--; - break; - } - } - ptr++; - } - - // If we didn't find the closing brace, it's a multi-line if - if (ifStackPtr >= 0 && braceCount > 0) { - // Don't store the opening brace line content if it's just whitespace - if (*contentStart != '\0') { - strcat(ifStack[ifStackPtr].content, contentStart); - } - } - } - } - else if (ifStackPtr < 0) { - // Regular command outside if block - interpretCommand(trimmed); - } - } - - if (ifStackPtr >= 0) { - reportError(ERROR_SYNTAX, 0, "Unclosed if block at end of file"); - } - - free(line); - fclose(file); - - // Cleanup - freeAllVariables(); -} - -int main(int argc, char *argv[]) { - if (argc < 2) { - displayHelp(argv[0]); - return 1; - } - - if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--h") == 0) { - displayHelp(argv[0]); - return 0; - } - - if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--v") == 0) { - printf("%s\n", NOVIQ_VERSION_FULL); - return 0; - } - - if (strcmp(argv[1], "-e") == 0) { - if (argc != 3) { - fprintf(stderr, "Error: No input file specified\n"); - displayHelp(argv[0]); - return 1; - } - const char *filename = argv[2]; - executeFile(filename); - return 0; - } - - fprintf(stderr, "Error: Invalid argument '%s'\n", argv[1]); - displayHelp(argv[0]); - return 1; -} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5dca37b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,24 @@ +fn get_version() -> String { + // Get version from Cargo.toml + let cargo_version = env!("CARGO_PKG_VERSION"); + let base_version = format!("nebula-{}", cargo_version); + + // Check if SNAPSHOT environment variable is set during build + let is_snapshot = option_env!("SNAPSHOT").is_some(); + + if is_snapshot || cfg!(debug_assertions) { + // Snapshot or Debug build - use snapshot format + let date = chrono::Local::now().format("%y%m%d").to_string(); + format!("{}-pulsar.{}", base_version, date) + } else { + // Release build - use base version + base_version + } +} + +fn main() { + println!("Noviq Programming Language"); + println!("Version: {}", get_version()); + println!(); + println!("A simple, compiled programming language written in Rust."); +} diff --git a/src/operators/arithmetic.c b/src/operators/arithmetic.c deleted file mode 100644 index 55c17d1..0000000 --- a/src/operators/arithmetic.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "arithmetic.h" -#include "error.h" -#include -#include - -int isOperator(char c) { - return c == '+' || c == '-' || c == '*' || c == '/' || c == '%'; -} - -Variable performArithmeticOperation(Variable *left, Variable *right, const char *operator) { - Variable result; - - // Type checking - if (left->type == STRING || right->type == STRING) { - reportError(ERROR_TYPE, getCurrentLine(), - "Cannot perform arithmetic operations with strings"); - } - - if (left->type == BOOLEAN || right->type == BOOLEAN) { - reportError(ERROR_TYPE, getCurrentLine(), - "Cannot perform arithmetic operations with booleans"); - } - - float leftVal = (left->type == FLOAT) ? left->value.floatValue : (float)left->value.intValue; - float rightVal = (right->type == FLOAT) ? right->value.floatValue : (float)right->value.intValue; - - result.type = FLOAT; - result.name = NULL; - result.isConstant = 0; - - if (strcmp(operator, "**") == 0) { - result.value.floatValue = pow(leftVal, rightVal); - } else if (strcmp(operator, "//") == 0) { - if (rightVal == 0) { - reportError(ERROR_DIV_BY_ZERO, getCurrentLine(), "Division by zero"); - } - result.type = INT; - result.value.intValue = (int)(leftVal / rightVal); - } else if (strcmp(operator, "%") == 0) { - if (rightVal == 0) { - reportError(ERROR_DIV_BY_ZERO, getCurrentLine(), "Modulo by zero"); - } - result.type = INT; - result.value.intValue = (int)leftVal % (int)rightVal; - } else { - switch(operator[0]) { - case '+': result.value.floatValue = leftVal + rightVal; break; - case '-': result.value.floatValue = leftVal - rightVal; break; - case '*': result.value.floatValue = leftVal * rightVal; break; - case '/': - if (rightVal == 0) { - reportError(ERROR_DIV_BY_ZERO, getCurrentLine(), "Division by zero"); - } - result.value.floatValue = leftVal / rightVal; - break; - default: - reportError(ERROR_SYNTAX, getCurrentLine(), "Unknown operator '%s'", operator); - } - } - - // Convert to INT if result is a whole number and operation isn't division - if (result.type == FLOAT && operator[0] != '/' && - result.value.floatValue == (int)result.value.floatValue) { - result.type = INT; - result.value.intValue = (int)result.value.floatValue; - } - - return result; -} diff --git a/src/operators/comparison.c b/src/operators/comparison.c deleted file mode 100644 index 869405c..0000000 --- a/src/operators/comparison.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "comparison.h" -#include "error.h" -#include - -int isComparisonOperator(const char *str) { - return (strcmp(str, ">") == 0 || - strcmp(str, "<") == 0 || - strcmp(str, ">=") == 0 || - strcmp(str, "<=") == 0 || - strcmp(str, "==") == 0); -} - -Variable performComparison(Variable *left, Variable *right, const char *operator) { - Variable result; - result.type = BOOLEAN; - result.name = NULL; - result.isConstant = 0; - - // Convert operands to comparable values - float leftVal, rightVal; - - switch(left->type) { - case INT: leftVal = (float)left->value.intValue; break; - case FLOAT: leftVal = left->value.floatValue; break; - case BOOLEAN: leftVal = (float)left->value.boolValue; break; - default: - reportError(ERROR_TYPE, getCurrentLine(), "Cannot compare string values"); - } - - switch(right->type) { - case INT: rightVal = (float)right->value.intValue; break; - case FLOAT: rightVal = right->value.floatValue; break; - case BOOLEAN: rightVal = (float)right->value.boolValue; break; - default: - reportError(ERROR_TYPE, getCurrentLine(), "Cannot compare string values"); - } - - if (strcmp(operator, ">") == 0) { - result.value.boolValue = leftVal > rightVal; - } else if (strcmp(operator, "<") == 0) { - result.value.boolValue = leftVal < rightVal; - } else if (strcmp(operator, ">=") == 0) { - result.value.boolValue = leftVal >= rightVal; - } else if (strcmp(operator, "<=") == 0) { - result.value.boolValue = leftVal <= rightVal; - } else if (strcmp(operator, "==") == 0) { - result.value.boolValue = leftVal == rightVal; - } - - return result; -} diff --git a/src/operators/logical.c b/src/operators/logical.c deleted file mode 100644 index c9cc187..0000000 --- a/src/operators/logical.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "logical.h" -#include -#include - -int isLogicalOperator(const char *str) { - return (strcmp(str, "AND") == 0 || - strcmp(str, "OR") == 0 || - strcmp(str, "NOT") == 0 || - strcmp(str, "&&") == 0 || - strcmp(str, "||") == 0 || - strcmp(str, "!") == 0); -} - -Variable performLogicalOperation(Variable *left, Variable *right, const char *operator) { - Variable result; - result.type = BOOLEAN; - result.name = NULL; - result.isConstant = 0; - - // Convert operands to boolean values - int leftBool = 0, rightBool = 0; - - if (left) { - switch(left->type) { - case BOOLEAN: leftBool = left->value.boolValue; break; - case INT: leftBool = left->value.intValue != 0; break; - case FLOAT: leftBool = left->value.floatValue != 0; break; - case STRING: leftBool = strlen(left->value.stringValue) > 0; break; - } - } - - if (right) { - switch(right->type) { - case BOOLEAN: rightBool = right->value.boolValue; break; - case INT: rightBool = right->value.intValue != 0; break; - case FLOAT: rightBool = right->value.floatValue != 0; break; - case STRING: rightBool = strlen(right->value.stringValue) > 0; break; - } - } - - if (strcmp(operator, "AND") == 0 || strcmp(operator, "&&") == 0) { - result.value.boolValue = leftBool && rightBool; - } else if (strcmp(operator, "OR") == 0 || strcmp(operator, "||") == 0) { - result.value.boolValue = leftBool || rightBool; - } else if (strcmp(operator, "NOT") == 0 || strcmp(operator, "!") == 0) { - result.value.boolValue = !leftBool; - } - - return result; -} diff --git a/src/statements/control_flow.c b/src/statements/control_flow.c deleted file mode 100644 index 35391dc..0000000 --- a/src/statements/control_flow.c +++ /dev/null @@ -1,142 +0,0 @@ -#include "control_flow.h" -#include "variables.h" -#include "expressions.h" -#include "interpreter.h" -#include -#include -#include - -int evaluateCondition(const char *condition) { - // First try to find it as a boolean variable - Variable *var = findVariable(condition); - if (var) { - if (var->type == BOOLEAN) return var->value.boolValue; - if (var->type == INT) return var->value.intValue != 0; - if (var->type == FLOAT) return var->value.floatValue != 0; - if (var->type == STRING) return strlen(var->value.stringValue) > 0; - } - - // Try to evaluate as an expression - Variable *result = evaluateExpression(condition); - if (result) { - int value; - if (result->type == BOOLEAN) value = result->value.boolValue; - else if (result->type == INT) value = result->value.intValue != 0; - else if (result->type == FLOAT) value = result->value.floatValue != 0; - else value = 0; - free(result); - return value; - } - - return 0; -} - -void executeIfBlock(const char *condition, const char *block) { - // Remove any leading/trailing spaces from condition - while (*condition == ' ' || *condition == '\t') condition++; - char *conditionEnd = (char *)condition + strlen(condition) - 1; - while (conditionEnd > condition && (*conditionEnd == ' ' || *conditionEnd == '\t')) conditionEnd--; - - // Create temporary condition string - char *tempCondition = (char *)malloc(conditionEnd - condition + 2); - strncpy(tempCondition, condition, conditionEnd - condition + 1); - tempCondition[conditionEnd - condition + 1] = '\0'; - - if (evaluateCondition(tempCondition)) { - // Extract and execute if block - char *ifContent = strstr(block, "{"); - if (ifContent) { - // Find the matching closing brace by counting nested braces - char *endIf = ifContent + 1; - int braceCount = 1; - int inString = 0; - char stringChar = 0; - - while (*endIf && braceCount > 0) { - // Handle string literals - if ((*endIf == '"' || *endIf == '\'') && !inString) { - inString = 1; - stringChar = *endIf; - } else if (inString && *endIf == stringChar) { - inString = 0; - } - - // Only count braces outside of strings - if (!inString) { - if (*endIf == '{') braceCount++; - if (*endIf == '}') braceCount--; - } - - // Move to next character if we haven't found the matching brace - if (braceCount > 0) endIf++; - } - - // Execute if block content - // Calculate the length of content between { and } - size_t contentLen = endIf - (ifContent + 1); - - char *blockContent = malloc(contentLen + 1); - strncpy(blockContent, ifContent + 1, contentLen); - blockContent[contentLen] = '\0'; - - char *line = strtok(blockContent, "\n"); - while (line) { - while (*line == ' ' || *line == '\t') line++; - if (*line) { - interpretCommand(line); - } - line = strtok(NULL, "\n"); - } - free(blockContent); - } - } else { - // Find and execute else block if it exists - char *elseContent = strstr(block, "else"); - // Skip any nested else blocks by ensuring we're at the right nesting level - while (elseContent) { - // Count braces between start and else to verify it's the correct else - const char *temp = block; - int braceCount = 0; - while (temp < elseContent) { - if (*temp == '{') braceCount++; - if (*temp == '}') braceCount--; - temp++; - } - // If braceCount is 0, we found our else - if (braceCount == 0) break; - elseContent = strstr(elseContent + 4, "else"); - } - - if (elseContent) { - char *elseBlock = strchr(elseContent, '{'); - if (elseBlock) { - // Find the end of the else block using brace counting - char *endElse = elseBlock + 1; - int braceCount = 1; - while (*endElse && braceCount > 0) { - if (*endElse == '{') braceCount++; - if (*endElse == '}') braceCount--; - if (braceCount > 0) endElse++; - } - - // Execute else block content - size_t contentLen = endElse - (elseBlock + 1); - char *blockContent = malloc(contentLen + 1); - strncpy(blockContent, elseBlock + 1, contentLen); - blockContent[contentLen] = '\0'; - - char *line = strtok(blockContent, "\n"); - while (line) { - while (*line == ' ' || *line == '\t') line++; - if (*line) { - interpretCommand(line); - } - line = strtok(NULL, "\n"); - } - free(blockContent); - } - } - } - - free(tempCondition); -} diff --git a/src/statements/display.c b/src/statements/display.c deleted file mode 100644 index 1f9ce79..0000000 --- a/src/statements/display.c +++ /dev/null @@ -1,95 +0,0 @@ -#include "display.h" -#include "variables.h" -#include "expressions.h" -#include "error.h" -#include -#include -#include - -void display(const char *text) { - printf("%s\n", text); -} - -void displayInt(int value) { - printf("%d\n", value); -} - -void displayFloat(float value) { - printf("%f\n", value); -} - -void displayFormatted(const char *format, char **vars, int varCount) { - char output[1024] = ""; - - while (*format) { - if (*format == '%' && strncmp(format, "%var", 4) == 0) { - format += 4; - int varNum = 0; - while (*format >= '0' && *format <= '9') { - varNum = varNum * 10 + (*format - '0'); - format++; - } - - if (varNum < 1 || varNum > varCount) { - reportError(ERROR_RUNTIME, getCurrentLine(), - "Invalid variable number %d (expected 1-%d)", varNum, varCount); - } - - // Get the variable or expression - char *expr = vars[varNum - 1]; - while (*expr == ' ') expr++; // Skip leading spaces - - // Use isExpression to check for all operator types - if (isExpression(expr)) { - // Handle expression - Variable *result = evaluateExpression(expr); - if (result) { - char numStr[32]; - if (result->type == INT) { - sprintf(numStr, "%d", result->value.intValue); - } else { - sprintf(numStr, "%.6f", result->value.floatValue); - } - strcat(output, numStr); - free(result); - } else { - reportError(ERROR_RUNTIME, getCurrentLine(), - "Invalid expression '%s'", expr); - } - } else { - // Handle simple variable - Variable *var = findVariable(expr); - if (!var) { - reportError(ERROR_UNDEFINED_VAR, getCurrentLine(), - "Variable '%s' not found", expr); - } - - char numStr[32]; - switch (var->type) { - case INT: - sprintf(numStr, "%d", var->value.intValue); - strcat(output, numStr); - break; - case FLOAT: - sprintf(numStr, "%.6f", var->value.floatValue); - strcat(output, numStr); - break; - case BOOLEAN: - strcat(output, var->value.boolValue ? "true" : "false"); - break; - case STRING: - strcat(output, var->value.stringValue); - break; - } - } - continue; - } - - int len = strlen(output); - output[len] = *format; - output[len + 1] = '\0'; - format++; - } - - printf("%s\n", output); -} diff --git a/src/statements/input.c b/src/statements/input.c deleted file mode 100644 index 624960e..0000000 --- a/src/statements/input.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "input.h" -#include "variables.h" -#include "expressions.h" -#include -#include -#include - -void processInput(const char *prompt, const char *varName) { - char buffer[1024]; - - // Print the prompt - printf("%s", prompt); - fflush(stdout); - - // Get input - if (fgets(buffer, sizeof(buffer), stdin) != NULL) { - // Remove trailing newline - size_t len = strlen(buffer); - if (len > 0 && buffer[len-1] == '\n') { - buffer[len-1] = '\0'; - } - - // Try to parse as number first - char *endptr; - long intValue = strtol(buffer, &endptr, 10); - - if (*endptr == '\0') { - // It's an integer - int value = (int)intValue; - addVariable(varName, INT, &value, 0); - } else if (isFloat(buffer)) { - // It's a float - float value = parseFloat(buffer); - addVariable(varName, FLOAT, &value, 0); - } else if (strcmp(buffer, "true") == 0) { - // It's boolean true - int value = 1; - addVariable(varName, BOOLEAN, &value, 0); - } else if (strcmp(buffer, "false") == 0) { - // It's boolean false - int value = 0; - addVariable(varName, BOOLEAN, &value, 0); - } else { - // Treat as string by default - addVariable(varName, STRING, buffer, 0); - } - } -} - -void handleInputCommand(const char *args) { - char *argsTemp = strdup(args); - char **prompts = NULL; - char **variables = NULL; - int count = 0; - int maxVars = 10; // Maximum variables to handle - - prompts = malloc(maxVars * sizeof(char*)); - variables = malloc(maxVars * sizeof(char*)); - - // Initialize prompts to NULL - for (int i = 0; i < maxVars; i++) { - prompts[i] = NULL; - } - - char *token = strtok(argsTemp, ","); - while (token != NULL && count < maxVars) { - // Skip leading whitespace - while (*token == ' ') token++; - - if (token[0] == '"' || token[0] == '\'') { - // This is a prompt - size_t len = strlen(token); - token[len-1] = '\0'; // Remove closing quote - prompts[count] = strdup(token + 1); - } else { - // This is a variable - variables[count] = strdup(token); - count++; - } - token = strtok(NULL, ","); - } - - // Process all variables - for (int i = 0; i < count; i++) { - // If we have a prompt for this variable, use it - // Otherwise use a modified version of the first prompt - if (prompts[i]) { - processInput(prompts[i], variables[i]); - } else if (i > 0 && prompts[0]) { - // For subsequent variables using the same prompt, just show ": " - processInput(": ", variables[i]); - } else { - processInput(prompts[0], variables[i]); - } - } - - // Cleanup - for (int i = 0; i < count; i++) { - if (prompts[i]) free(prompts[i]); - if (variables[i]) free(variables[i]); - } - free(prompts); - free(variables); - free(argsTemp); -} diff --git a/src/utils/error.c b/src/utils/error.c deleted file mode 100644 index c922859..0000000 --- a/src/utils/error.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "error.h" -#include -#include -#include - -static const char *currentFile = NULL; -static int currentLine = 0; - -void setErrorFile(const char *filename) { - currentFile = filename; -} - -int getCurrentLine(void) { - return currentLine; -} - -void setCurrentLine(int lineNumber) { - currentLine = lineNumber; -} - -void reportError(ErrorType type, int lineNumber, const char *format, ...) { - const char *errorPrefix[] = { - "Syntax Error", - "Runtime Error", - "Type Error", - "Undefined Variable Error", - "Constant Modification Error", - "Division by Zero Error", - "File Error", - "Memory Error" - }; - - fprintf(stderr, "%s", errorPrefix[type]); - - if (lineNumber > 0) { - fprintf(stderr, " on line %d", lineNumber); - } - - if (currentFile) { - fprintf(stderr, " in file '%s'", currentFile); - } - - fprintf(stderr, ": "); - - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - - fprintf(stderr, "\n"); - exit(EXIT_FAILURE); -} diff --git a/tests/test.nvq b/tests/test.nvq deleted file mode 100644 index 7e50f5c..0000000 --- a/tests/test.nvq +++ /dev/null @@ -1,77 +0,0 @@ -# This is a test file demonstrating Noviq comment functionality -x = 5 # Initialize x - -# Display welcome message -display("Hello, World!") - -## -This is a multi-line comment -It will be ignored by the interpreter -## - -y = "Sam" # Set name -display("Name: %var1", y) # Show name - -y = "Samy" # Set new name -display("Name: %var1", y) # Show new name - -a = 10.22 -b = false -display("Noviq is running!") -display("Variable display: %var1 %var2 %var3 %var4",x,y,a,b) -display("%var1",a+x) -display("%var1",a*x) -display("%var1",a/x) -display("%var1",a%x) -mat = a**x -display("%var1",mat) -display("%var1",a//x) -v1 = 5 AND 0 -v2 = 0 OR 1 -v3 = NOT 5 -display("%var1 %var2 %var3",v1,v2,v3) - -v4 = 5 && 0 -v5 = 0 || 1 -v6 = !5 -display("%var1 %var2 %var3",v4,v5,v6) - -c1 = 5 > 3 -c2 = 4 < 7 -c3 = 5 >= 5 -c4 = 6 <= 3 -c5 = 4 == 4 -display("%var1 %var2 %var3 %var4 %var5",c1,c2,c3,c4,c5) - -# Testing user inputs: -# Remove the double # marks to execute the below: - -## -input("Enter value: ", x) # Single value -display("%var1",x) -input("Enter values: ", x, y) # Multiple values, one prompt -display("%var1 %var2",x,y) -input("Enter x: ", x, "Enter y: ", y) # Multiple values, multiple prompts -display("%var1 %var2",x,y) -## - -# Testing constants: -const a1 = 10 -display("%var1", a1) -const a2 = "Noviq" -display("%var1", a2) - -# Testing if statements: -x = 3 -y = 2 -if(x>=3){ - if(y<=2){ - display("Yes") - } else { - display("No") - } -} else { - display("Ofc not") -} - -# End of test file \ No newline at end of file diff --git a/tests/test_string_comments.nvq b/tests/test_string_comments.nvq deleted file mode 100644 index 17da4eb..0000000 --- a/tests/test_string_comments.nvq +++ /dev/null @@ -1,17 +0,0 @@ -# Test string parsing with comments -name1 = "Alice" # First name -name2 = 'Bob' # Second name with single quotes -name3 = "Charlie"# No space before comment -name4 = "Diana" # Multiple spaces -name5 = "Eve" # Comment with "quotes" in it - -display("Names:") -display("%var1", name1) -display("%var1", name2) -display("%var1", name3) -display("%var1", name4) -display("%var1", name5) - -# Test without comments -name6 = "Frank" -display("%var1", name6) diff --git a/wiki.txt b/wiki.txt deleted file mode 100644 index 19b3346..0000000 --- a/wiki.txt +++ /dev/null @@ -1,297 +0,0 @@ -Noviq Language Reference -========================= - -1. Introduction -------------- -Noviq is a simple, interpreted programming language designed for readability and ease of use. -It supports basic variable types and string formatting capabilities. - -2. Variable System ----------------- -Noviq supports four primary variable types: -a) Integers: Whole numbers (positive or negative) - Syntax: variableName = number - Example: count = 42 - -b) Strings: Text enclosed in quotes - Syntax: variableName = "text" or variableName = 'text' - Example: name = "John" - -c) Floats: Decimal numbers - Syntax: variableName = number.number - Example: price = 42.99 - -d) Booleans: True/false values - Syntax: variableName = true or variableName = false - Example: isValid = true - -3. Display System ---------------- -The display command is used for output and supports multiple formats: - -a) Direct Display: - - Strings: display("text") or display('text') - - Numbers: display(number) - Example: display("Hello, World!") - display(42) - -b) Variable Display: - Uses %var[n] format where n is the variable position (1-based) - Syntax: display("%var1", variableName) - Example: display("%var1", x) // Shows value of x - -c) Multiple Variable Display: - Can combine multiple variables in one display - Syntax: display("text %var1 more-text %var2", var1, var2) - Example: display("Age: %var1, Name: %var2", age, name) - -4. Input System -------------- -The input command is used to get user input and store it in variables: - -a) Single Variable Input: - Syntax: input("prompt message", variableName) - Example: input("Enter value: ", x) - -b) Multiple Variable Input (Same Prompt): - Syntax: input("prompt message", var1, var2, ...) - Example: input("Enter values: ", x, y) - Note: Subsequent inputs will show just ":" as prompt - -c) Multiple Variable Input (Different Prompts): - Syntax: input("prompt1", var1, "prompt2", var2, ...) - Example: input("Enter x: ", x, "Enter y: ", y) - -d) Type Detection: - The input system automatically detects and converts to: - - Integer: When input is a whole number - - Float: When input contains decimal point - - Boolean: When input is "true" or "false" - - String: For all other input types - -e) Usage Rules: - - Each prompt must be in quotes - - Variables don't need to be declared beforehand - - Press Enter after each input value - - Type is determined automatically - - Values are stored immediately after input - -5. String Formatting System ------------------------- -Noviq supports dynamic string creation using variables: - -a) Basic Format: - Syntax: result = ("format string", var1, var2, ...) - Example: greeting = ("%var1, welcome!", name) - -b) Multiple Variables: - Can combine multiple variables in any order - Example: info = ("%var1 is %var2 years old", name, age) - -6. Syntax Rules -------------- -a) Variables: - - Must start with letter or underscore - - Can contain letters, numbers, underscores - - Case-sensitive - - No special characters or spaces - -b) Strings: - - Must use matching quotes ('' or "") - - Cannot mix quote types - - Can contain any characters - -c) General: - - One command per line - - No line terminators (semicolons) needed - - Whitespace is ignored except in strings - - Empty lines are allowed - -7. Control Flow -------------- -a) If Statement: - - Basic conditional execution - Syntax: - if(condition) { - statements - } - - - Condition can be: - * Boolean variable - * Comparison expression - * Logical expression - - - Example: - if(x > 5) { - display("x is greater than 5") - } - - if(isValid) { - display("Valid") - } - - if(x > 5 AND y < 10) { - display("Condition met") - } - - - Indentation is optional - - Braces are required - - Multiple statements allowed in block - - Nested if statements supported - - - Else Statement: - Syntax: - if(condition) { - statements - } else { - statements - } - - Example: - if(x > 5) { - display("Greater than 5") - } else { - display("Less than or equal to 5") - } - - - Rules: - * else must be on same line as closing brace of if - * else block must have braces - * else can be used with any if statement - * else executes when if condition is false - -8. Error Handling ---------------- -The interpreter provides specific errors for: -a) Syntax Violations: - - Missing or mismatched quotes - - Invalid variable names - - Missing parentheses - - Invalid number format - -b) Runtime Errors: - - Undefined variables - - Invalid variable types - - Format string errors - - Memory allocation failures - -Each error message includes the line number and details about the error. - -9. Arithmetic Operations ---------------------- -Noviq supports basic arithmetic operations for numbers: - -a) Supported Operators: - + Addition - - Subtraction - * Multiplication - / Division - // Floor Division - % Modulus - ** Exponentiation - -b) Usage: - Syntax: result = operand1 operator operand2 - Examples: - sum = 5 + 3 - diff = 10 - 4 - prod = 6 * 2 - quot = 15 / 3 - floor_div = 17 // 5 // Results in 3 - remainder = 17 % 5 // Results in 2 - power = 2 ** 3 // Results in 8 - -c) Rules: - - Operands can be numbers or variables - - Operations between different types (int/float) result in float - - Division always produces float results - - Division by zero produces an error - -10. Logical Operations ------------------- -Noviq supports basic logical operations: - -a) Supported Operators: - AND or && - Logical AND - OR or || - Logical OR - NOT or ! - Logical NOT - > - Greater than - < - Less than - >= - Greater than or equal - <= - Less than or equal - == - Equal to - -b) Usage: - Syntax: result = operand1 operator operand2 - Examples: - isTrue = true AND false // Results in false - isTrue = true && false // Results in false - isTrue = true OR false // Results in true - isTrue = true || false // Results in true - isTrue = NOT true // Results in false - isTrue = !true // Results in false - -c) Type Conversion Rules: - - Numbers: 0 is false, any other value is true - - Strings: Empty string is false, non-empty is true - - Booleans: Used directly - - Results are always boolean type - -11. Comments ---------- -Noviq supports two types of comments: - -a) Single-line Comments: - Syntax: # comment text - Example: x = 5 # This sets x to 5 - - Everything after # on a line is ignored - - Can be used at the end of code lines - - Can be used for full-line comments - -b) Multi-line Comments: - Syntax: ## text ## - Example: - ## - This is a multi-line comment - It can span multiple lines - The interpreter ignores everything between the markers - ## - - - Uses ## markers - - Must be on lines by themselves (whitespace allowed) - - Can span any number of lines - - Cannot be nested - - Useful for temporarily disabling blocks of code - -c) Comment Rules: - - Comments cannot be nested - - Multi-line comment markers must be on their own lines - - Single-line comments can appear anywhere on a line - - Code after a comment marker on the same line is ignored - - Comments do not affect line numbering for error reporting - -12. Constants ------------ -Noviq supports constant declarations: - -a) Declaration: - Syntax: const variableName = value - Example: const pi = 3.14159 - const name = "John" - const enabled = true - -b) Rules: - - Constants must be initialized when declared - - Constants cannot be modified after declaration - - Constants support all variable types - - Attempting to modify a constant produces an error - - Constants can be used in expressions like regular variables - -c) Usage Examples: - const MAX_SIZE = 100 - const PI = 3.14159 - const GREETING = "Hello" - const ENABLED = true - - x = MAX_SIZE + 5 // Valid: using constant in expression - MAX_SIZE = 200 // Error: cannot modify constant From f3a387fd232fe2e5c47f0f0b8d4e1d602ee6b175 Mon Sep 17 00:00:00 2001 From: Coredex Date: Sat, 8 Nov 2025 20:33:04 +0530 Subject: [PATCH 2/3] [Patch] Do not push releases. --- .github/workflows/build.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c03624d..917e4a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -136,19 +136,3 @@ jobs: - name: Get current date id: date run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT - - - name: Create Release - uses: softprops/action-gh-release@v1 - with: - tag_name: snapshot-${{ steps.date.outputs.date }}-${{ github.run_number }} - name: Snapshot Build ${{ steps.date.outputs.date }} - draft: false - prerelease: true - files: artifacts/**/* - body: | - Automated snapshot build from commit ${{ github.sha }} - - Built on: ${{ steps.date.outputs.date }} - Commit: ${{ github.event.head_commit.message }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From f60f108569db920febb4729deacb570c8f407c1e Mon Sep 17 00:00:00 2001 From: Coredex Date: Sat, 8 Nov 2025 21:41:54 +0530 Subject: [PATCH 3/3] [Patch] Add print statement. --- Cargo.toml | 6 +- ORGANIZATION.md | 99 +++++++++++++++ README.md | 6 +- STRUCTURE.md | 218 ++++++++++++++++++++++++++++++++ examples/README.md | 30 +++++ examples/hello.nvq | 6 + examples/test.nvq | 5 + src/frontend/ast.rs | 18 +++ src/frontend/lexer/mod.rs | 109 ++++++++++++++++ src/frontend/lexer/reader.rs | 94 ++++++++++++++ src/frontend/mod.rs | 17 +++ src/frontend/parser/expr.rs | 79 ++++++++++++ src/frontend/parser/mod.rs | 77 +++++++++++ src/frontend/parser/stmt.rs | 40 ++++++ src/frontend/token.rs | 29 +++++ src/lib.rs | 19 +++ src/main.rs | 91 ++++++++++--- src/runtime/builtins/log.rs | 30 +++++ src/runtime/builtins/mod.rs | 23 ++++ src/runtime/interpreter/expr.rs | 32 +++++ src/runtime/interpreter/mod.rs | 53 ++++++++ src/runtime/interpreter/stmt.rs | 21 +++ src/runtime/mod.rs | 14 ++ src/runtime/value.rs | 22 ++++ src/utils/mod.rs | 14 ++ src/utils/tests.rs | 51 ++++++++ src/utils/version.rs | 38 ++++++ 27 files changed, 1223 insertions(+), 18 deletions(-) create mode 100644 ORGANIZATION.md create mode 100644 STRUCTURE.md create mode 100644 examples/README.md create mode 100644 examples/hello.nvq create mode 100644 examples/test.nvq create mode 100644 src/frontend/ast.rs create mode 100644 src/frontend/lexer/mod.rs create mode 100644 src/frontend/lexer/reader.rs create mode 100644 src/frontend/mod.rs create mode 100644 src/frontend/parser/expr.rs create mode 100644 src/frontend/parser/mod.rs create mode 100644 src/frontend/parser/stmt.rs create mode 100644 src/frontend/token.rs create mode 100644 src/lib.rs create mode 100644 src/runtime/builtins/log.rs create mode 100644 src/runtime/builtins/mod.rs create mode 100644 src/runtime/interpreter/expr.rs create mode 100644 src/runtime/interpreter/mod.rs create mode 100644 src/runtime/interpreter/stmt.rs create mode 100644 src/runtime/mod.rs create mode 100644 src/runtime/value.rs create mode 100644 src/utils/mod.rs create mode 100644 src/utils/tests.rs create mode 100644 src/utils/version.rs diff --git a/Cargo.toml b/Cargo.toml index c8faefb..6605747 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,12 +3,16 @@ name = "noviq" version = "0.1.0" edition = "2021" authors = ["Noviq Contributors"] -description = "A simple, compiled programming language written in Rust" +description = "A simple, interpreted programming language written in Rust" license = "GPL-3.0" [dependencies] chrono = "0.4" +[lib] +name = "noviq" +path = "src/lib.rs" + [[bin]] name = "noviq" path = "src/main.rs" diff --git a/ORGANIZATION.md b/ORGANIZATION.md new file mode 100644 index 0000000..a5962d6 --- /dev/null +++ b/ORGANIZATION.md @@ -0,0 +1,99 @@ +# Noviq Project Organization + +This document shows the final modular structure of the Noviq interpreter. + +## Directory Structure + +``` +src/ +├── main.rs # Entry point +├── lib.rs # Library root +│ +├── utils/ # Utilities +│ ├── mod.rs +│ ├── version.rs # Version management (nebula/pulsar) +│ └── tests.rs # Integration tests +│ +├── frontend/ # Source → AST +│ ├── mod.rs +│ ├── token.rs # Token definitions +│ ├── ast.rs # AST definitions +│ │ +│ ├── lexer/ # Tokenization +│ │ ├── mod.rs # Main lexer logic +│ │ └── reader.rs # Character reading +│ │ +│ └── parser/ # Parsing +│ ├── mod.rs # Main parser logic +│ ├── expr.rs # Expression parsing +│ └── stmt.rs # Statement parsing +│ +└── runtime/ # AST → Execution + ├── mod.rs + ├── value.rs # Value types (String, Null) + │ + ├── builtins/ # Built-in functions + │ ├── mod.rs # Function router + │ └── log.rs # log() implementation + │ + └── interpreter/ # Execution + ├── mod.rs # Main interpreter + ├── expr.rs # Expression evaluator + └── stmt.rs # Statement executor +``` + +## Module Responsibilities + +### Frontend +- **token.rs**: Token enum definitions +- **ast.rs**: AST node definitions (Expr, Stmt) +- **lexer/**: Converts source text → tokens + - `reader.rs`: Character-level operations + - `mod.rs`: Tokenization logic +- **parser/**: Converts tokens → AST + - `expr.rs`: Expression parsing + - `stmt.rs`: Statement parsing + - `mod.rs`: Main parser orchestration + +### Runtime +- **value.rs**: Runtime value types +- **builtins/**: Built-in functions (each function gets its own file) + - `log.rs`: The `log()` function + - `mod.rs`: Routes function calls +- **interpreter/**: Executes AST + - `expr.rs`: Evaluates expressions + - `stmt.rs`: Executes statements + - `mod.rs`: Main interpreter orchestration + +### Utils +- **version.rs**: Version string generation +- **tests.rs**: Integration tests + +## Benefits of This Structure + +1. **Modularity**: Each component has a single responsibility +2. **Extensibility**: Easy to add new builtins (just create a new file in `builtins/`) +3. **Testability**: Each module can be tested independently +4. **Clarity**: Clear separation between parsing and execution +5. **Maintainability**: Small, focused files instead of monoliths + +## Adding New Features + +### Adding a new builtin function: +1. Create `src/runtime/builtins/myfunction.rs` +2. Implement the function following the pattern in `log.rs` +3. Add it to `src/runtime/builtins/mod.rs` + +### Extending the lexer: +- Add character handling to `src/frontend/lexer/reader.rs` +- Add tokenization logic to `src/frontend/lexer/mod.rs` + +### Extending the parser: +- Modify `src/frontend/parser/expr.rs` for expressions +- Modify `src/frontend/parser/stmt.rs` for statements +- Update `src/frontend/ast.rs` if new AST nodes are needed + +### Extending the interpreter: +- Modify `src/runtime/interpreter/expr.rs` for expression evaluation +- Modify `src/runtime/interpreter/stmt.rs` for statement execution +- Update `src/runtime/value.rs` if new value types are needed diff --git a/README.md b/README.md index 3e466aa..037f8d5 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,13 @@ Noviq- The name Noviq is make up of two words: Nova(which means new) and unique. - So, Nova + unique = Noviq. -**Noviq** = **Nova** (new) + **unique**- Noviq is just a simple compiled programming language writtin in Rust which aims to be independent in the future, i.e, being writtin in Noviq itself similar to how rust does it. +**Noviq** = **Nova** (new) + **unique**- Noviq is just a simple interpreted programming language writtin in Rust which aims to be independent in the future, i.e, being writtin in Noviq itself similar to how rust does it. -A simple, compiled programming language written in Rust with a focus on clarity and ease of use. +A simple, interpreted programming language written in Rust with a focus on clarity and ease of use. A language written to have an easy syntax and high performance. +Noviq aims to be a compiled language instead of an interpreted language in the future. + ## Building ```bash diff --git a/STRUCTURE.md b/STRUCTURE.md new file mode 100644 index 0000000..97d8757 --- /dev/null +++ b/STRUCTURE.md @@ -0,0 +1,218 @@ +# Noviq Project Structure + +This document describes the organization of the Noviq interpreter codebase. + +## Overview + +Noviq is an interpreted programming language written in Rust. The project is organized as a library (`lib.rs`) with a minimal binary entry point (`main.rs`). + +## Directory Structure + +``` +Noviq/ +├── src/ +│ ├── main.rs # Binary entry point +│ ├── lib.rs # Library root +│ ├── utils/ # Utilities +│ │ ├── mod.rs # Utils module exports +│ │ ├── version.rs # Version management +│ │ └── tests.rs # Integration tests +│ ├── frontend/ # Lexer and Parser +│ │ ├── mod.rs # Frontend module exports +│ │ ├── token.rs # Token definitions +│ │ ├── ast.rs # AST definitions +│ │ ├── lexer/ # Tokenization +│ │ │ ├── mod.rs # Lexer main logic +│ │ │ └── reader.rs # Character reading utilities +│ │ └── parser/ # AST generation +│ │ ├── mod.rs # Parser main logic +│ │ ├── expr.rs # Expression parsing +│ │ └── stmt.rs # Statement parsing +│ └── runtime/ # Interpreter and execution +│ ├── mod.rs # Runtime module exports +│ ├── value.rs # Runtime value types +│ ├── builtins/ # Built-in functions +│ │ ├── mod.rs # Builtin router +│ │ └── log.rs # log() function +│ └── interpreter/ # Execution engine +│ ├── mod.rs # Interpreter main logic +│ ├── expr.rs # Expression evaluation +│ └── stmt.rs # Statement execution +├── examples/ # Example Noviq programs +│ ├── README.md +│ ├── hello.nvq +│ └── test.nvq +├── Cargo.toml +├── README.md +├── STRUCTURE.md +└── build.sh/bat/ps1 +``` + +## Module Organization + +### `main.rs` +The binary entry point. Kept minimal - only initializes and calls into the library. +Handles command-line arguments: +- No args: Shows version info and usage +- `--help` / `-h`: Shows help message +- `--version` / `-v`: Shows version +- ``: Reads and executes a Noviq file + +### `lib.rs` +The library root that: +- Declares all modules +- Re-exports public API +- Provides documentation for the crate + +### `utils/` +Utility modules: +- `version.rs` - Version string generation (nebula-X.Y.Z or pulsar builds) +- `tests.rs` - Integration tests + +### `frontend/` +Tokenization and parsing: +- `token.rs` - Token type definitions +- `ast.rs` - AST node definitions (Expr, Stmt) +- `lexer/` - Tokenizer implementation + - `mod.rs` - Main lexer logic + - `reader.rs` - Character reading and string/identifier extraction +- `parser/` - Parser implementation + - `mod.rs` - Main parser logic + - `expr.rs` - Expression parsing + - `stmt.rs` - Statement parsing + +### `runtime/` +Execution and built-in functions: +- `value.rs` - Runtime value types +- `builtins/` - Built-in function implementations + - `mod.rs` - Builtin function router + - `log.rs` - The `log()` function +- `interpreter/` - Execution engine + - `mod.rs` - Main interpreter logic + - `expr.rs` - Expression evaluator + - `stmt.rs` - Statement executor + +### `tests.rs` +Integration tests that verify: +- Example files exist and are readable +- File syntax is correct +- Core functionality works as expected + +### `examples/` +Example Noviq programs (`.nvq` extension): +- `hello.nvq` - Basic hello world with log statements +- `test.nvq` - Simple log test +- Each example demonstrates language features + +## Current Features + +The interpreter currently supports: +- **Comments**: Lines starting with `#` +- **String literals**: Double-quoted strings with escape sequences +- **Function calls**: `log("message")` - prints to stdout + +## Module Organization + +### Frontend (Parsing) +``` +frontend/ +├── token.rs - Token enum (Identifier, String, LeftParen, etc.) +├── ast.rs - AST nodes (Expr, Stmt) +├── lexer/ +│ ├── mod.rs - Main tokenization logic +│ └── reader.rs - Character-level reading (skip whitespace, read strings, etc.) +└── parser/ + ├── mod.rs - Main parsing logic + ├── expr.rs - Expression parser + └── stmt.rs - Statement parser +``` + +### Runtime (Execution) +``` +runtime/ +├── value.rs - Value enum (String, Null) +├── builtins/ +│ ├── mod.rs - Routes builtin calls by name +│ └── log.rs - log() implementation +└── interpreter/ + ├── mod.rs - Main interpreter + ├── expr.rs - Expression evaluator + └── stmt.rs - Statement executor +``` + +### Utils +``` +utils/ +├── version.rs - Version string generation +└── tests.rs - Integration tests +``` + +## Building + +```bash +# Debug build (includes pulsar timestamp) +cargo build + +# Release build +cargo build --release + +# Snapshot build (optimized but with pulsar timestamp) +SNAPSHOT=1 cargo build --profile=snapshot +``` + +## Testing + +```bash +# Run all tests +cargo test + +# Run a specific test +cargo test test_example_files_exist + +# Run tests with output +cargo test -- --nocapture +``` + +## Running Examples + +```bash +# Run with cargo +cargo run -- examples/hello.nvq + +# Or run the binary directly +./target/debug/noviq examples/hello.nvq + +# Show help +./target/debug/noviq --help + +# Show version +./target/debug/noviq --version +``` + +## Design Principles + +1. **Separation of Concerns**: Each module has a single, well-defined purpose +2. **Library-First**: Core functionality in `lib.rs`, binary is just a thin wrapper +3. **Testability**: Each module includes its own tests +4. **Documentation**: All public items are documented +5. **Extensibility**: Easy to add new modules without modifying existing code + +## Adding New Features + +To add a new feature: + +1. Create a new module file in the appropriate directory +2. Add module declaration to `lib.rs` or the parent module +3. Implement the feature with tests +4. Update documentation +5. Re-export public items if needed + +Example: +```rust +// src/builtins/math.rs +pub fn add(a: i64, b: i64) -> i64 { a + b } + +// src/builtins/mod.rs +pub mod math; +pub use math::add; +``` diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..e00fd73 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,30 @@ +# Noviq Examples + +This directory contains example Noviq programs (`.nvq` files). + +## Running Examples + +```bash +# Run an example file +noviq examples/hello.nvq + +# Or with cargo +cargo run -- examples/hello.nvq +``` + +## Available Examples + +### `hello.nvq` +Basic hello world program demonstrating: +- Print statements with `log()` +- Comments with `#` +- String literals + +### `test.nvq` +Basic test file for development + +## Current Language Features + +- **Comments**: `# This is a comment` +- **String literals**: `"Hello, World!"` +- **Function calls**: `log("message")` diff --git a/examples/hello.nvq b/examples/hello.nvq new file mode 100644 index 0000000..b79540d --- /dev/null +++ b/examples/hello.nvq @@ -0,0 +1,6 @@ +# Hello World Example +# This is a simple Noviq program + +log("Hello, World!") +log("Welcome to Noviq!") +log("This is a test of the log function") diff --git a/examples/test.nvq b/examples/test.nvq new file mode 100644 index 0000000..025b71f --- /dev/null +++ b/examples/test.nvq @@ -0,0 +1,5 @@ +# Basic test + +log("Testing") +log("Multiple") +log("Lines") diff --git a/src/frontend/ast.rs b/src/frontend/ast.rs new file mode 100644 index 0000000..d7047e1 --- /dev/null +++ b/src/frontend/ast.rs @@ -0,0 +1,18 @@ +// frontend/ast.rs +// Abstract Syntax Tree (AST) node definitions. +// Defines the structure of parsed Noviq programs (expressions and statements). + +/// AST node definitions for Noviq +/// +/// Kept minimal for the current language subset (string literals and function calls) + +#[derive(Debug, Clone, PartialEq)] +pub enum Expr { + String(String), + Call { name: String, args: Vec }, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Stmt { + Expr(Expr), +} diff --git a/src/frontend/lexer/mod.rs b/src/frontend/lexer/mod.rs new file mode 100644 index 0000000..2955caf --- /dev/null +++ b/src/frontend/lexer/mod.rs @@ -0,0 +1,109 @@ +// frontend/lexer/mod.rs +// Main lexer implementation. +// Coordinates character reading and token generation from source code. + +/// Lexer module +/// +/// Tokenizes Noviq source code + +mod reader; + +use reader::Reader; +use crate::frontend::token::Token; + +pub struct Lexer { + reader: Reader, +} + +impl Lexer { + pub fn new(input: &str) -> Self { + Lexer { + reader: Reader::new(input), + } + } + + pub fn next_token(&mut self) -> Token { + self.reader.skip_whitespace(); + + // Handle comments + if self.reader.current == Some('#') { + self.reader.skip_comment(); + if self.reader.current == Some('\n') { + self.reader.advance(); + return Token::Newline; + } else if self.reader.current.is_none() { + return Token::Eof; + } + return self.next_token(); + } + + match self.reader.current { + None => Token::Eof, + Some('\n') => { + self.reader.advance(); + Token::Newline + } + Some('"') => Token::String(self.reader.read_string()), + Some(ch) if ch.is_alphabetic() || ch == '_' => { + Token::Identifier(self.reader.read_identifier()) + } + Some('(') => { + self.reader.advance(); + Token::LeftParen + } + Some(')') => { + self.reader.advance(); + Token::RightParen + } + Some(_ch) => { + // Unknown character, skip it + self.reader.advance(); + self.next_token() + } + } + } + + pub fn tokenize(&mut self) -> Vec { + let mut tokens = Vec::new(); + + loop { + let token = self.next_token(); + if token == Token::Eof { + tokens.push(token); + break; + } + tokens.push(token); + } + + tokens + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tokenize_string() { + let mut lexer = Lexer::new(r#""Hello, World!""#); + let tokens = lexer.tokenize(); + assert_eq!(tokens[0], Token::String("Hello, World!".to_string())); + } + + #[test] + fn test_tokenize_identifier() { + let mut lexer = Lexer::new("log"); + let tokens = lexer.tokenize(); + assert_eq!(tokens[0], Token::Identifier("log".to_string())); + } + + #[test] + fn test_tokenize_function_call() { + let mut lexer = Lexer::new(r#"log("Hello")"#); + let tokens = lexer.tokenize(); + assert_eq!(tokens[0], Token::Identifier("log".to_string())); + assert_eq!(tokens[1], Token::LeftParen); + assert_eq!(tokens[2], Token::String("Hello".to_string())); + assert_eq!(tokens[3], Token::RightParen); + } +} diff --git a/src/frontend/lexer/reader.rs b/src/frontend/lexer/reader.rs new file mode 100644 index 0000000..8720f34 --- /dev/null +++ b/src/frontend/lexer/reader.rs @@ -0,0 +1,94 @@ +// frontend/lexer/reader.rs +// Character-level reading utilities for tokenization. +// Handles reading characters, strings, identifiers, and skipping whitespace/comments. + +/// Character reading utilities for the lexer + +pub struct Reader { + input: Vec, + position: usize, + pub current: Option, +} + +impl Reader { + pub fn new(input: &str) -> Self { + let chars: Vec = input.chars().collect(); + let current = chars.get(0).copied(); + Reader { + input: chars, + position: 0, + current, + } + } + + pub fn advance(&mut self) { + self.position += 1; + self.current = self.input.get(self.position).copied(); + } + + pub fn skip_whitespace(&mut self) { + while let Some(ch) = self.current { + if ch == ' ' || ch == '\t' || ch == '\r' { + self.advance(); + } else { + break; + } + } + } + + pub fn skip_comment(&mut self) { + if self.current == Some('#') { + while self.current.is_some() && self.current != Some('\n') { + self.advance(); + } + } + } + + pub fn read_string(&mut self) -> String { + let mut result = String::new(); + self.advance(); // Skip opening quote + + while let Some(ch) = self.current { + if ch == '"' { + self.advance(); // Skip closing quote + break; + } else if ch == '\\' { + self.advance(); + if let Some(escaped) = self.current { + match escaped { + 'n' => result.push('\n'), + 't' => result.push('\t'), + 'r' => result.push('\r'), + '"' => result.push('"'), + '\\' => result.push('\\'), + _ => { + result.push('\\'); + result.push(escaped); + } + } + self.advance(); + } + } else { + result.push(ch); + self.advance(); + } + } + + result + } + + pub fn read_identifier(&mut self) -> String { + let mut ident = String::new(); + + while let Some(ch) = self.current { + if ch.is_alphanumeric() || ch == '_' { + ident.push(ch); + self.advance(); + } else { + break; + } + } + + ident + } +} diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs new file mode 100644 index 0000000..c13db6e --- /dev/null +++ b/src/frontend/mod.rs @@ -0,0 +1,17 @@ +// frontend/mod.rs +// Frontend module declaration. +// Exports the lexer, parser, token types, and AST definitions. + +/// Frontend - Lexer and Parser +/// +/// This module handles tokenization and parsing of Noviq source code. + +pub mod token; +pub mod ast; +pub mod lexer; +pub mod parser; + +pub use token::Token; +pub use ast::{Expr, Stmt}; +pub use lexer::Lexer; +pub use parser::Parser; diff --git a/src/frontend/parser/expr.rs b/src/frontend/parser/expr.rs new file mode 100644 index 0000000..c61eaae --- /dev/null +++ b/src/frontend/parser/expr.rs @@ -0,0 +1,79 @@ +// frontend/parser/expr.rs +// Expression parsing logic. +// Parses tokens into expression AST nodes (strings, function calls, etc.). + +/// Expression parsing + +use crate::frontend::token::Token; +use crate::frontend::ast::Expr; + +pub struct ExprParser<'a> { + tokens: &'a [Token], + position: &'a mut usize, +} + +impl<'a> ExprParser<'a> { + pub fn new(tokens: &'a [Token], position: &'a mut usize) -> Self { + ExprParser { tokens, position } + } + + fn current(&self) -> &Token { + self.tokens.get(*self.position).unwrap_or(&Token::Eof) + } + + fn advance(&mut self) -> Token { + let token = self.current().clone(); + *self.position += 1; + token + } + + fn expect(&mut self, expected: Token) -> Result<(), String> { + if self.current() == &expected { + self.advance(); + Ok(()) + } else { + Err(format!("Expected {:?}, got {:?}", expected, self.current())) + } + } + + pub fn parse(&mut self) -> Result { + match self.current().clone() { + Token::String(s) => { + self.advance(); + Ok(Expr::String(s)) + } + Token::Identifier(name) => { + self.advance(); + + // Must be a function call + if self.current() == &Token::LeftParen { + self.advance(); // consume '(' + let args = self.parse_arguments()?; + self.expect(Token::RightParen)?; + Ok(Expr::Call { name, args }) + } else { + Err(format!("Expected '(' after identifier '{}'", name)) + } + } + token => Err(format!("Unexpected token in expression: {:?}", token)), + } + } + + fn parse_arguments(&mut self) -> Result, String> { + let mut args = Vec::new(); + + // Empty argument list + if self.current() == &Token::RightParen { + return Ok(args); + } + + // Parse first argument + let mut expr_parser = ExprParser::new(self.tokens, self.position); + args.push(expr_parser.parse()?); + + // Parse remaining arguments (separated by commas in future) + // For now, just single argument supported + + Ok(args) + } +} diff --git a/src/frontend/parser/mod.rs b/src/frontend/parser/mod.rs new file mode 100644 index 0000000..1324ab9 --- /dev/null +++ b/src/frontend/parser/mod.rs @@ -0,0 +1,77 @@ +// frontend/parser/mod.rs +// Main parser implementation. +// Orchestrates token-to-AST conversion, manages parsing state, and delegates to expr/stmt parsers. + +/// Parser module +/// +/// Converts tokens into an AST + +mod expr; +mod stmt; + +use stmt::StmtParser; +use crate::frontend::token::Token; +use crate::frontend::ast::Stmt; +use crate::frontend::lexer::Lexer; + +pub struct Parser { + tokens: Vec, + position: usize, +} + +impl Parser { + pub fn new(source: &str) -> Self { + let mut lexer = Lexer::new(source); + let tokens = lexer.tokenize(); + Parser { tokens, position: 0 } + } + + fn current(&self) -> &Token { + self.tokens.get(self.position).unwrap_or(&Token::Eof) + } + + fn advance(&mut self) { + self.position += 1; + } + + fn skip_newlines(&mut self) { + while self.current() == &Token::Newline { + self.advance(); + } + } + + pub fn parse(&mut self) -> Result, String> { + let mut statements = Vec::new(); + + self.skip_newlines(); + + while self.current() != &Token::Eof { + let mut stmt_parser = StmtParser::new(&self.tokens, &mut self.position); + let stmt = stmt_parser.parse()?; + statements.push(stmt); + self.skip_newlines(); + } + + Ok(statements) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::frontend::ast::Expr; + + #[test] + fn test_parse_function_call() { + let mut parser = Parser::new(r#"log("Hello")"#); + let stmts = parser.parse().unwrap(); + assert_eq!(stmts.len(), 1); + match &stmts[0] { + Stmt::Expr(Expr::Call { name, args }) => { + assert_eq!(name, "log"); + assert_eq!(args.len(), 1); + } + _ => panic!("Expected function call"), + } + } +} diff --git a/src/frontend/parser/stmt.rs b/src/frontend/parser/stmt.rs new file mode 100644 index 0000000..1e3b1ff --- /dev/null +++ b/src/frontend/parser/stmt.rs @@ -0,0 +1,40 @@ +// frontend/parser/stmt.rs +// Statement parsing logic. +// Parses tokens into statement AST nodes by delegating to expression parser. + +/// Statement parsing + +use crate::frontend::token::Token; +use crate::frontend::ast::Stmt; +use super::expr::ExprParser; + +pub struct StmtParser<'a> { + tokens: &'a [Token], + position: &'a mut usize, +} + +impl<'a> StmtParser<'a> { + pub fn new(tokens: &'a [Token], position: &'a mut usize) -> Self { + StmtParser { tokens, position } + } + + fn current(&self) -> &Token { + self.tokens.get(*self.position).unwrap_or(&Token::Eof) + } + + fn advance(&mut self) { + *self.position += 1; + } + + pub fn parse(&mut self) -> Result { + let mut expr_parser = ExprParser::new(self.tokens, self.position); + let expr = expr_parser.parse()?; + + // Skip optional newline after statement + if self.current() == &Token::Newline { + self.advance(); + } + + Ok(Stmt::Expr(expr)) + } +} diff --git a/src/frontend/token.rs b/src/frontend/token.rs new file mode 100644 index 0000000..36dc726 --- /dev/null +++ b/src/frontend/token.rs @@ -0,0 +1,29 @@ +// frontend/token.rs +// Token type definitions for the lexer. +// Defines all token types that can be produced during tokenization. + +use std::fmt; + +/// Tokens produced by the lexer +#[derive(Debug, Clone, PartialEq)] +pub enum Token { + Identifier(String), + String(String), + LeftParen, + RightParen, + Newline, + Eof, +} + +impl fmt::Display for Token { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Token::Identifier(s) => write!(f, "Identifier({})", s), + Token::String(s) => write!(f, "String(\"{}\")", s), + Token::LeftParen => write!(f, "("), + Token::RightParen => write!(f, ")"), + Token::Newline => write!(f, "Newline"), + Token::Eof => write!(f, "EOF"), + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a7fbb8f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,19 @@ +// lib.rs +// Main library file for Noviq. +// Declares all modules and re-exports commonly used items for external use. + +/// Noviq Programming Language +/// +/// A simple, interpreted programming language written in Rust. +/// +/// This library provides the core functionality for the Noviq language, +/// including lexing, parsing, and interpretation. + +pub mod utils; +pub mod frontend; +pub mod runtime; + +// Re-export commonly used items +pub use utils::{get_version, get_package_name, get_package_description}; +pub use frontend::{Parser, Lexer}; +pub use runtime::Interpreter; diff --git a/src/main.rs b/src/main.rs index 5dca37b..88c44f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,85 @@ -fn get_version() -> String { - // Get version from Cargo.toml - let cargo_version = env!("CARGO_PKG_VERSION"); - let base_version = format!("nebula-{}", cargo_version); +// main.rs +// Entry point for the Noviq interpreter binary. +// Handles command-line arguments, reads source files, and invokes the parser/interpreter. + +/// Noviq Programming Language - Main Entry Point +/// +/// This is the initialization file for the Noviq interpreter. +/// The actual implementation is in the library modules. + +use noviq::{get_version, get_package_description, Parser, Interpreter}; +use std::env; +use std::fs; +use std::process; + +fn main() { + let args: Vec = env::args().collect(); + + // If no arguments, show version info + if args.len() == 1 { + println!("Noviq Programming Language"); + println!("Version: {}", get_version()); + println!(); + println!("{}", get_package_description()); + println!(); + println!("Usage: noviq "); + println!(" noviq --version"); + println!(" noviq --help"); + return; + } + + // Handle flags + match args[1].as_str() { + "--version" | "-v" => { + println!("Noviq {}", get_version()); + return; + } + "--help" | "-h" => { + print_help(); + return; + } + _ => {} + } - // Check if SNAPSHOT environment variable is set during build - let is_snapshot = option_env!("SNAPSHOT").is_some(); + // Try to read and execute the file + let filename = &args[1]; - if is_snapshot || cfg!(debug_assertions) { - // Snapshot or Debug build - use snapshot format - let date = chrono::Local::now().format("%y%m%d").to_string(); - format!("{}-pulsar.{}", base_version, date) - } else { - // Release build - use base version - base_version + match fs::read_to_string(filename) { + Ok(contents) => { + if let Err(e) = execute_program(&contents) { + eprintln!("Error: {}", e); + process::exit(1); + } + } + Err(e) => { + eprintln!("Error reading file '{}': {}", filename, e); + process::exit(1); + } } } -fn main() { +fn print_help() { println!("Noviq Programming Language"); println!("Version: {}", get_version()); println!(); - println!("A simple, compiled programming language written in Rust."); + println!("USAGE:"); + println!(" noviq Run a Noviq program"); + println!(" noviq --version Show version information"); + println!(" noviq --help Show this help message"); + println!(); + println!("EXAMPLES:"); + println!(" noviq examples/hello.nvq"); + println!(" noviq my_program.nvq"); +} + +fn execute_program(source: &str) -> Result<(), String> { + // Parse the source code + let mut parser = Parser::new(source); + let statements = parser.parse()?; + + // Execute the statements + let mut interpreter = Interpreter::new(); + interpreter.execute(statements)?; + + Ok(()) } diff --git a/src/runtime/builtins/log.rs b/src/runtime/builtins/log.rs new file mode 100644 index 0000000..25925d1 --- /dev/null +++ b/src/runtime/builtins/log.rs @@ -0,0 +1,30 @@ +// runtime/builtins/log.rs +// Implementation of the log() built-in function. +// Prints values to stdout, separated by spaces. + +use crate::frontend::ast::Expr; +use crate::runtime::value::Value; + +/// Implements the `log` builtin. +/// +/// Evaluates each argument using the provided evaluator and prints them separated by spaces. +pub fn call(args: Vec, eval: &mut F) -> Result +where + F: FnMut(Expr) -> Result, +{ + if args.is_empty() { + println!(); + return Ok(Value::Null); + } + + for (i, arg) in args.into_iter().enumerate() { + let v = eval(arg)?; + if i > 0 { + print!(" "); + } + print!("{}", v); + } + println!(); + + Ok(Value::Null) +} diff --git a/src/runtime/builtins/mod.rs b/src/runtime/builtins/mod.rs new file mode 100644 index 0000000..9529256 --- /dev/null +++ b/src/runtime/builtins/mod.rs @@ -0,0 +1,23 @@ +// runtime/builtins/mod.rs +// Built-in function router. +// Dispatches function calls by name to their respective implementations. + +mod log; + +use crate::frontend::ast::Expr; +use crate::runtime::value::Value; + +pub use log::call as call_log; + +/// Generic entry point for calling builtins by name. +/// +/// `eval` is a mutable closure that evaluates an `Expr` into a `Value`. +pub fn call_builtin(name: &str, args: Vec, eval: &mut F) -> Result +where + F: FnMut(Expr) -> Result, +{ + match name { + "log" => call_log(args, eval), + other => Err(format!("Unknown builtin: {}", other)), + } +} diff --git a/src/runtime/interpreter/expr.rs b/src/runtime/interpreter/expr.rs new file mode 100644 index 0000000..7445b72 --- /dev/null +++ b/src/runtime/interpreter/expr.rs @@ -0,0 +1,32 @@ +// runtime/interpreter/expr.rs +// Expression evaluation logic. +// Evaluates expression AST nodes into runtime values. + +/// Expression evaluation + +use crate::frontend::ast::Expr; +use crate::runtime::value::Value; +use crate::runtime::builtins; + +pub struct ExprEvaluator; + +impl ExprEvaluator { + pub fn new() -> Self { + ExprEvaluator + } + + pub fn evaluate(&mut self, expr: Expr) -> Result { + match expr { + Expr::String(s) => Ok(Value::String(s)), + Expr::Call { name, args } => { + builtins::call_builtin(&name, args, &mut |e| self.evaluate(e)) + } + } + } +} + +impl Default for ExprEvaluator { + fn default() -> Self { + Self::new() + } +} diff --git a/src/runtime/interpreter/mod.rs b/src/runtime/interpreter/mod.rs new file mode 100644 index 0000000..d221623 --- /dev/null +++ b/src/runtime/interpreter/mod.rs @@ -0,0 +1,53 @@ +// runtime/interpreter/mod.rs +// Main interpreter implementation. +// Orchestrates AST execution by coordinating statement and expression evaluation. + +/// Interpreter module +/// +/// Executes Noviq AST + +mod stmt; +mod expr; + +use stmt::StmtExecutor; +use expr::ExprEvaluator; +use crate::frontend::ast::Stmt; + +pub struct Interpreter { + evaluator: ExprEvaluator, +} + +impl Interpreter { + pub fn new() -> Self { + Interpreter { + evaluator: ExprEvaluator::new(), + } + } + + pub fn execute(&mut self, statements: Vec) -> Result<(), String> { + for stmt in statements { + StmtExecutor::execute(stmt, &mut self.evaluator)?; + } + Ok(()) + } +} + +impl Default for Interpreter { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::frontend::Parser; + + #[test] + fn test_execute_function_call() { + let mut parser = Parser::new(r#"log("Hello")"#); + let stmts = parser.parse().unwrap(); + let mut interpreter = Interpreter::new(); + assert!(interpreter.execute(stmts).is_ok()); + } +} diff --git a/src/runtime/interpreter/stmt.rs b/src/runtime/interpreter/stmt.rs new file mode 100644 index 0000000..e549e99 --- /dev/null +++ b/src/runtime/interpreter/stmt.rs @@ -0,0 +1,21 @@ +// runtime/interpreter/stmt.rs +// Statement execution logic. +// Executes statement AST nodes by delegating to expression evaluator. + +/// Statement execution + +use crate::frontend::ast::Stmt; +use super::expr::ExprEvaluator; + +pub struct StmtExecutor; + +impl StmtExecutor { + pub fn execute(stmt: Stmt, evaluator: &mut ExprEvaluator) -> Result<(), String> { + match stmt { + Stmt::Expr(expr) => { + evaluator.evaluate(expr)?; + Ok(()) + } + } + } +} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs new file mode 100644 index 0000000..09f4b45 --- /dev/null +++ b/src/runtime/mod.rs @@ -0,0 +1,14 @@ +// runtime/mod.rs +// Runtime module declaration. +// Exports the interpreter, value types, and built-in functions. + +/// Runtime - Interpreter and Execution +/// +/// This module handles execution of parsed Noviq programs. + +pub mod value; +pub mod builtins; +pub mod interpreter; + +pub use value::Value; +pub use interpreter::Interpreter; diff --git a/src/runtime/value.rs b/src/runtime/value.rs new file mode 100644 index 0000000..04cabf4 --- /dev/null +++ b/src/runtime/value.rs @@ -0,0 +1,22 @@ +// runtime/value.rs +// Runtime value type definitions. +// Defines the types of values that can exist during program execution. + +/// Runtime values for Noviq +/// +/// Represents values that can be computed and manipulated at runtime. + +#[derive(Debug, Clone, PartialEq)] +pub enum Value { + String(String), + Null, +} + +impl std::fmt::Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Value::String(s) => write!(f, "{}", s), + Value::Null => write!(f, "null"), + } + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..bb8b17f --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,14 @@ +// utils/mod.rs +// Utilities module declaration. +// Groups together version information, tests, and other helper functionality. + +/// Utility modules +/// +/// Contains version info, tests, and other utilities. + +pub mod version; + +#[cfg(test)] +pub mod tests; + +pub use version::{get_version, get_package_name, get_package_description}; diff --git a/src/utils/tests.rs b/src/utils/tests.rs new file mode 100644 index 0000000..9a5f342 --- /dev/null +++ b/src/utils/tests.rs @@ -0,0 +1,51 @@ +// utils/tests.rs +// Integration tests for the Noviq interpreter. +// Verifies that example files exist, are readable, and have correct syntax. + +/// Tests for the Noviq interpreter +/// +/// These tests verify that the interpreter can read and process +/// example files correctly. + +#[cfg(test)] +mod tests { + use std::fs; + use std::path::Path; + + #[test] + fn test_example_files_exist() { + assert!(Path::new("examples/hello.nvq").exists(), "hello.nvq should exist"); + assert!(Path::new("examples/test.nvq").exists(), "test.nvq should exist"); + } + + #[test] + fn test_can_read_hello_example() { + let contents = fs::read_to_string("examples/hello.nvq") + .expect("Should be able to read hello.nvq"); + + assert!(contents.contains("Hello, World!"), "Should contain greeting"); + assert!(contents.contains("log"), "Should contain log statements"); + } + + #[test] + fn test_can_read_test_example() { + let contents = fs::read_to_string("examples/test.nvq") + .expect("Should be able to read test.nvq"); + + assert!(contents.contains("log"), "Should contain log statements"); + } + + #[test] + fn test_example_syntax() { + let hello = fs::read_to_string("examples/hello.nvq").unwrap(); + + // Check for comment syntax + assert!(hello.contains("#"), "Should have comments"); + + // Check for string literals + assert!(hello.contains("\""), "Should have string literals"); + + // Check for log function + assert!(hello.contains("log"), "Should have log function calls"); + } +} diff --git a/src/utils/version.rs b/src/utils/version.rs new file mode 100644 index 0000000..d107c6d --- /dev/null +++ b/src/utils/version.rs @@ -0,0 +1,38 @@ +// utils/version.rs +// Version string generation for Noviq. +// Provides functions to get version, package name, and description from Cargo.toml. + +/// Version management for Noviq +/// +/// Handles version string generation based on build type: +/// - Release builds: nebula-X.Y.Z +/// - Debug/Snapshot builds: nebula-X.Y.Z-pulsar.YYMMDD + +/// Get the current version string for Noviq +pub fn get_version() -> String { + // Get version from Cargo.toml + let cargo_version = env!("CARGO_PKG_VERSION"); + let base_version = format!("nebula-{}", cargo_version); + + // Check if SNAPSHOT environment variable is set during build + let is_snapshot = option_env!("SNAPSHOT").is_some(); + + if is_snapshot || cfg!(debug_assertions) { + // Snapshot or Debug build - use snapshot format + let date = chrono::Local::now().format("%y%m%d").to_string(); + format!("{}-pulsar.{}", base_version, date) + } else { + // Release build - use base version + base_version + } +} + +/// Get package name +pub fn get_package_name() -> &'static str { + env!("CARGO_PKG_NAME") +} + +/// Get package description +pub fn get_package_description() -> &'static str { + env!("CARGO_PKG_DESCRIPTION") +}