Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b3a47076d | |||
| e57f229c77 | |||
| cd78d7a42d | |||
| c88aa45a93 | |||
| 7ce030a181 | |||
| e29c9161c8 | |||
| ebab168283 | |||
| dfe1aab2ea | |||
| 15b8ed7690 | |||
| b61005bcaa | |||
| e191173e7b | |||
| f0ffbafee1 | |||
| d16a36a141 | |||
| 22c7614e7c | |||
| af243a8ce8 | |||
| 239c18fece | |||
| 2cc9981215 |
+10
-16
@@ -1,17 +1,17 @@
|
|||||||
# AUTOGENERATED COPYRIGHT HEADER START
|
# AUTOGENERATED COPYRIGHT HEADER START
|
||||||
# Copyright (C) NaN-NaN undefined
|
# Copyright (C) 2024-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
# Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
||||||
# AUTOGENERATED COPYRIGHT HEADER END
|
# AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
|
||||||
# Basic Formatting
|
# Basic Formatting
|
||||||
TabWidth: 4
|
TabWidth: 4
|
||||||
UseTab: ForContinuationAndIndentation
|
UseTab: ForContinuationAndIndentation
|
||||||
ColumnLimit: 65535
|
ColumnLimit: 65535
|
||||||
|
LineEnding: LF
|
||||||
#- 0 does not respect the original line breaks!
|
#- 0 does not respect the original line breaks!
|
||||||
|
|
||||||
# Language
|
# Language
|
||||||
Language: Cpp
|
Language: Cpp
|
||||||
Standard: c++17
|
Standard: c++20
|
||||||
|
|
||||||
# Indentation
|
# Indentation
|
||||||
AccessModifierOffset: 0
|
AccessModifierOffset: 0
|
||||||
@@ -28,18 +28,10 @@ NamespaceIndentation: All
|
|||||||
IncludeCategories:
|
IncludeCategories:
|
||||||
- Regex: '^"warning-disable.hpp"$'
|
- Regex: '^"warning-disable.hpp"$'
|
||||||
Priority: 50
|
Priority: 50
|
||||||
- Regex: '^(<|")(config.hpp|common.hpp|ui-common.hpp|strings.hpp|version.hpp|obs.h)("|>)'
|
|
||||||
Priority: 100
|
|
||||||
- Regex: '^<obs-'
|
|
||||||
Priority: 150
|
|
||||||
- Regex: '^<'
|
- Regex: '^<'
|
||||||
Priority: 200
|
Priority: 200
|
||||||
- Regex: '^<Q'
|
|
||||||
Priority: 250
|
|
||||||
- Regex: '^"'
|
- Regex: '^"'
|
||||||
Priority: 300
|
Priority: 300
|
||||||
- Regex: '.moc"$'
|
|
||||||
Priority: 300
|
|
||||||
- Regex: '^"warning-enable.hpp"$'
|
- Regex: '^"warning-enable.hpp"$'
|
||||||
Priority: 500
|
Priority: 500
|
||||||
SortIncludes: true
|
SortIncludes: true
|
||||||
@@ -50,7 +42,8 @@ AlignConsecutiveAssignments: true
|
|||||||
AlignConsecutiveDeclarations: true
|
AlignConsecutiveDeclarations: true
|
||||||
AlignEscapedNewlines: Left
|
AlignEscapedNewlines: Left
|
||||||
AlignOperands: true
|
AlignOperands: true
|
||||||
AlignTrailingComments: false
|
AlignTrailingComments: true
|
||||||
|
#ArrayInitializerAlignmentStyle: Right
|
||||||
DerivePointerAlignment: false
|
DerivePointerAlignment: false
|
||||||
PointerAlignment: Left
|
PointerAlignment: Left
|
||||||
|
|
||||||
@@ -78,16 +71,17 @@ BraceWrapping:
|
|||||||
SplitEmptyFunction: false
|
SplitEmptyFunction: false
|
||||||
SplitEmptyRecord: false
|
SplitEmptyRecord: false
|
||||||
SplitEmptyNamespace: false
|
SplitEmptyNamespace: false
|
||||||
BinPackArguments: true
|
BinPackArguments: false
|
||||||
BinPackParameters: true
|
BinPackParameters: false
|
||||||
BreakBeforeBinaryOperators: NonAssignment
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
BreakBeforeBraces: Custom
|
BreakBeforeBraces: Custom
|
||||||
BreakBeforeTernaryOperators: true
|
BreakBeforeTernaryOperators: true
|
||||||
BreakConstructorInitializers: BeforeColon
|
BreakConstructorInitializers: BeforeColon
|
||||||
#BreakInheritanceList: BeforeColon
|
#BreakInheritanceList: BeforeColon
|
||||||
BreakStringLiterals: true
|
BreakStringLiterals: false
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
Cpp11BracedListStyle: true
|
Cpp11BracedListStyle: false
|
||||||
|
PackConstructorInitializers: NextLineOnly
|
||||||
|
|
||||||
# Spaces
|
# Spaces
|
||||||
SpaceAfterCStyleCast: false
|
SpaceAfterCStyleCast: false
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# AUTOGENERATED COPYRIGHT HEADER START
|
# AUTOGENERATED COPYRIGHT HEADER START
|
||||||
# Copyright (C) NaN-NaN undefined
|
|
||||||
# Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
# Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
# AUTOGENERATED COPYRIGHT HEADER END
|
# AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# AUTOGENERATED COPYRIGHT HEADER START
|
# AUTOGENERATED COPYRIGHT HEADER START
|
||||||
# Copyright (C) NaN-NaN undefined
|
|
||||||
# Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
# Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
# AUTOGENERATED COPYRIGHT HEADER END
|
# AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
# AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
# Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
# AUTOGENERATED COPYRIGHT HEADER END
|
||||||
# Build Directories
|
# Build Directories
|
||||||
/build
|
/build
|
||||||
/build32
|
/build32
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
[submodule "cmake/cmake-version"]
|
|
||||||
path = cmake/cmake-version
|
|
||||||
url = https://github.com/Xaymar/cmake-version.git
|
|
||||||
[submodule "cmake/cmake-clang"]
|
|
||||||
path = cmake/cmake-clang
|
|
||||||
url = https://github.com/Xaymar/cmake-clang.git
|
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"cmake.configureArgs": [
|
||||||
|
"-C ${workspaceFolder}/cmake/generators/ninja.cmake"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
+16
-148
@@ -1,7 +1,14 @@
|
|||||||
## AUTOGENERATED COPYRIGHT HEADER START
|
# Copyright 2017-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
# Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
#
|
||||||
# AUTOGENERATED COPYRIGHT HEADER END
|
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
cmake_minimum_required(VERSION 3.26...3.29.2 FATAL_ERROR)
|
#
|
||||||
|
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 4.0 FATAL_ERROR)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# CMake Setup
|
# CMake Setup
|
||||||
@@ -12,8 +19,6 @@ list(APPEND CMAKE_MESSAGE_INDENT "[${PROJECT_NAME}] ")
|
|||||||
# Module Search Paths
|
# Module Search Paths
|
||||||
list(APPEND CMAKE_MODULE_PATH
|
list(APPEND CMAKE_MODULE_PATH
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules"
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake-clang"
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake-version"
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake"
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,157 +26,20 @@ list(APPEND CMAKE_MODULE_PATH
|
|||||||
#- Interprocedural optimizations
|
#- Interprocedural optimizations
|
||||||
include("CheckIPOSupported")
|
include("CheckIPOSupported")
|
||||||
|
|
||||||
#- CMake-based Versioning
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
include("version")
|
|
||||||
|
|
||||||
#- CMake-based Clang integration
|
|
||||||
include("clang")
|
|
||||||
|
|
||||||
SET_PROPERTY( GLOBAL PROPERTY USE_FOLDERS ON)
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
# Versioning
|
|
||||||
################################################################################
|
|
||||||
|
|
||||||
version(GENERATE _VERSION COMPRESSED MAJOR 0 MINOR 0 PATCH 0 TWEAK 0 REQUIRE "PATCH;")
|
|
||||||
version(PARSE _VERSION "${_VERSION}" REQUIRE "PATCH;TWEAK")
|
|
||||||
|
|
||||||
# If possible, automatically generate versions from git.
|
|
||||||
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git")
|
|
||||||
find_program(GIT
|
|
||||||
NAMES
|
|
||||||
git
|
|
||||||
git.exe
|
|
||||||
)
|
|
||||||
|
|
||||||
if(EXISTS "${GIT}")
|
|
||||||
# Try and calculate the exist version using git.
|
|
||||||
execute_process(COMMAND "${GIT}" describe --tags --long --abbrev=8 HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} RESULT_VARIABLE GIT_RESULT OUTPUT_VARIABLE GIT_OUTPUT ERROR_VARIABLE GIT_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
|
|
||||||
if((GIT_RESULT EQUAL 0) AND (NOT "${GIT_OUTPUT}" STREQUAL ""))
|
|
||||||
# Result will be MAJOR.MINOR.PATCH-TWEAK-gHASH
|
|
||||||
string(REPLACE "-" ";" GIT_OUTPUT "${GIT_OUTPUT}")
|
|
||||||
string(REPLACE "." ";" GIT_OUTPUT "${GIT_OUTPUT}")
|
|
||||||
|
|
||||||
# Split into components
|
|
||||||
list(GET GIT_OUTPUT 0 GIT_OUTPUT_MAJOR)
|
|
||||||
list(GET GIT_OUTPUT 1 GIT_OUTPUT_MINOR)
|
|
||||||
list(GET GIT_OUTPUT 2 GIT_OUTPUT_PATCH)
|
|
||||||
list(GET GIT_OUTPUT 3 GIT_OUTPUT_TWEAK)
|
|
||||||
list(GET GIT_OUTPUT 4 GIT_OUTPUT_BUILD)
|
|
||||||
|
|
||||||
# Special case: Tag contains prerelease
|
|
||||||
if(GIT_OUTPUT_PATCH MATCHES "([0-9]+)([a-zA-Z]+)([0-9]*)")
|
|
||||||
# Patch requires special parsing.
|
|
||||||
set(GIT_OUTPUT_PATCH "${CMAKE_MATCH_1}")
|
|
||||||
if(CMAKE_MATCH_3 GREATER 0)
|
|
||||||
set(GIT_OUTPUT_PRERELEASE "${CMAKE_MATCH_2}")
|
|
||||||
else()
|
|
||||||
set(GIT_OUTPUT_PRERELEASE "a")
|
|
||||||
endif()
|
|
||||||
MATH(EXPR GIT_OUTPUT_TWEAK "${GIT_OUTPUT_TWEAK} + ${CMAKE_MATCH_3}")
|
|
||||||
|
|
||||||
# Modify the global version
|
|
||||||
version(MODIFY _VERSION "${_VERSION}" COMPRESS
|
|
||||||
MAJOR "${GIT_OUTPUT_MAJOR}"
|
|
||||||
MINOR "${GIT_OUTPUT_MINOR}"
|
|
||||||
PATCH "${GIT_OUTPUT_PATCH}"
|
|
||||||
TWEAK "${GIT_OUTPUT_TWEAK}"
|
|
||||||
BUILD "${GIT_OUTPUT_BUILD}"
|
|
||||||
PRERELEASE "${GIT_OUTPUT_PRERELEASE}"
|
|
||||||
REQUIRE "PATCH;TWEAK"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
# Modify the global version
|
|
||||||
version(MODIFY _VERSION "${_VERSION}" COMPRESS
|
|
||||||
MAJOR "${GIT_OUTPUT_MAJOR}"
|
|
||||||
MINOR "${GIT_OUTPUT_MINOR}"
|
|
||||||
PATCH "${GIT_OUTPUT_PATCH}"
|
|
||||||
TWEAK "${GIT_OUTPUT_TWEAK}"
|
|
||||||
BUILD "${GIT_OUTPUT_BUILD}"
|
|
||||||
PRERELEASE "a"
|
|
||||||
REQUIRE "PATCH;TWEAK"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
execute_process(COMMAND "${GIT}" rev-list --count HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} RESULT_VARIABLE GIT_RESULT OUTPUT_VARIABLE GIT_OUTPUT ERROR_VARIABLE GIT_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
|
|
||||||
if((GIT_RESULT EQUAL 0) AND (NOT "${GIT_OUTPUT}" STREQUAL ""))
|
|
||||||
version(MODIFY _VERSION "${_VERSION}" COMPRESS
|
|
||||||
TWEAK "${GIT_OUTPUT}"
|
|
||||||
PRERELEASE "a"
|
|
||||||
REQUIRE "PATCH;TWEAK"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Determine the build using git.
|
|
||||||
execute_process(COMMAND "${GIT}" log -1 "--pretty=format:g%h" --abbrev=8 HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} RESULT_VARIABLE GIT_RESULT OUTPUT_VARIABLE GIT_OUTPUT ERROR_VARIABLE GIT_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
|
|
||||||
if((GIT_RESULT EQUAL 0) AND (NOT "${GIT_OUTPUT}" STREQUAL ""))
|
|
||||||
version(MODIFY _VERSION "${_VERSION}" COMPRESS
|
|
||||||
BUILD "${GIT_OUTPUT}"
|
|
||||||
REQUIRE "PATCH;TWEAK"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
message(WARNING "Failed to detect build version with 'git'.")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(WARNING "Failed to automatically detect version with 'git'.")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(WARNING "'git' not found, automatic version detection disabled.")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(STATUS "Not a git repository, automatic version detection disabled.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Allow manual overrides of the detected version.
|
|
||||||
if(${PREFIX}VERSION)
|
|
||||||
version(PARSE _VERSION_CFG "${${PREFIX}VERSION}" REQUIRE "PATCH;TWEAK")
|
|
||||||
if("${_VERSION_CFG_BUILD}" STREQUAL "")
|
|
||||||
set(_VERSION_CFG_BUILD "${_VERSION_BUILD}")
|
|
||||||
endif()
|
|
||||||
version(GENERATE _VERSION COMPRESS
|
|
||||||
MAJOR "${_VERSION_CFG_MAJOR}"
|
|
||||||
MINOR "${_VERSION_CFG_MINOR}"
|
|
||||||
PATCH "${_VERSION_CFG_PATCH}"
|
|
||||||
TWEAK "${_VERSION_CFG_TWEAK}"
|
|
||||||
PRERELEASE "${_VERSION_CFG_PRERELEASE}"
|
|
||||||
BUILD "${_VERSION_CFG_BUILD}"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Fix up missing parts of the Version
|
|
||||||
version(PARSE _VERSION "${_VERSION}" REQUIRE "PATCH;TWEAK")
|
|
||||||
|
|
||||||
set(_VERSION_THIN "${_VERSION_MAJOR}.${_VERSION_MINOR}.${_VERSION_PATCH}")
|
|
||||||
if(NOT (_VERSION_PRERELEASE STREQUAL ""))
|
|
||||||
set(_VERSION_THIN "${_VERSION_THIN}${_VERSION_PRERELEASE}${_VERSION_TWEAK}")
|
|
||||||
endif()
|
|
||||||
if(NOT (VERSION_COMMIT STREQUAL ""))
|
|
||||||
set(_VERSION_THIN "${_VERSION_THIN}-${_VERSION_BUILD}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Parse & Log the detected version.
|
|
||||||
message(STATUS "Version ${_VERSION_THIN}")
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Project
|
# Project
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
version(GENERATE PROJECT_VERSION
|
|
||||||
MAJOR "${_VERSION_MAJOR}"
|
|
||||||
MINOR "${_VERSION_MINOR}"
|
|
||||||
PATCH "${_VERSION_PATCH}"
|
|
||||||
TWEAK "${_VERSION_TWEAK}"
|
|
||||||
REQUIRE "PATCH;TWEAK"
|
|
||||||
)
|
|
||||||
project(
|
project(
|
||||||
${PROJECT_NAME}
|
${PROJECT_NAME}
|
||||||
VERSION ${PROJECT_VERSION}
|
|
||||||
)
|
)
|
||||||
set(PROJECT_IDENTIFER "com.xaymar.BlitzLLVM")
|
set(PROJECT_IDENTIFER "com.xaymar.blitzllvm")
|
||||||
set(PROJECT_TITLE "BlitzLLVM")
|
set(PROJECT_TITLE "BlitzLLVM")
|
||||||
set(PROJECT_AUTHORS "See AUTHORS file")
|
set(PROJECT_AUTHORS "Xaymar <info@xaymar.com>") # ToDo: Generate from AUTHORS
|
||||||
set(PROJECT_COPYRIGHT "All Rights Reserved. See LICENSE file for more information")
|
set(PROJECT_COPYRIGHT "All Rights Reserved. See LICENSE file for more information") # ToDo: Generate from LICENSE
|
||||||
set(PROJECT_TRADEMARKS "")
|
set(PROJECT_TRADEMARKS "")
|
||||||
|
|
||||||
function(init_project TARGET)
|
function(init_project TARGET)
|
||||||
@@ -233,4 +101,4 @@ endfunction()
|
|||||||
################################################################################
|
################################################################################
|
||||||
# Sub-projects
|
# Sub-projects
|
||||||
################################################################################
|
################################################################################
|
||||||
ADD_SUBDIRECTORY("code_compiler")
|
add_subdirectory("code_compiler")
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"version": 6,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 4,
|
||||||
|
"minor": 0,
|
||||||
|
"patch": 0
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_INTERPROCEDURAL_OPTIMIZATION": "ON"
|
||||||
|
},
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inherits": "default",
|
||||||
|
"name": "windows-x64-llvm",
|
||||||
|
"description": "Windows, x86-64 (LLVM)",
|
||||||
|
"binaryDir": "build/windows-x64-llvm",
|
||||||
|
"installDir": "distrib/windows-x64-llvm",
|
||||||
|
"toolchainFile": "cmake/toolchains/llvm.cmake",
|
||||||
|
"generator": "Ninja Multi-Config",
|
||||||
|
"cacheVariables": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inherits": "default",
|
||||||
|
"name": "windows-x64-msvc2022",
|
||||||
|
"description": "Windows, x86-64 (MSVC 2022)",
|
||||||
|
"binaryDir": "build/windows-x64-msvc2022",
|
||||||
|
"installDir": "distrib/windows-x64-msvc2022",
|
||||||
|
"generator": "Visual Studio 17 2022",
|
||||||
|
"cacheVariables": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buildPresets": [
|
||||||
|
{
|
||||||
|
"configurePreset": "windows-x64-llvm",
|
||||||
|
"name": "windows-x64-llvm",
|
||||||
|
"description": "",
|
||||||
|
"displayName": "Windows, x64 (LLVM)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"configurePreset": "windows-x64-msvc2022",
|
||||||
|
"name": "windows-x64-msvc2022",
|
||||||
|
"description": "",
|
||||||
|
"displayName": "Windows, x64 (MSVC 2022)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"workflowPresets": [
|
||||||
|
{
|
||||||
|
"name": "windows-x64-llvm",
|
||||||
|
"description": "",
|
||||||
|
"displayName": "",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "configure",
|
||||||
|
"name": "windows-x64-llvm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2014-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
Copyright 2014-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
|||||||
+91
-1
@@ -1,2 +1,92 @@
|
|||||||
== What is BlitzLLVM?
|
== What is BlitzLLVM?
|
||||||
BlitzLLVM is an attempt at a functional transpiler for BlitzBasic (Blitz2D, Blitz3D, and BlitzPlus).
|
BlitzLLVM is an attempt at a BlitzBasic (Blitz2D, Blitz3D, BlitzPlus) compiler using the LLVM compiler backend to enable support for many architectures and platforms. The LLVM backend enables us to take advantage of optimizations and architecture targets that LLVM already provides for other languages, and may even enable further functionality previously considered impossible.
|
||||||
|
|
||||||
|
=== Features
|
||||||
|
++++
|
||||||
|
<table width="100%">
|
||||||
|
<colgroup>
|
||||||
|
<col width="20%">
|
||||||
|
<col width="60%">
|
||||||
|
<col width="10%">
|
||||||
|
<col width="10%">
|
||||||
|
</colgroup>
|
||||||
|
<tr>
|
||||||
|
<th align=left>Feature</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Target Version</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Unicode Support</td>
|
||||||
|
<td>Convert all ASCII functionality to support UTF-8 Unicode instead.</td>
|
||||||
|
<td>1.0</td>
|
||||||
|
<td>❌</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>User Library Handling</td>
|
||||||
|
<td>Allow users to provide their own user libraries to load at runtime.</td>
|
||||||
|
<td>1.0</td>
|
||||||
|
<td>❌</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan=2>Lexer / Tokenizer</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Basic Functionality</td>
|
||||||
|
<td>Handling the necessary basic functionality, like turning text and symbols into tokens</td>
|
||||||
|
<td>0.1</td>
|
||||||
|
<td>✅</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Common Types</td>
|
||||||
|
<td>Lexing for the basic types that BlitzBasic supports: Integers, Reals, Strings.</td>
|
||||||
|
<td>0.1</td>
|
||||||
|
<td>✅</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Location Tracking</td>
|
||||||
|
<td>Properly tracking Line and Character location within a provided file to provide better errors and warnings later on.</td>
|
||||||
|
<td>0.1</td>
|
||||||
|
<td>✅</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Error Handling</td>
|
||||||
|
<td>Throw a proper exception when unexpected behavior occurs, providing necessary information about the error.</td>
|
||||||
|
<td>0.1</td>
|
||||||
|
<td>✅</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan=2>Parser (AST)</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Global Variables</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Local Variables</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Global Functions</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Local Functions</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Math Expressions</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
++++
|
||||||
|
|||||||
Submodule cmake/cmake-clang deleted from 654f2bcc20
Submodule cmake/cmake-version deleted from 3bef96bafa
@@ -0,0 +1,170 @@
|
|||||||
|
# Copyright (C) 2024-2025 Michael Fabian 'Xaymar' Dirks
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# This is a script file that installs the necessary "Ninja" and "Ninja Multi-Config" generators.
|
||||||
|
# Run with `cmake -P cmake/generators/ninja.cmake` from the source directory.
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 4.0 FATAL_ERROR)
|
||||||
|
include_guard(GLOBAL)
|
||||||
|
list(APPEND CMAKE_MESSAGE_INDENT "[Ninja] ")
|
||||||
|
|
||||||
|
# Necessary for propagation into the try_compile CMake subprocesses. It's unclear why this is not the default behavior.
|
||||||
|
foreach(_T IN ITEMS CMAKE_MAKE_PROGRAM)
|
||||||
|
if(DEFINED ENV{${_T}})
|
||||||
|
set(${_T} "$ENV{${_T}}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT CMAKE_MAKE_PROGRAM)
|
||||||
|
# Try and figure out what processor we need to get binaries for.
|
||||||
|
if(CMAKE_HOST_SYSTEM_NAME MATCHES "[Ww]indows")
|
||||||
|
string(TOLOWER "$ENV{PROCESSOR_ARCHITECTURE}" _ARCH)
|
||||||
|
set(_OS "windows")
|
||||||
|
elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "[Dd]arwin")
|
||||||
|
set(_ARCH "multiarch")
|
||||||
|
set(_OS "macos")
|
||||||
|
elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "[Oo]pen[Bb][Ss][Dd]")
|
||||||
|
set(_OS "openbsd")
|
||||||
|
execute_process(
|
||||||
|
COMMAND "arch"
|
||||||
|
OUTPUT_VARIABLE _ARCH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set(_OS "linux")
|
||||||
|
execute_process(
|
||||||
|
COMMAND "uname -p"
|
||||||
|
OUTPUT_VARIABLE _ARCH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE _RES
|
||||||
|
)
|
||||||
|
if(_RES EQUAL 0)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "uname -m"
|
||||||
|
OUTPUT_VARIABLE _ARCH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE _RES
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(NINJA_VERSION "1.12.1")
|
||||||
|
set(NINJA_DIR "${CMAKE_SOURCE_DIR}/extra/ninja-${NINJA_VERSION}-${_OS}-${_ARCH}")
|
||||||
|
|
||||||
|
# Check if Ninja is present and if it is up to date.
|
||||||
|
find_program(NINJA_BIN
|
||||||
|
NAMES
|
||||||
|
ninja
|
||||||
|
HINTS
|
||||||
|
"${NINJA_DIR}/"
|
||||||
|
NO_CACHE NO_DEFAULT_PATH NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_INSTALL_PREFIX NO_CMAKE_FIND_ROOT_PATH
|
||||||
|
)
|
||||||
|
if(IS_EXECUTABLE "${NINJA_BIN}")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${NINJA_BIN} --version
|
||||||
|
OUTPUT_VARIABLE NINJA_VERSION_INSTALLED
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
string(REGEX REPLACE "[\r\n]+" "" NINJA_VERSION_INSTALLED "${NINJA_VERSION_INSTALLED}")
|
||||||
|
if((EXISTS NINJA_BIN) AND (NINJA_VERSION_INSTALLED VERSION_LESS NINJA_VERSION))
|
||||||
|
message(STATUS "Found outdated v${NINJA_VERSION_INSTALLED}.")
|
||||||
|
unset(NINJA_BIN)
|
||||||
|
else()
|
||||||
|
#message(STATUS "Found v${NINJA_VERSION_INSTALLED}.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if((NOT IS_EXECUTABLE "${NINJA_BIN}") OR (NINJA_VERSION_INSTALLED VERSION_LESS NINJA_VERSION))
|
||||||
|
# It isn't up to date or doesn't exist, so try to download the latest version.
|
||||||
|
|
||||||
|
# First we'll have to figure out what we need to download, and from where.
|
||||||
|
if(_OS MATCHES "windows")
|
||||||
|
# Windows and Windows-like
|
||||||
|
SET (_FILE_EXT "zip")
|
||||||
|
if(_ARCH MATCHES "([xX]86)|([aA][mM][dD]64)")
|
||||||
|
# It's x86, x86-64 or amd64
|
||||||
|
SET (_FILE_NAME "ninja-win")
|
||||||
|
else()
|
||||||
|
# Assume ARM or ARM64
|
||||||
|
SET (_FILE_NAME "ninja-winarm64")
|
||||||
|
endif()
|
||||||
|
elseif(_OS MATCHES "macos")
|
||||||
|
# MacOS and MacOS-like
|
||||||
|
SET (_FILE_EXT "zip")
|
||||||
|
SET (_FILE_NAME "ninja-mac")
|
||||||
|
else()
|
||||||
|
# Assume this to be generic Unix-like.
|
||||||
|
SET (_FILE_EXT "tar.xz")
|
||||||
|
if(_ARCH MATCHES "([xX]86)|([aA][mM][dD]64)")
|
||||||
|
# It's x86, x86-64 or amd64
|
||||||
|
SET (_FILE_NAME "ninja-linux")
|
||||||
|
else()
|
||||||
|
# Assume ARM or ARM64
|
||||||
|
SET (_FILE_NAME "ninja-linux-aarch64")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Download the ideal version.
|
||||||
|
if(NOT EXISTS "${NINJA_DIR}.${_FILE_EXT}")
|
||||||
|
message(STATUS "Downloading Ninja v${NINJA_VERSION}...")
|
||||||
|
file(DOWNLOAD
|
||||||
|
"https://github.com/ninja-build/ninja/releases/download/v${NINJA_VERSION}/${_FILE_NAME}.${_FILE_EXT}"
|
||||||
|
"${NINJA_DIR}.${_FILE_EXT}"
|
||||||
|
SHOW_PROGRESS
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "Skipping download as file already exists.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Extract it.
|
||||||
|
message(STATUS "Extracting...")
|
||||||
|
file(ARCHIVE_EXTRACT
|
||||||
|
INPUT "${NINJA_DIR}.${_FILE_EXT}"
|
||||||
|
DESTINATION "${NINJA_DIR}/"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Delete the archive itself.
|
||||||
|
message(STATUS "Cleaning...")
|
||||||
|
#file(REMOVE "${NINJA_DIR}.${_FILE_EXT}")
|
||||||
|
|
||||||
|
# Check if Ninja is present and if it is up to date.
|
||||||
|
message(STATUS "Testing...")
|
||||||
|
find_program(NINJA_BIN
|
||||||
|
NAMES
|
||||||
|
ninja
|
||||||
|
HINTS
|
||||||
|
"${NINJA_DIR}/"
|
||||||
|
NO_CACHE NO_DEFAULT_PATH NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_INSTALL_PREFIX NO_CMAKE_FIND_ROOT_PATH
|
||||||
|
)
|
||||||
|
if(IS_EXECUTABLE "${NINJA_BIN}")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${NINJA_BIN} --version
|
||||||
|
OUTPUT_VARIABLE NINJA_VERSION_INSTALLED
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
string(REGEX REPLACE "[\r\n]+" "" NINJA_VERSION_INSTALLED "${NINJA_VERSION_INSTALLED}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if((NOT IS_EXECUTABLE "${NINJA_BIN}") OR (NINJA_VERSION_INSTALLED VERSION_LESS NINJA_VERSION))
|
||||||
|
message(FATAL_ERROR "Failed to install newer version of Ninja.")
|
||||||
|
elseif(IS_EXECUTABLE "${NINJA_BIN}")
|
||||||
|
message(STATUS "Found v${NINJA_VERSION_INSTALLED}.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_MAKE_PROGRAM "${NINJA_BIN}" CACHE STRING "" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
||||||
@@ -0,0 +1,399 @@
|
|||||||
|
# Copyright (C) 2019-2025 Michael Fabian 'Xaymar' Dirks
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# This is a (mostly) self-contained toolchain file that sets up everything necessary to compile with LLVM/Clang.
|
||||||
|
# cmake --fresh -C cmake/generators/ninja.cmake --preset windows-x64-llvm
|
||||||
|
# !BUG: try_compile downloads the whole thing again, which it shouldn't. Why is CMake not passing the compiler stuff on?
|
||||||
|
|
||||||
|
# Which version of LLVM do we want to have (or newer)?
|
||||||
|
set(LLVM_VERSION "19.1.7")
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 4.0 FATAL_ERROR)
|
||||||
|
include_guard(GLOBAL)
|
||||||
|
list(APPEND CMAKE_MESSAGE_INDENT "[LLVM] ")
|
||||||
|
|
||||||
|
# CMake 3.6: Force variables to be propagated to try_compile sub-processes.
|
||||||
|
list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
||||||
|
LLVM_CLANG
|
||||||
|
LLVM_CLANGPP
|
||||||
|
LLVM_AR
|
||||||
|
LLVM_RANLIB
|
||||||
|
LLVM_NM
|
||||||
|
LLVM_READELF
|
||||||
|
LLVM_OBJCOPY
|
||||||
|
LLVM_OBJDUMP
|
||||||
|
LLVM_STRIP
|
||||||
|
LLVM_DIR
|
||||||
|
CMAKE_CXX_COMPILER
|
||||||
|
CMAKE_C_COMPILER
|
||||||
|
CMAKE_ASM_COMPILER
|
||||||
|
CMAKE_RC_COMPILER
|
||||||
|
CMAKE_LINKER
|
||||||
|
CMAKE_C_COMPILER_LINKER
|
||||||
|
CMAKE_CXX_COMPILER_LINKER
|
||||||
|
CMAKE_RC_COMPILER_LINKER
|
||||||
|
CMAKE_ASM_COMPILER_LINKER
|
||||||
|
CMAKE_NM
|
||||||
|
CMAKE_OBJDUMP
|
||||||
|
CMAKE_OBJCOPY
|
||||||
|
CMAKE_RANLIB
|
||||||
|
CMAKE_C_COMPILER_RANLIB
|
||||||
|
CMAKE_CXX_COMPILER_RANLIB
|
||||||
|
CMAKE_RC_COMPILER_RANLIB
|
||||||
|
CMAKE_ASM_COMPILER_RANLIB
|
||||||
|
CMAKE_AR
|
||||||
|
CMAKE_C_COMPILER_AR
|
||||||
|
CMAKE_CXX_COMPILER_AR
|
||||||
|
CMAKE_RC_COMPILER_AR
|
||||||
|
CMAKE_ASM_COMPILER_AR
|
||||||
|
)
|
||||||
|
set(CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES OFF)
|
||||||
|
|
||||||
|
# Macro for common stuff
|
||||||
|
macro(find_llvm)
|
||||||
|
set(LLVM_FOUND "FALSE")
|
||||||
|
|
||||||
|
# - AR
|
||||||
|
find_program(
|
||||||
|
LLVM_AR
|
||||||
|
NAMES
|
||||||
|
llvm-ar
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
|
||||||
|
# - Library Randomizer
|
||||||
|
find_program(
|
||||||
|
LLVM_RANLIB
|
||||||
|
NAMES
|
||||||
|
llvm-ranlib
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
|
||||||
|
# - Linker
|
||||||
|
if(_ARCH MATCHES "[xX]64")
|
||||||
|
find_program(
|
||||||
|
LLVM_LD
|
||||||
|
NAMES
|
||||||
|
ld64.lld
|
||||||
|
ld.lld
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
find_program(
|
||||||
|
LLVM_LD
|
||||||
|
NAMES
|
||||||
|
ld.lld
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# - Object Copy
|
||||||
|
find_program(
|
||||||
|
LLVM_OBJCOPY
|
||||||
|
NAMES
|
||||||
|
llvm-objcopy
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
|
||||||
|
# - Object Dump
|
||||||
|
find_program(
|
||||||
|
LLVM_OBJDUMP
|
||||||
|
NAMES
|
||||||
|
llvm-objdump
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
|
||||||
|
# - NM
|
||||||
|
find_program(
|
||||||
|
LLVM_NM
|
||||||
|
NAMES
|
||||||
|
llvm-nm
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
|
||||||
|
# - ReadELF
|
||||||
|
find_program(
|
||||||
|
LLVM_READELF
|
||||||
|
NAMES
|
||||||
|
llvm-readelf
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
|
||||||
|
# - Strip Debug Info
|
||||||
|
find_program(
|
||||||
|
LLVM_STRIP
|
||||||
|
NAMES
|
||||||
|
llvm-strip
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
|
||||||
|
# - C Compiler
|
||||||
|
find_program(
|
||||||
|
LLVM_CLANG
|
||||||
|
NAMES
|
||||||
|
clang
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
|
||||||
|
# - C++ Compiler
|
||||||
|
find_program(
|
||||||
|
LLVM_CLANGPP
|
||||||
|
NAMES
|
||||||
|
clang++
|
||||||
|
PATHS
|
||||||
|
"${LLVM_DIR}/bin/"
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
|
||||||
|
set(LLVM_FOUND TRUE)
|
||||||
|
foreach(_TEST IN ITEMS
|
||||||
|
"LLVM_AR"
|
||||||
|
"LLVM_LD"
|
||||||
|
"LLVM_CLANG"
|
||||||
|
"LLVM_CLANGPP")
|
||||||
|
if(NOT IS_EXECUTABLE ${${_TEST}})
|
||||||
|
set(LLVM_FOUND FALSE)
|
||||||
|
break()
|
||||||
|
else()
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${${_TEST}}" --version
|
||||||
|
OUTPUT_VARIABLE ${_TEST}_VERSION
|
||||||
|
RESULT_VARIABLE _RES
|
||||||
|
)
|
||||||
|
if(NOT _RES EQUAL 0)
|
||||||
|
set(LLVM_FOUND FALSE)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(REGEX MATCH "[1-9+]?[0-9+]\.[1-9+]?[0-9+]\.[1-9+]?[0-9+]\." "${_TEST}_VERSION" "${${_TEST}_VERSION}")
|
||||||
|
string(REGEX REPLACE "[\r\n]+" "" "${_TEST}_VERSION" "${${_TEST}_VERSION}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
if(NOT CMAKE_C_COMPILER OR NOT CMAKE_CXX_COMPILER OR NOT CMAKE_LINKER OR NOT CMAKE_AR)
|
||||||
|
# Try and figure out what processor we need to get binaries for.
|
||||||
|
if(CMAKE_HOST_SYSTEM_NAME MATCHES "[Ww]indows")
|
||||||
|
set(_OS "windows")
|
||||||
|
string(TOLOWER "$ENV{PROCESSOR_ARCHITECTURE}" _ARCH)
|
||||||
|
if(_ARCH MATCHES "(amd64)|(AMD64)")
|
||||||
|
set(_FILE_NAME "LLVM-${LLVM_VERSION}-win64")
|
||||||
|
elseif(_ARCH MATCHES "(x86)|(X86)")
|
||||||
|
set(_FILE_NAME "LLVM-${LLVM_VERSION}-win32")
|
||||||
|
else()
|
||||||
|
set(_FILE_NAME "LLVM-${LLVM_VERSION}-woa64")
|
||||||
|
endif()
|
||||||
|
set(_FILE_EXT "exe")
|
||||||
|
|
||||||
|
#Computer\HKEY_CURRENT_USER\SOFTWARE\7-Zip\Path64
|
||||||
|
cmake_host_system_information(
|
||||||
|
RESULT 7ZIP_DIR
|
||||||
|
QUERY WINDOWS_REGISTRY "HKCU/SOFTWARE/7-Zip" VALUE "Path64"
|
||||||
|
VIEW HOST
|
||||||
|
)
|
||||||
|
find_program(7ZIP_BIN
|
||||||
|
NAMES
|
||||||
|
7z
|
||||||
|
7za
|
||||||
|
HINTS
|
||||||
|
"${7ZIP_DIR}"
|
||||||
|
"C:/Program Files/7-Zip"
|
||||||
|
"C:/Program Files (x86)/7-Zip"
|
||||||
|
)
|
||||||
|
elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "[Dd]arwin")
|
||||||
|
set(_OS "macos")
|
||||||
|
execute_process(
|
||||||
|
COMMAND "uname -m"
|
||||||
|
OUTPUT_VARIABLE _ARCH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE _RES
|
||||||
|
)
|
||||||
|
if(_ARCH MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
|
||||||
|
set(_FILE_NAME "LLVM-${LLVM_VERSION}-MacOS-X64")
|
||||||
|
else()
|
||||||
|
set(_FILE_NAME "LLVM-${LLVM_VERSION}-MacOS-ARM64")
|
||||||
|
endif()
|
||||||
|
set(_FILE_EXT "tar.xz")
|
||||||
|
elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "[Oo]pen[Bb][Ss][Dd]")
|
||||||
|
set(_OS "openbsd")
|
||||||
|
execute_process(
|
||||||
|
COMMAND "arch"
|
||||||
|
OUTPUT_VARIABLE _ARCH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
set(_FILE_EXT "tar.xz")
|
||||||
|
if(_ARCH MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
|
||||||
|
set(_FILE_NAME "LLVM-${LLVM_VERSION}-Linux-X64")
|
||||||
|
else()
|
||||||
|
set(_FILE_NAME "LLVM-${LLVM_VERSION}-Linux-ARM64")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(_OS "linux")
|
||||||
|
execute_process(
|
||||||
|
COMMAND "uname -p"
|
||||||
|
OUTPUT_VARIABLE _ARCH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE _RES
|
||||||
|
)
|
||||||
|
if(_RES EQUAL 0)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "uname -m"
|
||||||
|
OUTPUT_VARIABLE _ARCH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE _RES
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
set(_FILE_EXT "tar.xz")
|
||||||
|
if(_ARCH MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
|
||||||
|
set(_FILE_NAME "LLVM-${LLVM_VERSION}-Linux-X64")
|
||||||
|
else()
|
||||||
|
set(_FILE_NAME "LLVM-${LLVM_VERSION}-Linux-ARM64")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
set(LLVM_DIR "${CMAKE_SOURCE_DIR}/extra/llvm-${LLVM_VERSION}-${_OS}-${_ARCH}")
|
||||||
|
|
||||||
|
# Try and find an existing LLVM installation.
|
||||||
|
find_llvm()
|
||||||
|
if(LLVM_CLANG_VERSION AND LLVM_CLANG_VERSION VERSION_LESS LLVM_VERSION)
|
||||||
|
message(STATUS "Found outdated v${LLVM_CLANG_VERSION}.")
|
||||||
|
elseif(NOT LLVM_FOUND)
|
||||||
|
message(STATUS "No installed LLVM found.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if((NOT LLVM_FOUND) OR (LLVM_CLANG_VERSION VERSION_LESS LLVM_VERSION))
|
||||||
|
# It isn't up to date or doesn't exist, so try to download the latest version.
|
||||||
|
if((_FILE_EXT MATCHES "exe") AND NOT 7ZIP_BIN)
|
||||||
|
message(FATAL_ERROR "7-Zip is required to continue setting up LLVM. Please provide '7z.exe' in PATH or by installing the latest version from https://www.7-zip.org/.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Download the ideal version.
|
||||||
|
if(NOT EXISTS "${LLVM_DIR}.${_FILE_EXT}")
|
||||||
|
message(STATUS "Downloading LLVM v${LLVM_VERSION}...")
|
||||||
|
file(DOWNLOAD
|
||||||
|
"https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/${_FILE_NAME}.${_FILE_EXT}"
|
||||||
|
"${LLVM_DIR}.${_FILE_EXT}"
|
||||||
|
SHOW_PROGRESS
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "Skipping download as file already exists.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Extract it.
|
||||||
|
message(STATUS "Extracting...")
|
||||||
|
if(_FILE_EXT MATCHES "exe")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${7ZIP_BIN} x -y -aoa "-o${LLVM_DIR}/" "${LLVM_DIR}.${_FILE_EXT}"
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
OUTPUT_QUIET
|
||||||
|
ERROR_QUIET
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
file(ARCHIVE_EXTRACT
|
||||||
|
INPUT "${LLVM_DIR}.${_FILE_EXT}"
|
||||||
|
DESTINATION "${LLVM_DIR}/"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Delete the archive itself.
|
||||||
|
message(STATUS "Cleaning...")
|
||||||
|
#file(REMOVE "${LLVM_DIR}.${_FILE_EXT}")
|
||||||
|
|
||||||
|
# Final stuff
|
||||||
|
#message(STATUS "Testing...")
|
||||||
|
find_llvm()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(LLVM_FOUND AND (LLVM_CLANG_VERSION VERSION_GREATER_EQUAL LLVM_VERSION))
|
||||||
|
message(STATUS "Found v${LLVM_CLANG_VERSION}.")
|
||||||
|
|
||||||
|
foreach(_T IN ITEMS CMAKE_AR CMAKE_C_COMPILER_AR CMAKE_CXX_COMPILER_AR CMAKE_RC_COMPILER_AR CMAKE_ASM_COMPILER_AR)
|
||||||
|
set(${_T} "${LLVM_AR}")
|
||||||
|
#set(${_T} "${LLVM_AR}" PARENT_SCOPE)
|
||||||
|
set(${_T} "${LLVM_AR}" CACHE STRING "" FORCE)
|
||||||
|
set(ENV{${_T}} "${LLVM_AR}")
|
||||||
|
endforeach()
|
||||||
|
foreach(_T IN ITEMS CMAKE_RANLIB CMAKE_C_COMPILER_RANLIB CMAKE_CXX_COMPILER_RANLIB CMAKE_RC_COMPILER_RANLIB CMAKE_ASM_COMPILER_RANLIB)
|
||||||
|
set(${_T} "${LLVM_RANLIB}")
|
||||||
|
#set(${_T} "${LLVM_RANLIB}" PARENT_SCOPE)
|
||||||
|
set(${_T} "${LLVM_RANLIB}" CACHE STRING "" FORCE)
|
||||||
|
set(ENV{${_T}} "${LLVM_RANLIB}")
|
||||||
|
endforeach()
|
||||||
|
foreach(_T IN ITEMS CMAKE_OBJCOPY)
|
||||||
|
set(${_T} "${LLVM_OBJCOPY}")
|
||||||
|
#set(${_T} "${LLVM_OBJCOPY}" PARENT_SCOPE)
|
||||||
|
set(${_T} "${LLVM_OBJCOPY}" CACHE STRING "" FORCE)
|
||||||
|
set(ENV{${_T}} "${LLVM_OBJCOPY}")
|
||||||
|
endforeach()
|
||||||
|
foreach(_T IN ITEMS CMAKE_OBJDUMP)
|
||||||
|
set(${_T} "${LLVM_OBJDUMP}")
|
||||||
|
#set(${_T} "${LLVM_OBJDUMP}" PARENT_SCOPE)
|
||||||
|
set(${_T} "${LLVM_OBJDUMP}" CACHE STRING "" FORCE)
|
||||||
|
set(ENV{${_T}} "${LLVM_OBJDUMP}")
|
||||||
|
endforeach()
|
||||||
|
foreach(_T IN ITEMS CMAKE_NM)
|
||||||
|
set(${_T} "${LLVM_NM}")
|
||||||
|
#set(${_T} "${LLVM_NM}" PARENT_SCOPE)
|
||||||
|
set(${_T} "${LLVM_NM}" CACHE STRING "" FORCE)
|
||||||
|
set(ENV{${_T}} "${LLVM_NM}")
|
||||||
|
endforeach()
|
||||||
|
foreach(_T IN ITEMS CMAKE_LINKER CMAKE_C_COMPILER_LINKER CMAKE_CXX_COMPILER_LINKER CMAKE_RC_COMPILER_LINKER CMAKE_ASM_COMPILER_LINKER)
|
||||||
|
set(${_T} "${LLVM_LD}")
|
||||||
|
#set(${_T} "${LLVM_LD}" PARENT_SCOPE)
|
||||||
|
set(${_T} "${LLVM_LD}" CACHE STRING "" FORCE)
|
||||||
|
set(ENV{${_T}} "${LLVM_LD}")
|
||||||
|
endforeach()
|
||||||
|
foreach(_T IN ITEMS CMAKE_C_COMPILER CMAKE_ASM_COMPILER CMAKE_RC_COMPILER)
|
||||||
|
set(${_T} "${LLVM_CLANG}")
|
||||||
|
#set(${_T} "${LLVM_CLANG}" PARENT_SCOPE)
|
||||||
|
set(${_T} "${LLVM_CLANG}" CACHE STRING "" FORCE)
|
||||||
|
set(ENV{${_T}} "${LLVM_CLANG}")
|
||||||
|
endforeach()
|
||||||
|
foreach(_T IN ITEMS CMAKE_CXX_COMPILER)
|
||||||
|
set(${_T} "${LLVM_CLANGPP}")
|
||||||
|
#set(${_T} "${LLVM_CLANGPP}" PARENT_SCOPE)
|
||||||
|
set(${_T} "${LLVM_CLANGPP}" CACHE STRING "" FORCE)
|
||||||
|
set(ENV{${_T}} "${LLVM_CLANGPP}")
|
||||||
|
endforeach()
|
||||||
|
foreach(_T IN ITEMS LLVM_CLANG LLVM_CLANGPP LLVM_AR LLVM_RANLIB LLVM_NM LLVM_READELF LLVM_OBJCOPY LLVM_OBJDUMP LLVM_STRIP LLVM_DIR)
|
||||||
|
set(${_T} "${${_T}}" CACHE STRING "" FORCE)
|
||||||
|
set(ENV${_T} "${${_T}}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Failed to find or provide a compatible LLVM installation.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
## AUTOGENERATED COPYRIGHT HEADER START
|
# AUTOGENERATED COPYRIGHT HEADER START
|
||||||
# Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
# Copyright (C) 2017-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
# AUTOGENERATED COPYRIGHT HEADER END
|
# AUTOGENERATED COPYRIGHT HEADER END
|
||||||
project(code_compiler
|
project(compiler)
|
||||||
VERSION ${PROJECT_VERSION}
|
|
||||||
)
|
|
||||||
add_executable(${PROJECT_NAME})
|
add_executable(${PROJECT_NAME})
|
||||||
init_project(${PROJECT_NAME})
|
init_project(${PROJECT_NAME})
|
||||||
|
|
||||||
@@ -15,16 +13,14 @@ target_sources(${PROJECT_NAME} PRIVATE
|
|||||||
"source/error.cpp"
|
"source/error.cpp"
|
||||||
"source/parser.hpp"
|
"source/parser.hpp"
|
||||||
"source/parser.cpp"
|
"source/parser.cpp"
|
||||||
|
"source/util.hpp"
|
||||||
|
"source/util.cpp"
|
||||||
|
"source/types.hpp"
|
||||||
|
"source/types.cpp"
|
||||||
"source/compiler.hpp"
|
"source/compiler.hpp"
|
||||||
"source/compiler.cpp"
|
"source/compiler.cpp"
|
||||||
"source/ast/ast.hpp"
|
"source/ast/ast.hpp"
|
||||||
"source/ast/ast.cpp"
|
"source/ast/ast.cpp"
|
||||||
"source/ast/arithmetic.hpp"
|
|
||||||
"source/ast/arithmetic.cpp"
|
|
||||||
"source/ast/function.hpp"
|
|
||||||
"source/ast/function.cpp"
|
|
||||||
"source/ast/value.hpp"
|
|
||||||
"source/ast/value.cpp"
|
|
||||||
)
|
)
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||||
"${PROJECT_SOURCE_DIR}/source"
|
"${PROJECT_SOURCE_DIR}/source"
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
|
||||||
// Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
|
||||||
#include "arithmetic.hpp"
|
|
||||||
|
|
||||||
blitz::ast::arithmetic_expression::arithmetic_expression(expression_operator op, std::unique_ptr<expression> left, std::unique_ptr<expression> right)
|
|
||||||
: m_operator(op), m_left(std::move(left)), m_right(std::move(right)) {}
|
|
||||||
|
|
||||||
blitz::ast::arithmetic_expression::~arithmetic_expression() {}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
|
||||||
// Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
|
||||||
#pragma once
|
|
||||||
#include "ast.hpp"
|
|
||||||
#include "value.hpp"
|
|
||||||
|
|
||||||
namespace blitz {
|
|
||||||
namespace ast {
|
|
||||||
enum class expression_operator : int8_t {
|
|
||||||
Add, /*+*/
|
|
||||||
Subtract, /*-*/
|
|
||||||
Multiply, /***/
|
|
||||||
Divide, /*/*/
|
|
||||||
Invert, /*~*/
|
|
||||||
Power, /*^*/
|
|
||||||
Equal, /*=*/
|
|
||||||
};
|
|
||||||
|
|
||||||
class arithmetic_expression : public expression {
|
|
||||||
public:
|
|
||||||
arithmetic_expression(expression_operator op, std::unique_ptr<expression> left, std::unique_ptr<expression> right);
|
|
||||||
virtual ~arithmetic_expression();
|
|
||||||
|
|
||||||
private:
|
|
||||||
expression_operator m_operator;
|
|
||||||
std::unique_ptr<expression> m_left, m_right;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +1,352 @@
|
|||||||
// AUTOGENERATED COPYRIGHT HEADER START
|
// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
// Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2017-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
#include "ast.hpp"
|
#include "ast.hpp"
|
||||||
#include <cstdlib>
|
#include "util.hpp"
|
||||||
|
|
||||||
|
blitz::ast::variable::~variable()
|
||||||
blitz::ast::value_expression::value_expression(blitz::token token) : expression(token) {}
|
|
||||||
|
|
||||||
blitz::ast::integer_expression::integer_expression(blitz::token token) : value_expression(token), _value(0)
|
|
||||||
{
|
{
|
||||||
if (_token.text.length() > 0) {
|
/* Variable Parsing
|
||||||
_value = atol(_token.text.c_str());
|
*
|
||||||
|
* Declaration:
|
||||||
|
* - 8bit Signed Integer Variable
|
||||||
|
* Variable:Byte
|
||||||
|
* Variable:Int8
|
||||||
|
* - 8bit Unsigned Integer Variable
|
||||||
|
* Variable:UByte
|
||||||
|
* Variable:UInt8
|
||||||
|
* - 16bit Signed Integer Variable
|
||||||
|
* Variable:Short
|
||||||
|
* Variable:Int16
|
||||||
|
* - 16bit Unsigned Integer Variable
|
||||||
|
* Variable:UShort
|
||||||
|
* Variable:UInt16
|
||||||
|
* - 32bit Signed Integer Variable
|
||||||
|
* Variable
|
||||||
|
* Variable%
|
||||||
|
* Variable:Int
|
||||||
|
* Variable:Int32
|
||||||
|
* - 32bit Unsigned Integer Variable
|
||||||
|
* Variable:UInt
|
||||||
|
* Variable:UInt32
|
||||||
|
* - 64bit Signed Integer Variable
|
||||||
|
* Variable%%
|
||||||
|
* Variable:Long
|
||||||
|
* Variable:Int64
|
||||||
|
* - 64bit Unsigned Integer Variable
|
||||||
|
* Variable:ULong
|
||||||
|
* Variable:UInt64
|
||||||
|
* - 32bit Real Variable
|
||||||
|
* Variable#
|
||||||
|
* Variable:Float
|
||||||
|
* Variable:Float32
|
||||||
|
* Variable:Real
|
||||||
|
* Variable:Real32
|
||||||
|
* - 64bit Real Variable
|
||||||
|
* Variable##
|
||||||
|
* Variable:Double
|
||||||
|
* Variable:Float64
|
||||||
|
* Variable:Real64
|
||||||
|
* - UTF-8 String Variable
|
||||||
|
* Variable$
|
||||||
|
* Variable:String
|
||||||
|
* - Struct Variable
|
||||||
|
* Variable.StructName
|
||||||
|
* Variable:StructName
|
||||||
|
*
|
||||||
|
* Access:
|
||||||
|
* - Struct Access:
|
||||||
|
* Variable\Key
|
||||||
|
* - Array Access:
|
||||||
|
* Variable[IntegerIndex]
|
||||||
|
* - Dynamic Array Access:
|
||||||
|
* Variable(IntegerIndex)
|
||||||
|
* - Direct Access:
|
||||||
|
* Variable
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blitz::ast::variable::can_parse(std::shared_ptr<blitz::lexer> lexer)
|
||||||
|
{
|
||||||
|
return lexer->current().type == blitz::token::variant::TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<blitz::ast::node> blitz::ast::variable::try_parse(std::shared_ptr<blitz::lexer> lexer)
|
||||||
|
{
|
||||||
|
auto file = lexer->file();
|
||||||
|
|
||||||
|
auto name_tk = lexer->current();
|
||||||
|
if (name_tk.type != blitz::token::variant::TEXT) {
|
||||||
|
throw blitz::error(file, name_tk.location, name_tk.location, blitz::format("Unexpected %s, expected text.", name_tk.to_string().c_str()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
blitz::ast::real_expression::real_expression(blitz::token token) : value_expression(token), _value(0.0f)
|
auto node = std::make_shared<blitz::ast::variable>();
|
||||||
{
|
node->tokens.push_back(name_tk);
|
||||||
if (_token.text.length() > 0) {
|
node->type = blitz::types::type::UNKNOWN;
|
||||||
_value = atof(_token.text.c_str());
|
node->name = name_tk.text;
|
||||||
|
|
||||||
|
// Check if this has a type definition
|
||||||
|
auto symbol_tk = lexer->peek();
|
||||||
|
if (symbol_tk.type != blitz::token::variant::SYMBOL) {
|
||||||
|
return node;
|
||||||
|
} else if (symbol_tk.location.second != (name_tk.location.second + name_tk.text.length())) {
|
||||||
|
// We only care about these if they're immediately attached to us.
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
if (symbol_tk.text == ":") {
|
||||||
|
// :Type
|
||||||
|
node->tokens.push_back(lexer->next()); // Advance to next token.
|
||||||
|
auto type_tk = lexer->next();
|
||||||
|
if (type_tk != blitz::token::variant::TEXT) {
|
||||||
|
throw blitz::error(file, name_tk.location, type_tk.location, blitz::format("Unexpected %s, expected text.", type_tk.to_string().c_str()));
|
||||||
|
}
|
||||||
|
if (type_tk.location.second != (symbol_tk.location.second + 1)) {
|
||||||
|
throw blitz::error(file, name_tk.location, type_tk.location, blitz::format("Unexpected white space, expected text."));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto type = blitz::types::from_string(type_tk.text);
|
||||||
|
if (type == blitz::types::type::UNKNOWN) {
|
||||||
|
throw blitz::error(file, name_tk.location, type_tk.location, blitz::format("Unexpected %s, expected built-in type name.", type_tk.text.c_str()));
|
||||||
|
}
|
||||||
|
node->tokens.push_back(type_tk);
|
||||||
|
|
||||||
|
node->type = type;
|
||||||
|
} else if (symbol_tk.text == ".") {
|
||||||
|
// .Struct
|
||||||
|
node->tokens.push_back(lexer->next()); // Advance to next token.
|
||||||
|
auto type_tk = lexer->next();
|
||||||
|
if (type_tk != blitz::token::variant::TEXT) {
|
||||||
|
throw blitz::error(file, name_tk.location, type_tk.location, blitz::format("Unexpected %s, expected text.", type_tk.to_string().c_str()));
|
||||||
|
}
|
||||||
|
if (type_tk.location.second != (symbol_tk.location.second + 1)) {
|
||||||
|
throw blitz::error(file, name_tk.location, type_tk.location, blitz::format("Unexpected white space, expected text."));
|
||||||
|
}
|
||||||
|
node->tokens.push_back(type_tk);
|
||||||
|
|
||||||
|
node->type = blitz::types::type::STRUCT;
|
||||||
|
node->struct_name = type_tk.text;
|
||||||
|
} else if (symbol_tk.text == "%") {
|
||||||
|
// Int32
|
||||||
|
node->tokens.push_back(lexer->next()); // Advance to next token.
|
||||||
|
node->type = blitz::types::type::INT32;
|
||||||
|
} else if (symbol_tk.text == "#") {
|
||||||
|
// Float
|
||||||
|
node->tokens.push_back(lexer->next()); // Advance to next token.
|
||||||
|
node->type = blitz::types::type::FLOAT32;
|
||||||
|
} else if (symbol_tk.text == "$") {
|
||||||
|
// String
|
||||||
|
node->tokens.push_back(lexer->next()); // Advance to next token.
|
||||||
|
node->type = blitz::types::type::STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
blitz::ast::string_expression::string_expression(blitz::token token) : value_expression(token), _value()
|
blitz::ast::value::~value() {}
|
||||||
|
|
||||||
|
bool blitz::ast::value::can_parse(std::shared_ptr<blitz::lexer> lexer)
|
||||||
{
|
{
|
||||||
_value = _token.text;
|
auto tk = lexer->current();
|
||||||
|
switch (tk.type) {
|
||||||
|
case blitz::token::variant::STRING:
|
||||||
|
case blitz::token::variant::REAL:
|
||||||
|
case blitz::token::variant::INTEGER:
|
||||||
|
return true;
|
||||||
|
case blitz::token::variant::TEXT: {
|
||||||
|
// We can only parse True, False, Null
|
||||||
|
std::string text = tk.text;
|
||||||
|
std::transform(text.cbegin(), text.cend(), text.begin(), blitz::utility::utf8_safe_tolower);
|
||||||
|
if (text == "false") {
|
||||||
|
return true;
|
||||||
|
} else if (text == "true") {
|
||||||
|
return true;
|
||||||
|
} else if (text == "null") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
blitz::ast::variable_expression::variable_expression(blitz::token token) : expression(token) {}
|
std::shared_ptr<blitz::ast::node> blitz::ast::value::try_parse(std::shared_ptr<blitz::lexer> lexer)
|
||||||
|
{
|
||||||
|
auto file = lexer->file();
|
||||||
|
auto tk = lexer->current();
|
||||||
|
|
||||||
|
auto node = std::make_shared<blitz::ast::value>();
|
||||||
|
node->type = variant::UNKNOWN;
|
||||||
|
|
||||||
|
if (tk.type == blitz::token::variant::STRING) {
|
||||||
|
node->type = variant::STRING;
|
||||||
|
node->text = tk.text;
|
||||||
|
return node;
|
||||||
|
} else if (tk.type == blitz::token::variant::REAL) {
|
||||||
|
node->type = variant::REAL;
|
||||||
|
node->number.f = strtod(tk.text.c_str(), nullptr);
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
throw blitz::error(file, tk.location, tk.location, blitz::format("Real '%s' is not representable on this system.", tk.text.c_str()));
|
||||||
|
}
|
||||||
|
} else if (tk.type == blitz::token::variant::INTEGER) {
|
||||||
|
// Figure out which base this integer is in (and where it starts).
|
||||||
|
int base = 10;
|
||||||
|
const char* text = tk.text.c_str();
|
||||||
|
if ((tk.text.length() > 1) && (text[0] == '0')) {
|
||||||
|
if (text[1] == 'x') { // Base 16
|
||||||
|
base = 16;
|
||||||
|
text = text += 2;
|
||||||
|
} else if (text[1] == 'b') { // Base 2
|
||||||
|
base = 2;
|
||||||
|
text = text += 2;
|
||||||
|
} else if (isdigit(text[1])) {
|
||||||
|
base = 8;
|
||||||
|
text = text += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tk.text[tk.text.length() - 1] == 'u') {
|
||||||
|
// User wants this to be unsigned.
|
||||||
|
node->type = variant::UNSIGNED_INTEGER;
|
||||||
|
node->number.ui = strtoull(text, nullptr, base);
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
throw blitz::error(file, tk.location, tk.location, blitz::format("Integer '%s' is not representable on this system.", tk.text.c_str()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Try and figure out if it is unsigned.
|
||||||
|
node->number.i = strtoll(text, nullptr, base);
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
node->type = variant::UNSIGNED_INTEGER;
|
||||||
|
node->number.ui = strtoull(text, nullptr, base);
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
throw blitz::error(file, tk.location, tk.location, blitz::format("Integer '%s' is not representable on this system.", tk.text.c_str()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node->type = variant::INTEGER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (tk.type == blitz::token::variant::TEXT) {
|
||||||
|
std::string text = tk.text;
|
||||||
|
std::transform(text.cbegin(), text.cend(), text.begin(), blitz::utility::utf8_safe_tolower);
|
||||||
|
if (text == "false") {
|
||||||
|
node->type = variant::BOOL;
|
||||||
|
node->number.b = false;
|
||||||
|
} else if (text == "true") {
|
||||||
|
node->type = variant::BOOL;
|
||||||
|
node->number.b = true;
|
||||||
|
} else if (text == "null") {
|
||||||
|
node->type = variant::NULL;
|
||||||
|
node->number.ui = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
blitz::ast::declare::~declare()
|
||||||
|
{
|
||||||
|
/* Variable Declaration
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* - Local myVar1, myVar2%, myVar3 = "Help", myVar4$ = "Me"
|
||||||
|
* - Global myVar2g# = 3.147
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blitz::ast::declare::can_parse(std::shared_ptr<blitz::lexer> lexer)
|
||||||
|
{
|
||||||
|
auto tk = lexer->current();
|
||||||
|
if (tk != blitz::token::variant::TEXT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string text = tk.text;
|
||||||
|
std::transform(text.cbegin(), text.cend(), text.begin(), blitz::utility::utf8_safe_tolower);
|
||||||
|
if (text == "local") {
|
||||||
|
return true;
|
||||||
|
} else if (text == "global") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<blitz::ast::node> blitz::ast::declare::try_parse(std::shared_ptr<blitz::lexer> lexer)
|
||||||
|
{
|
||||||
|
bool is_global;
|
||||||
|
|
||||||
|
auto file = lexer->file();
|
||||||
|
auto tk = lexer->current();
|
||||||
|
if (tk != blitz::token::variant::TEXT) {
|
||||||
|
throw blitz::error(file, tk.location, tk.location, blitz::format("Unexpected %s, expected text.", tk.to_string().c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is Local or Global.
|
||||||
|
std::string text = tk.text;
|
||||||
|
std::transform(text.cbegin(), text.cend(), text.begin(), blitz::utility::utf8_safe_tolower);
|
||||||
|
if (text == "local") {
|
||||||
|
is_global = false;
|
||||||
|
} else if (text == "global") {
|
||||||
|
is_global = true;
|
||||||
|
} else {
|
||||||
|
throw blitz::error(file, tk.location, tk.location, blitz::format("Unexpected %s, expected Local or Global.", tk.to_string().c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto node = std::make_shared<blitz::ast::declare>();
|
||||||
|
node->global = is_global;
|
||||||
|
|
||||||
|
// Local myVar
|
||||||
|
// Local myVar, myVar2
|
||||||
|
// Local myVar = Expression
|
||||||
|
// Local myVar = Expression, myVar2
|
||||||
|
// Local myVar = Expression :
|
||||||
|
// Local myVar : Local MyVar
|
||||||
|
// Local myVar:Int
|
||||||
|
// Local myVar.StructType
|
||||||
|
|
||||||
|
for (tk = lexer->peek(); tk != blitz::token::variant::ENDOFFILE; tk = lexer->peek()) {
|
||||||
|
// Declarations require a valid variable name
|
||||||
|
if (tk.type != blitz::token::variant::TEXT) {
|
||||||
|
throw blitz::error(file, tk.location, tk.location, blitz::format("Expected variable name, got %s.", tk.to_string().c_str()));
|
||||||
|
} else if (!blitz::ast::variable::can_parse(lexer)) {
|
||||||
|
throw blitz::error(file, tk.location, tk.location, blitz::format("Expected variable name, got %s.", tk.to_string().c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance the lexer and parse the variable declaration.
|
||||||
|
tk = lexer->next();
|
||||||
|
auto variable_nd = blitz::ast::variable::try_parse(lexer);
|
||||||
|
node->nodes.push_back(variable_nd);
|
||||||
|
|
||||||
|
// Peek at what's coming up and decide on behavior.
|
||||||
|
tk = lexer->peek();
|
||||||
|
if ((tk.type == blitz::token::variant::NEWLINE) || ((tk.type == blitz::token::variant::SYMBOL) && (tk.text == ":")) || (tk.type == blitz::token::variant::ENDOFFILE)) {
|
||||||
|
// Nothing useful, break out here.
|
||||||
|
break;
|
||||||
|
} else if ((tk.type == blitz::token::variant::SYMBOL) && (tk.text == ",")) {
|
||||||
|
// Next variable is being declared.
|
||||||
|
lexer->next();
|
||||||
|
continue;
|
||||||
|
} else if ((tk.type == blitz::token::variant::SYMBOL) && (tk.text == "=")) {
|
||||||
|
// Assignment, not implemented yet. Skip until next valid symbol.
|
||||||
|
lexer->next();
|
||||||
|
do {
|
||||||
|
if ((tk.type == blitz::token::variant::SYMBOL) && (tk.text == ",")) {
|
||||||
|
// Next variable is being declared.
|
||||||
|
break;
|
||||||
|
} else if ((tk.type == blitz::token::variant::NEWLINE) || ((tk.type == blitz::token::variant::SYMBOL) && (tk.text == ":"))) {
|
||||||
|
return node;
|
||||||
|
} else if (tk.type == blitz::token::variant::ENDOFFILE) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
tk = lexer->next();
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
/// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
// Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2017-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <list>
|
#include <list>
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../lexer.hpp"
|
#include "../lexer.hpp"
|
||||||
|
#include "../types.hpp"
|
||||||
|
|
||||||
// BlitzBasic Built-Ins
|
// BlitzBasic Built-Ins
|
||||||
// - Include: Followed by a String, which is the file to include at this location.
|
// - Include: Followed by a String, which is the file to include at this location.
|
||||||
@@ -28,87 +29,62 @@
|
|||||||
// - Function, Return, End Function: Defines a function, and allows returning values. Yes, I know, End itself terminates the program, this is a special case. Thanks younger Sibly.
|
// - Function, Return, End Function: Defines a function, and allows returning values. Yes, I know, End itself terminates the program, this is a special case. Thanks younger Sibly.
|
||||||
// - And, Or, Not: Logical operator, self-explanatory really.
|
// - And, Or, Not: Logical operator, self-explanatory really.
|
||||||
|
|
||||||
|
#undef NULL
|
||||||
|
|
||||||
namespace blitz {
|
namespace blitz {
|
||||||
namespace ast {
|
namespace ast {
|
||||||
class expression {
|
struct node {
|
||||||
public:
|
std::vector<blitz::token> tokens;
|
||||||
virtual ~expression() = default;
|
|
||||||
|
virtual ~node() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Values
|
struct variable : public node {
|
||||||
class value_expression : public expression {
|
std::string name;
|
||||||
protected:
|
blitz::types::type type;
|
||||||
blitz::token _token;
|
std::string struct_name;
|
||||||
|
|
||||||
public:
|
virtual ~variable();
|
||||||
virtual ~value_expression() = default;
|
|
||||||
value_expression(blitz::token token);
|
static bool can_parse(std::shared_ptr<blitz::lexer> lexer);
|
||||||
|
static std::shared_ptr<blitz::ast::node> try_parse(std::shared_ptr<blitz::lexer> lexer);
|
||||||
};
|
};
|
||||||
|
|
||||||
class integer_expression : public value_expression {
|
struct value : public node {
|
||||||
protected:
|
enum class variant {
|
||||||
int32_t _value;
|
UNKNOWN,
|
||||||
|
NULL,
|
||||||
|
BOOL,
|
||||||
|
INTEGER,
|
||||||
|
UNSIGNED_INTEGER,
|
||||||
|
REAL,
|
||||||
|
STRING,
|
||||||
|
} type;
|
||||||
|
union {
|
||||||
|
bool b;
|
||||||
|
intmax_t i;
|
||||||
|
uintmax_t ui;
|
||||||
|
double f;
|
||||||
|
} number;
|
||||||
|
std::string text;
|
||||||
|
|
||||||
public:
|
virtual ~value();
|
||||||
virtual ~integer_expression() = default;
|
|
||||||
integer_expression(blitz::token token);
|
static bool can_parse(std::shared_ptr<blitz::lexer> lexer);
|
||||||
|
static std::shared_ptr<blitz::ast::node> try_parse(std::shared_ptr<blitz::lexer> lexer);
|
||||||
};
|
};
|
||||||
|
|
||||||
class real_expression : public value_expression {
|
struct declare : public node {
|
||||||
protected:
|
// Local, Global
|
||||||
float _value;
|
bool global;
|
||||||
|
std::list<std::shared_ptr<blitz::ast::node>> nodes;
|
||||||
|
|
||||||
public:
|
virtual ~declare();
|
||||||
virtual ~real_expression() = default;
|
|
||||||
real_expression(blitz::token token);
|
static bool can_parse(std::shared_ptr<blitz::lexer> lexer);
|
||||||
};
|
static std::shared_ptr<blitz::ast::node> try_parse(std::shared_ptr<blitz::lexer> lexer);
|
||||||
|
|
||||||
class string_expression : public value_expression {
|
|
||||||
std::string _value;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~string_expression() = default;
|
|
||||||
string_expression(blitz::token token);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** One or more constant values
|
|
||||||
*
|
|
||||||
* Const var = Value, var2 = value
|
|
||||||
*/
|
|
||||||
class const_expression : public expression {
|
|
||||||
std::list<std::shared_ptr<variable_expression>> _values;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/** One or more local variables
|
|
||||||
*
|
|
||||||
* Local var, var2 = value, var3
|
|
||||||
*/
|
|
||||||
class local_expression : public expression {
|
|
||||||
std::list<std::shared_ptr<variable_expression>> _values;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** One or more global variables
|
|
||||||
*
|
|
||||||
* Local var, var2 = value, var3
|
|
||||||
*/
|
|
||||||
class global_expression : public expression {
|
|
||||||
std::list<std::shared_ptr<variable_expression>> _values;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** A variable definition
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class variable_expression : public expression {
|
|
||||||
blitz::token _assign;
|
|
||||||
std::string _name;
|
|
||||||
std::shared_ptr<value_expression> _value;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~variable_expression() = default;
|
|
||||||
variable_expression(blitz::token token);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct expression : public node {};
|
||||||
} // namespace ast
|
} // namespace ast
|
||||||
} // namespace blitz
|
} // namespace blitz
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
|
||||||
// Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
|
||||||
#include "function.hpp"
|
|
||||||
|
|
||||||
blitz::ast::ScopeExpression::ScopeExpression() {}
|
|
||||||
|
|
||||||
blitz::ast::ScopeExpression::~ScopeExpression() {}
|
|
||||||
|
|
||||||
void blitz::ast::ScopeExpression::AddExpression(std::unique_ptr<expression> ex) {
|
|
||||||
m_expressions.push_back(std::move(ex));
|
|
||||||
}
|
|
||||||
|
|
||||||
blitz::ast::FunctionExpression::FunctionExpression(ValueType returnType,
|
|
||||||
std::string& m_name,
|
|
||||||
std::list<std::unique_ptr<VariableExpression>> parameters,
|
|
||||||
std::unique_ptr<ScopeExpression> scope)
|
|
||||||
: m_returnType(returnType), m_name(m_name), m_parameters(std::move(parameters)), m_content(std::move(scope)) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
blitz::ast::FunctionExpression::~FunctionExpression() {}
|
|
||||||
|
|
||||||
blitz::ast::CallExpression::CallExpression(std::string& name, std::list<std::unique_ptr<VariableExpression>> arguments)
|
|
||||||
: m_name(name), m_arguments(std::move(arguments)) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
blitz::ast::CallExpression::~CallExpression() {}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
|
||||||
// Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
|
||||||
#pragma once
|
|
||||||
#include "ast.hpp"
|
|
||||||
#include "value.hpp"
|
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace blitz {
|
|
||||||
namespace ast {
|
|
||||||
class ScopeExpression : public expression {
|
|
||||||
public:
|
|
||||||
ScopeExpression();
|
|
||||||
virtual ~ScopeExpression();
|
|
||||||
|
|
||||||
void AddExpression(std::unique_ptr<expression> ex);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::list<std::unique_ptr<expression>> m_expressions;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FunctionExpression : public ScopeExpression {
|
|
||||||
public:
|
|
||||||
FunctionExpression(ValueType returnType,
|
|
||||||
std::string& m_name,
|
|
||||||
std::list<std::unique_ptr<VariableExpression>> parameters,
|
|
||||||
std::unique_ptr<ScopeExpression> scope);
|
|
||||||
virtual ~FunctionExpression();
|
|
||||||
|
|
||||||
private:
|
|
||||||
ValueType m_returnType;
|
|
||||||
std::string m_name;
|
|
||||||
std::list<std::unique_ptr<VariableExpression>> m_parameters;
|
|
||||||
std::unique_ptr<ScopeExpression> m_content;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CallExpression : public expression {
|
|
||||||
public:
|
|
||||||
CallExpression(std::string& name, std::list<std::unique_ptr<VariableExpression>> arguments);
|
|
||||||
virtual ~CallExpression();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_name;
|
|
||||||
std::list<std::unique_ptr<VariableExpression>> m_arguments;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
|
||||||
// Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
|
||||||
#include "value.hpp"
|
|
||||||
|
|
||||||
blitz::ast::VariableExpression::VariableExpression(std::string& name, ValueType type /*= ValueType::Number*/)
|
|
||||||
: m_name(name), m_type(type) {}
|
|
||||||
|
|
||||||
blitz::ast::VariableExpression::~VariableExpression() {}
|
|
||||||
|
|
||||||
blitz::ast::ValueType blitz::ast::VariableExpression::GetType() {
|
|
||||||
return m_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
blitz::ast::NumberExpression::NumberExpression(int32_t value) : value(value) {}
|
|
||||||
|
|
||||||
blitz::ast::NumberExpression::~NumberExpression() {}
|
|
||||||
|
|
||||||
blitz::ast::ValueType blitz::ast::NumberExpression::GetType() {
|
|
||||||
return ValueType::INTEGER;
|
|
||||||
}
|
|
||||||
|
|
||||||
blitz::ast::DecimalExpression::DecimalExpression(float_t value) : value(value) {}
|
|
||||||
|
|
||||||
blitz::ast::DecimalExpression::~DecimalExpression() {}
|
|
||||||
|
|
||||||
blitz::ast::ValueType blitz::ast::DecimalExpression::GetType() {
|
|
||||||
return ValueType::REAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
blitz::ast::StringExpression::StringExpression(std::string value) : value(value) {}
|
|
||||||
|
|
||||||
blitz::ast::StringExpression::~StringExpression() {}
|
|
||||||
|
|
||||||
blitz::ast::ValueType blitz::ast::StringExpression::GetType() {
|
|
||||||
return ValueType::STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
blitz::ast::ConstExpression::ConstExpression(std::string& name, std::unique_ptr<ValueExpression> value)
|
|
||||||
: m_name(name), m_value(std::move(value)) {}
|
|
||||||
|
|
||||||
blitz::ast::ConstExpression::~ConstExpression() {}
|
|
||||||
|
|
||||||
blitz::ast::ValueType blitz::ast::ConstExpression::GetType() {
|
|
||||||
return m_value->GetType();
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
|
||||||
// Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
|
||||||
#pragma once
|
|
||||||
#include "ast.hpp"
|
|
||||||
#include "lexer.hpp"
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <float.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace blitz {
|
|
||||||
namespace ast {
|
|
||||||
enum class ValueType : int8_t {
|
|
||||||
UNKNOWN,
|
|
||||||
INTEGER,
|
|
||||||
REAL,
|
|
||||||
STRING,
|
|
||||||
TYPE,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ValueExpression : public expression {
|
|
||||||
public:
|
|
||||||
virtual ValueType GetType() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ConstExpression : public ValueExpression {
|
|
||||||
public:
|
|
||||||
ConstExpression(std::string& name, std::unique_ptr<ValueExpression> value);
|
|
||||||
virtual ~ConstExpression();
|
|
||||||
|
|
||||||
virtual ValueType GetType() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_name;
|
|
||||||
std::unique_ptr<ValueExpression> m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VariableExpression : public ValueExpression {
|
|
||||||
public:
|
|
||||||
VariableExpression(std::string& name, ValueType type = ValueType::INTEGER);
|
|
||||||
virtual ~VariableExpression();
|
|
||||||
|
|
||||||
virtual ValueType GetType() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_name;
|
|
||||||
ValueType m_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NumberExpression : public ValueExpression {
|
|
||||||
public:
|
|
||||||
NumberExpression(int32_t value);
|
|
||||||
virtual ~NumberExpression();
|
|
||||||
|
|
||||||
virtual ValueType GetType() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int32_t value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DecimalExpression : public ValueExpression {
|
|
||||||
public:
|
|
||||||
DecimalExpression(float_t value);
|
|
||||||
virtual ~DecimalExpression();
|
|
||||||
|
|
||||||
virtual ValueType GetType() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
float_t value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class StringExpression : public ValueExpression {
|
|
||||||
public:
|
|
||||||
StringExpression(std::string value);
|
|
||||||
virtual ~StringExpression();
|
|
||||||
|
|
||||||
virtual ValueType GetType() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
// AUTOGENERATED COPYRIGHT HEADER START
|
// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
// Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2024-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
|
#include <cstdarg>
|
||||||
|
|
||||||
blitz::error::~error() {}
|
blitz::error::~error() {}
|
||||||
|
|
||||||
@@ -26,3 +27,17 @@ std::pair<uint64_t, uint64_t> const& blitz::error::at() const
|
|||||||
{
|
{
|
||||||
return _at;
|
return _at;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string blitz::format(const char* format, ...)
|
||||||
|
{
|
||||||
|
va_list arg1;
|
||||||
|
va_list arg2;
|
||||||
|
va_start(arg1, format);
|
||||||
|
va_copy(arg2, arg1);
|
||||||
|
int length = vsnprintf(nullptr, 0, format, arg1);
|
||||||
|
std::vector<char> buffer(length + 1);
|
||||||
|
vsnprintf(buffer.data(), buffer.size(), format, arg2);
|
||||||
|
va_end(arg1);
|
||||||
|
va_end(arg2);
|
||||||
|
return {buffer.data(), buffer.data() + length};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// AUTOGENERATED COPYRIGHT HEADER START
|
// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
// Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2024-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
@@ -24,4 +24,6 @@ namespace blitz {
|
|||||||
|
|
||||||
std::pair<uint64_t, uint64_t> const& at() const;
|
std::pair<uint64_t, uint64_t> const& at() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string format(const char* format, ...);
|
||||||
} // namespace blitz
|
} // namespace blitz
|
||||||
|
|||||||
+264
-547
@@ -1,24 +1,11 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
/// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
// Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2017-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
#include "lexer.hpp"
|
#include "lexer.hpp"
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include "util.hpp"
|
||||||
std::string format(const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list arg1;
|
|
||||||
va_list arg2;
|
|
||||||
va_start(arg1, format);
|
|
||||||
va_copy(arg2, arg1);
|
|
||||||
int length = vsnprintf(nullptr, 0, format, arg1);
|
|
||||||
std::vector<char> buffer(length + 1);
|
|
||||||
vsnprintf(buffer.data(), buffer.size(), format, arg2);
|
|
||||||
va_end(arg1);
|
|
||||||
va_end(arg2);
|
|
||||||
return {buffer.data(), buffer.data() + length};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string blitz::token::to_string()
|
std::string blitz::token::to_string()
|
||||||
{
|
{
|
||||||
@@ -55,36 +42,46 @@ std::string blitz::token::to_string()
|
|||||||
name = "Symbol";
|
name = "Symbol";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
name = "How the fuck?!";
|
name = "Invalid";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == variant::NEWLINE || type == variant::CONTROL) {
|
if (type == variant::NEWLINE || type == variant::CONTROL) {
|
||||||
return format("%s(%llu@%llu, %d)", name.c_str(), location.first, location.second, text[0]);
|
return blitz::format("%s(%llu@%llu, %d)", name.c_str(), location.first, location.second, text[0]);
|
||||||
} else {
|
} else {
|
||||||
return format("%s(%llu@%llu, %s)", name.c_str(), location.first, location.second, text.c_str());
|
return blitz::format("%s(%llu@%llu, %s)", name.c_str(), location.first, location.second, text.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool blitz::token::operator==(variant rhs)
|
||||||
|
{
|
||||||
|
return type == rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blitz::token::operator==(std::string const& rhs)
|
||||||
|
{
|
||||||
|
return text == rhs;
|
||||||
|
}
|
||||||
|
|
||||||
blitz::lexer::~lexer() {}
|
blitz::lexer::~lexer() {}
|
||||||
|
|
||||||
blitz::lexer::lexer(std::filesystem::path file)
|
blitz::lexer::lexer(std::filesystem::path file)
|
||||||
{
|
{
|
||||||
// Usually files start at line 1 and character 1, so we should start there too.
|
// Usually files start at line 1 and character 1, so we should start there too.
|
||||||
_location = {1, 1};
|
_location = { 1, 1 };
|
||||||
|
|
||||||
// Try and open the file for reading.
|
// Try and open the file for reading.
|
||||||
_file = file;
|
_file = file;
|
||||||
_stream = std::ifstream(_file, std::ios_base::binary); // We use binary so we can eventually support UTF-8.
|
_stream = std::ifstream(_file, std::ios_base::binary); // We use binary so we can eventually support UTF-8.
|
||||||
if (!_stream.good() || _stream.eof() || _stream.bad() || _stream.fail()) {
|
if (!_stream.good() || _stream.eof() || _stream.bad() || _stream.fail()) {
|
||||||
throw std::runtime_error(format("Reading file '%s' failed.", file.generic_string().c_str()));
|
throw std::runtime_error(blitz::format("Reading file '%s' failed.", file.generic_string().c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize token storage to a default token.
|
// Initialize token storage to a default token.
|
||||||
_override = _current = blitz::token{
|
_next = _current = blitz::token{
|
||||||
.location = {0, 0},
|
.location = { 0, 0 },
|
||||||
.text = "",
|
.text = "",
|
||||||
.type = token::variant::UNKNOWN,
|
.type = token::variant::NONE,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,558 +92,278 @@ blitz::token blitz::lexer::current()
|
|||||||
|
|
||||||
blitz::token blitz::lexer::next()
|
blitz::token blitz::lexer::next()
|
||||||
{
|
{
|
||||||
enum class stage {
|
_current = peek();
|
||||||
DEFAULT,
|
_next = blitz::token{
|
||||||
TEXT,
|
.location = { 0, 0 },
|
||||||
NUMBER,
|
.text = "",
|
||||||
STRING,
|
.type = token::variant::NONE,
|
||||||
COMMENT,
|
|
||||||
} state = stage::DEFAULT;
|
|
||||||
|
|
||||||
std::stringstream buffer;
|
|
||||||
blitz::token token{
|
|
||||||
.location = _location,
|
|
||||||
.text = "",
|
|
||||||
.type = blitz::token::variant::UNKNOWN,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto issymbol = [](int chr) {
|
|
||||||
switch (chr) {
|
|
||||||
case ';': // Comment
|
|
||||||
case ':': // Command Separator
|
|
||||||
case '=': // Equal
|
|
||||||
case '<': // Less Than
|
|
||||||
case '>': // Greater Than
|
|
||||||
case '~': // Bitwise Not
|
|
||||||
case '^': // Exponential (X ^ Y = pow(X, Y))
|
|
||||||
case '+': // Plus
|
|
||||||
case '-': // Minus
|
|
||||||
case '*': // Multiply
|
|
||||||
case '/': // Divide
|
|
||||||
case ',': // Parameter Separation
|
|
||||||
case '%': // Integer Type
|
|
||||||
case '#': // Real Type
|
|
||||||
case '$': // String Type
|
|
||||||
case '.': // Structured Type
|
|
||||||
case '\\': // Structured Type Access
|
|
||||||
// Blitz Arrays
|
|
||||||
case '[':
|
|
||||||
case ']':
|
|
||||||
// Call, Grouping, Dim
|
|
||||||
case '(':
|
|
||||||
case ')':
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
auto iswhitespace = [](int chr) {
|
return _current;
|
||||||
switch (chr) {
|
}
|
||||||
case ' ':
|
|
||||||
case '\t':
|
blitz::token blitz::lexer::peek()
|
||||||
return true;
|
{
|
||||||
default:
|
if (_next.type == blitz::token::variant::NONE) {
|
||||||
return false;
|
// ToDo: Optimize
|
||||||
|
enum class stage {
|
||||||
|
DEFAULT,
|
||||||
|
TEXT,
|
||||||
|
NUMBER,
|
||||||
|
STRING,
|
||||||
|
COMMENT,
|
||||||
|
} state = stage::DEFAULT;
|
||||||
|
|
||||||
|
std::stringstream buffer;
|
||||||
|
blitz::token token{
|
||||||
|
.location = _location,
|
||||||
|
.text = "",
|
||||||
|
.type = blitz::token::variant::UNKNOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto issymbol = [](int chr) { return blitz::utility::is_symbol(chr); };
|
||||||
|
auto iswhitespace = [](int chr) { return blitz::utility::is_white_space(chr); };
|
||||||
|
|
||||||
|
// ToDo: Figure out why we don't ever hit chr == EOF.
|
||||||
|
if (_stream.eof()) {
|
||||||
|
token.location = _location;
|
||||||
|
token.type = blitz::token::variant::ENDOFFILE;
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ToDo: Figure out why we don't ever hit chr == EOF.
|
bool complete = false;
|
||||||
if (_stream.eof()) {
|
while (!complete && _stream.good()) {
|
||||||
token.location = _location;
|
// Peek at the current byte, without advancing the read pointer until we need to.
|
||||||
token.type = blitz::token::variant::ENDOFFILE;
|
auto chr = _stream.peek();
|
||||||
return token;
|
bool is_newline = (chr == '\r') || (chr == '\n');
|
||||||
}
|
bool is_returnfeed = (chr == '\r');
|
||||||
|
|
||||||
bool complete = false;
|
if (state == stage::DEFAULT) {
|
||||||
while (!complete && _stream.good()) {
|
if (chr == EOF) {
|
||||||
// Peek at the current byte, without advancing the read pointer until we need to.
|
token.type = blitz::token::variant::ENDOFFILE;
|
||||||
auto chr = _stream.peek();
|
token.text = "";
|
||||||
bool is_newline = (chr == '\r') || (chr == '\n');
|
token.location = _location;
|
||||||
bool is_returnfeed = (chr == '\r');
|
complete = true;
|
||||||
|
_location.second++;
|
||||||
|
} else if (is_newline) {
|
||||||
|
// New Line, should be handled like a control character, but with some special things.
|
||||||
|
token.type = blitz::token::variant::NEWLINE;
|
||||||
|
token.text = "\n";
|
||||||
|
token.location = _location;
|
||||||
|
complete = true;
|
||||||
|
|
||||||
if (state == stage::DEFAULT) {
|
// Advance the read pointer.
|
||||||
if (chr == EOF) {
|
|
||||||
token.type = blitz::token::variant::ENDOFFILE;
|
|
||||||
token.text = "";
|
|
||||||
token.location = _location;
|
|
||||||
complete = true;
|
|
||||||
_location.second++;
|
|
||||||
} else if (is_newline) {
|
|
||||||
// New Line, should be handled like a control character, but with some special things.
|
|
||||||
token.type = blitz::token::variant::NEWLINE;
|
|
||||||
token.text = "\n";
|
|
||||||
token.location = _location;
|
|
||||||
complete = true;
|
|
||||||
|
|
||||||
// Advance the read pointer.
|
|
||||||
_stream.get();
|
|
||||||
|
|
||||||
// Is this a Windows-style \r\n?
|
|
||||||
if (is_returnfeed && (_stream.peek() == '\n')) {
|
|
||||||
// If so, advance the read pointer again.
|
|
||||||
_stream.get();
|
_stream.get();
|
||||||
}
|
|
||||||
|
|
||||||
// Then update the location.
|
// Is this a Windows-style \r\n?
|
||||||
_location.first++;
|
if (is_returnfeed && (_stream.peek() == '\n')) {
|
||||||
_location.second = 1;
|
// If so, advance the read pointer again.
|
||||||
} else if (iswhitespace(chr)) {
|
_stream.get();
|
||||||
// This is white space, which we'll happily ignore.
|
}
|
||||||
_stream.get();
|
|
||||||
_location.second++;
|
// Then update the location.
|
||||||
} else if (chr < 32) {
|
_location.first++;
|
||||||
// Likely to be a control character.
|
_location.second = 1;
|
||||||
token.location = _location;
|
} else if (iswhitespace(chr)) {
|
||||||
token.type = blitz::token::variant::CONTROL;
|
// This is white space, which we'll happily ignore.
|
||||||
token.text = {1, char(chr)};
|
_stream.get();
|
||||||
complete = true;
|
_location.second++;
|
||||||
_stream.get();
|
} else if (chr < 32) {
|
||||||
_location.second++;
|
// Likely to be a control character.
|
||||||
} else if (chr == ':') {
|
token.location = _location;
|
||||||
|
token.type = blitz::token::variant::CONTROL;
|
||||||
|
token.text = { 1, char(chr) };
|
||||||
|
complete = true;
|
||||||
|
_stream.get();
|
||||||
|
_location.second++;
|
||||||
|
/*} else if (chr == ':') {
|
||||||
// Allows code writers to pretend it's all one line.
|
// Allows code writers to pretend it's all one line.
|
||||||
token.location = _location;
|
token.location = _location;
|
||||||
token.type = blitz::token::variant::SEPARATOR;
|
token.type = blitz::token::variant::SEPARATOR;
|
||||||
token.text = {1, char(chr)};
|
token.text = {1, char(chr)};
|
||||||
complete = true;
|
complete = true;
|
||||||
_stream.get();
|
_stream.get();
|
||||||
_location.second++;
|
_location.second++;*/
|
||||||
} else if (chr == ';') {
|
} else if (chr == ';') {
|
||||||
// A comment, which ends at the next new line.
|
// A comment, which ends at the next new line.
|
||||||
state = stage::COMMENT;
|
state = stage::COMMENT;
|
||||||
token.location = _location;
|
|
||||||
token.type = blitz::token::variant::COMMENT;
|
|
||||||
} else if (isdigit(chr)) {
|
|
||||||
// Probably an Integer, or if the latter, it's a Real.
|
|
||||||
state = stage::NUMBER;
|
|
||||||
token.location = _location;
|
|
||||||
token.type = blitz::token::variant::INTEGER;
|
|
||||||
} else if (isalpha(chr)) {
|
|
||||||
// Text of some kind.
|
|
||||||
state = stage::TEXT;
|
|
||||||
token.location = _location;
|
|
||||||
token.type = blitz::token::variant::TEXT;
|
|
||||||
} else if (chr == '"') {
|
|
||||||
// A quoted string.
|
|
||||||
state = stage::STRING;
|
|
||||||
token.location = _location;
|
|
||||||
token.type = blitz::token::variant::STRING;
|
|
||||||
|
|
||||||
// Advance so we actually get anywhere.
|
|
||||||
_stream.get();
|
|
||||||
_location.second++;
|
|
||||||
} else if (issymbol(chr)) {
|
|
||||||
// Special Handling for a few symbols that could mean multiple things.
|
|
||||||
if (chr == '.') { // '.' can start a Real, Label or Structured Type Access. We don't want to decide on the latter here, that's a parser thing.
|
|
||||||
buffer << (char)chr;
|
|
||||||
|
|
||||||
// We advance the read pointer here to look at what's coming next.
|
|
||||||
_stream.get();
|
|
||||||
chr = _stream.peek();
|
|
||||||
_location.second++;
|
|
||||||
|
|
||||||
// Peek at what's coming next.
|
|
||||||
if (isdigit(chr)) {
|
|
||||||
// This is a Real number.
|
|
||||||
token.location = _location;
|
|
||||||
token.type = blitz::token::variant::REAL;
|
|
||||||
state = stage::NUMBER;
|
|
||||||
} else {
|
|
||||||
// Assume this is a symbol and return to normal behavior.
|
|
||||||
token.location = _location;
|
|
||||||
token.text = buffer.str();
|
|
||||||
token.type = blitz::token::variant::SYMBOL;
|
|
||||||
complete = true;
|
|
||||||
}
|
|
||||||
} else if ((chr == '+') || (chr == '-')) { // '+' & '-' could be prefixes to an Integer or Real.
|
|
||||||
buffer << (char)chr;
|
|
||||||
|
|
||||||
// Advance the read pointer to peek at the future.
|
|
||||||
_stream.get();
|
|
||||||
chr = _stream.peek();
|
|
||||||
_location.second++;
|
|
||||||
|
|
||||||
// Peek at what's coming up.
|
|
||||||
if (isdigit(chr) || (chr == '.')) { // Likely to be a Real or Integer.
|
|
||||||
token.location = _location;
|
|
||||||
if (chr == '.') {
|
|
||||||
token.type = blitz::token::variant::REAL;
|
|
||||||
} else {
|
|
||||||
token.type = blitz::token::variant::INTEGER;
|
|
||||||
}
|
|
||||||
state = stage::NUMBER;
|
|
||||||
} else {
|
|
||||||
token.location = _location;
|
|
||||||
token.text = buffer.str();
|
|
||||||
token.type = blitz::token::variant::SYMBOL;
|
|
||||||
complete = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
token.location = _location;
|
token.location = _location;
|
||||||
token.text = {1, char(chr)};
|
token.type = blitz::token::variant::COMMENT;
|
||||||
token.type = blitz::token::variant::SYMBOL;
|
} else if (isdigit(chr)) {
|
||||||
complete = true;
|
// Probably an Integer, or if the latter, it's a Real.
|
||||||
|
state = stage::NUMBER;
|
||||||
|
token.location = _location;
|
||||||
|
token.type = blitz::token::variant::INTEGER;
|
||||||
|
} else if (isalpha(chr)) {
|
||||||
|
// Text of some kind.
|
||||||
|
state = stage::TEXT;
|
||||||
|
token.location = _location;
|
||||||
|
token.type = blitz::token::variant::TEXT;
|
||||||
|
} else if (chr == '"') {
|
||||||
|
// A quoted string.
|
||||||
|
state = stage::STRING;
|
||||||
|
token.location = _location;
|
||||||
|
token.type = blitz::token::variant::STRING;
|
||||||
|
|
||||||
// Advance so we actually get anywhere.
|
// Advance so we actually get anywhere.
|
||||||
_stream.get();
|
_stream.get();
|
||||||
_location.second++;
|
_location.second++;
|
||||||
}
|
} else if (issymbol(chr)) {
|
||||||
} else {
|
// Special Handling for a few symbols that could mean multiple things.
|
||||||
// Everything else is an error
|
if (chr == '.') { // '.' can start a Real, Label or Structured Type Access. We don't want to decide on the latter here, that's a parser thing.
|
||||||
throw blitz::error(_file, _location, _location, "You've encountered a bug. Please report this with the file that caused it.");
|
buffer << (char)chr;
|
||||||
}
|
|
||||||
} else if (state == stage::NUMBER) {
|
// We advance the read pointer here to look at what's coming next.
|
||||||
if ((chr == EOF) || (chr < 32) || is_newline || iswhitespace(chr) || (chr == ';')) {
|
_stream.get();
|
||||||
// EOF, Control, NL, Whitespace, and Comments should return to default parsing.
|
chr = _stream.peek();
|
||||||
complete = true;
|
_location.second++;
|
||||||
} else if (isdigit(chr) || (chr == '.')) {
|
|
||||||
_stream.get();
|
// Peek at what's coming next.
|
||||||
buffer << (char)chr;
|
if (isdigit(chr)) {
|
||||||
if (chr == '.') {
|
// This is a Real number.
|
||||||
if (token.type != token::variant::REAL) {
|
token.location = _location;
|
||||||
token.type = blitz::token::variant::REAL;
|
token.type = blitz::token::variant::REAL;
|
||||||
|
state = stage::NUMBER;
|
||||||
|
} else {
|
||||||
|
// Assume this is a symbol and return to normal behavior.
|
||||||
|
token.location = _location;
|
||||||
|
token.text = buffer.str();
|
||||||
|
token.type = blitz::token::variant::SYMBOL;
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
} else if ((chr == '+') || (chr == '-')) { // '+' & '-' could be prefixes to an Integer or Real.
|
||||||
|
buffer << (char)chr;
|
||||||
|
|
||||||
|
// Advance the read pointer to peek at the future.
|
||||||
|
_stream.get();
|
||||||
|
chr = _stream.peek();
|
||||||
|
_location.second++;
|
||||||
|
|
||||||
|
// Peek at what's coming up.
|
||||||
|
if (isdigit(chr) || (chr == '.')) { // Likely to be a Real or Integer.
|
||||||
|
token.location = _location;
|
||||||
|
if (chr == '.') {
|
||||||
|
token.type = blitz::token::variant::REAL;
|
||||||
|
} else {
|
||||||
|
token.type = blitz::token::variant::INTEGER;
|
||||||
|
}
|
||||||
|
state = stage::NUMBER;
|
||||||
|
} else {
|
||||||
|
token.location = _location;
|
||||||
|
token.text = buffer.str();
|
||||||
|
token.type = blitz::token::variant::SYMBOL;
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
token.text = buffer.str();
|
token.location = _location;
|
||||||
throw blitz::error(_file, token.location, _location, format("In token %s: Expected [0-9], got '%s' instead.", token.to_string().c_str(), std::string{1, (char)chr}.c_str()));
|
token.text = { char(chr) };
|
||||||
|
token.type = blitz::token::variant::SYMBOL;
|
||||||
|
complete = true;
|
||||||
|
|
||||||
|
// Advance so we actually get anywhere.
|
||||||
|
_stream.get();
|
||||||
|
_location.second++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if (issymbol(chr)) {
|
|
||||||
complete = true;
|
|
||||||
} else {
|
|
||||||
token.text = buffer.str();
|
|
||||||
throw blitz::error(_file, token.location, _location, format("In token %s: Expected [0-9.], got '%s' instead.", token.to_string().c_str(), std::string{1, (char)chr}.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (complete) {
|
|
||||||
token.text = buffer.str();
|
|
||||||
}
|
|
||||||
} else if (state == stage::TEXT) {
|
|
||||||
if ((chr == EOF) || (chr < 32) || is_newline || iswhitespace(chr) || issymbol(chr)) {
|
|
||||||
// Return to default parsing.
|
|
||||||
complete = true;
|
|
||||||
} else if (isalpha(chr) || isdigit(chr) || (chr == '_')) {
|
|
||||||
buffer << (char)chr;
|
|
||||||
_stream.get();
|
|
||||||
_location.second++;
|
|
||||||
} else {
|
|
||||||
token.text = buffer.str();
|
|
||||||
throw blitz::error(_file, token.location, _location, format("In token %s: Expected [a-zA-Z0-9_], got '%s' instead.", token.to_string().c_str(), std::string{1, (char)chr}.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (complete) {
|
|
||||||
token.text = buffer.str();
|
|
||||||
}
|
|
||||||
} else if (state == stage::STRING) {
|
|
||||||
if ((chr == EOF) || (chr < 32) || is_newline) {
|
|
||||||
// Return to default parsing.
|
|
||||||
complete = true;
|
|
||||||
} else if (chr == '"') { // The only true way to end a string.
|
|
||||||
complete = true;
|
|
||||||
|
|
||||||
// Skip over the " so we don't confuse the parser.
|
|
||||||
_stream.get();
|
|
||||||
_location.second++;
|
|
||||||
} else {
|
|
||||||
buffer << (char)chr;
|
|
||||||
_stream.get();
|
|
||||||
_location.second++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (complete) {
|
|
||||||
token.text = buffer.str();
|
|
||||||
}
|
|
||||||
} else if (state == stage::COMMENT) {
|
|
||||||
if ((chr == EOF) || (chr < 32) || is_newline) {
|
|
||||||
// Return to default parsing at this point.
|
|
||||||
complete = true;
|
|
||||||
} else {
|
|
||||||
buffer << (char)chr;
|
|
||||||
_stream.get();
|
|
||||||
_location.second++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (complete) {
|
|
||||||
token.text = buffer.str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = token;
|
|
||||||
return _current;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
std::pair<blitz::tokentype, std::string> blitz::lexer::current() {
|
|
||||||
return _current;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<blitz::tokentype, std::string> blitz::lexer::next(std::istream& fs) {
|
|
||||||
std::stringstream buffer;
|
|
||||||
blitz::tokentype token;
|
|
||||||
|
|
||||||
enum class parserState {
|
|
||||||
DEFAULT,
|
|
||||||
TEXT,
|
|
||||||
NUMBER,
|
|
||||||
STRING,
|
|
||||||
COMMENT,
|
|
||||||
} state = parserState::DEFAULT;
|
|
||||||
|
|
||||||
while ((token == blitz::tokentype::TokenUnknown) && !fs.eof() && fs.good()) {
|
|
||||||
auto chr = fs.get();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
std::pair<blitz::lexer::token, std::string> blitz::lexer::next(std::shared_ptr<std::istream> fs) {
|
|
||||||
std::string buf;
|
|
||||||
token tkn = token::TokenUnknown;
|
|
||||||
bool haveResult = false;
|
|
||||||
|
|
||||||
// Allow "overriding" the next retrieved Token.
|
|
||||||
if (m_overrideToken != token::TokenUnknown) {
|
|
||||||
buf = m_overrideText;
|
|
||||||
tkn = m_overrideToken;
|
|
||||||
m_overrideToken = token::TokenUnknown;
|
|
||||||
haveResult = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool m_isTextMode = false;
|
|
||||||
bool m_isNumberMode = false;
|
|
||||||
bool m_isStringMode = false;
|
|
||||||
bool m_isCommentMode = false;
|
|
||||||
bool m_numberModeHasDecimal = false;
|
|
||||||
while (((fs->eof() == false) && (fs->good())) && !haveResult) {
|
|
||||||
char chr = fs->get();
|
|
||||||
|
|
||||||
if (chr == '\r' || chr == '\n') {
|
|
||||||
if (tkn != token::TokenEOF) {
|
|
||||||
m_overrideToken = token::TokenNewLine;
|
|
||||||
m_overrideText = "";
|
|
||||||
} else {
|
|
||||||
tkn = token::TokenNewLine;
|
|
||||||
buf = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
m_isStringMode = false;
|
|
||||||
m_isNumberMode = false;
|
|
||||||
m_isTextMode = false;
|
|
||||||
m_isCommentMode = false;
|
|
||||||
break;
|
|
||||||
} else if (m_isStringMode) {
|
|
||||||
if (chr == '\"') {
|
|
||||||
m_overrideToken = token::TokenDoubleQuote;
|
|
||||||
m_overrideText = chr;
|
|
||||||
m_isStringMode = false;
|
|
||||||
tkn = token::TokenQuotedText;
|
|
||||||
break;
|
|
||||||
} else if (iscntrl(chr) || !isprint(chr)) {
|
|
||||||
fs->putback(chr);
|
|
||||||
m_isStringMode = false;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
buf += chr;
|
|
||||||
}
|
|
||||||
} else if (m_isTextMode) {
|
|
||||||
if (isalnum(chr) || (chr == '_')) {
|
|
||||||
buf += chr;
|
|
||||||
} else {
|
|
||||||
fs->putback(chr);
|
|
||||||
m_isTextMode = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (m_isNumberMode) {
|
|
||||||
if (isdigit(chr)) {
|
|
||||||
buf += chr;
|
|
||||||
} else if (chr == '.') {
|
|
||||||
if (m_numberModeHasDecimal == false) {
|
|
||||||
m_numberModeHasDecimal = true;
|
|
||||||
tkn = token::TokenDecimal;
|
|
||||||
buf += chr;
|
|
||||||
} else {
|
} else {
|
||||||
fs->putback(chr);
|
// Everything else is an error
|
||||||
m_isNumberMode = false;
|
throw blitz::error(_file, _location, _location, "You've encountered a bug. Please report this with the file that caused it.");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else if (state == stage::NUMBER) {
|
||||||
fs->putback(chr);
|
if ((chr == EOF) || (chr < 32) || is_newline || iswhitespace(chr) || (chr == ';')) {
|
||||||
m_isNumberMode = false;
|
// EOF, Control, NL, Whitespace, and Comments should return to default parsing.
|
||||||
break;
|
complete = true;
|
||||||
}
|
} else if (chr == 'f') {
|
||||||
} else if (m_isCommentMode) {
|
_stream.get();
|
||||||
buf += chr;
|
token.type = blitz::token::variant::REAL;
|
||||||
tkn = token::TokenComment;
|
complete = true;
|
||||||
} else {
|
} else if (chr == 'u') {
|
||||||
// Whitespace
|
_stream.get();
|
||||||
if (isspace(chr))
|
buffer << (char)chr;
|
||||||
continue;
|
token.type = blitz::token::variant::INTEGER;
|
||||||
|
complete = true;
|
||||||
// Control Code
|
} else if ((chr == 'b') || (chr == 'x')) {
|
||||||
if (iscntrl(chr)) {
|
_stream.get();
|
||||||
tkn = token::TokenUnknown;
|
buffer << (char)chr;
|
||||||
buf = chr;
|
if (buffer.tellp() > 2) {
|
||||||
}
|
throw blitz::error(_file, token.location, _location, blitz::format("In token %s: Expected [0-9], got '%s' instead.", token.to_string().c_str(), std::string{ 1, (char)chr }.c_str()));
|
||||||
|
}
|
||||||
// Special handling for + and -, due to numbers and decimals.
|
} else if (isdigit(chr) || (chr == '.')) {
|
||||||
if (chr == '+' || chr == '-') {
|
_stream.get();
|
||||||
char chr2 = fs->get();
|
buffer << (char)chr;
|
||||||
if (isdigit(chr2)) {
|
if (chr == '.') {
|
||||||
m_isNumberMode = true;
|
if (token.type != token::variant::REAL) {
|
||||||
m_numberModeHasDecimal = false;
|
token.type = blitz::token::variant::REAL;
|
||||||
tkn = token::TokenNumber;
|
} else {
|
||||||
buf = chr + chr2;
|
token.text = buffer.str();
|
||||||
break;
|
throw blitz::error(_file, token.location, _location, blitz::format("In token %s: Expected [0-9], got '%s' instead.", token.to_string().c_str(), std::string{ 1, (char)chr }.c_str()));
|
||||||
} else if (chr2 == '.') {
|
}
|
||||||
m_isNumberMode = true;
|
}
|
||||||
m_numberModeHasDecimal = true;
|
} else if (issymbol(chr)) {
|
||||||
tkn = token::TokenDecimal;
|
complete = true;
|
||||||
buf = chr + "0" + chr2;
|
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
fs->putback(chr2);
|
token.text = buffer.str();
|
||||||
|
throw blitz::error(_file, token.location, _location, blitz::format("In token %s: Expected ([0](b|x|))[0-9.], got '%s' instead.", token.to_string().c_str(), std::string{ 1, (char)chr }.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (complete) {
|
||||||
|
token.text = buffer.str();
|
||||||
|
}
|
||||||
|
} else if (state == stage::TEXT) {
|
||||||
|
if ((chr == EOF) || (chr < 32) || is_newline || iswhitespace(chr) || issymbol(chr)) {
|
||||||
|
// Return to default parsing.
|
||||||
|
complete = true;
|
||||||
|
} else if (isalpha(chr) || isdigit(chr) || (chr == '_')) {
|
||||||
|
buffer << (char)chr;
|
||||||
|
_stream.get();
|
||||||
|
_location.second++;
|
||||||
|
} else {
|
||||||
|
token.text = buffer.str();
|
||||||
|
throw blitz::error(_file, token.location, _location, blitz::format("In token %s: Expected [a-zA-Z0-9_], got '%s' instead.", token.to_string().c_str(), std::string{ 1, (char)chr }.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (complete) {
|
||||||
|
token.text = buffer.str();
|
||||||
|
}
|
||||||
|
} else if (state == stage::STRING) {
|
||||||
|
if ((chr == EOF) || (chr < 32) || is_newline) {
|
||||||
|
// Return to default parsing.
|
||||||
|
complete = true;
|
||||||
|
} else if (chr == '"') { // The only true way to end a string.
|
||||||
|
complete = true;
|
||||||
|
|
||||||
|
// Skip over the " so we don't confuse the parser.
|
||||||
|
_stream.get();
|
||||||
|
_location.second++;
|
||||||
|
} else {
|
||||||
|
buffer << (char)chr;
|
||||||
|
_stream.get();
|
||||||
|
_location.second++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (complete) {
|
||||||
|
token.text = buffer.str();
|
||||||
|
}
|
||||||
|
} else if (state == stage::COMMENT) {
|
||||||
|
if ((chr == EOF) || (chr < 32) || is_newline) {
|
||||||
|
// Return to default parsing at this point.
|
||||||
|
complete = true;
|
||||||
|
} else {
|
||||||
|
buffer << (char)chr;
|
||||||
|
_stream.get();
|
||||||
|
_location.second++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (complete) {
|
||||||
|
token.text = buffer.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Symbol
|
|
||||||
for (auto v : g_symbolCharacters) {
|
|
||||||
if (v.first == chr) {
|
|
||||||
tkn = v.second;
|
|
||||||
buf = v.first;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tkn != token::TokenEOF) {
|
|
||||||
haveResult = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strings, Text, Numbers
|
|
||||||
if (chr == ';') {
|
|
||||||
m_isCommentMode = true;
|
|
||||||
tkn = token::TokenSemicolon;
|
|
||||||
buf = chr;
|
|
||||||
break;
|
|
||||||
} else if (chr == '\"') {
|
|
||||||
m_isStringMode = true;
|
|
||||||
tkn = token::TokenDoubleQuote;
|
|
||||||
buf = chr;
|
|
||||||
break;
|
|
||||||
} else if (isalpha(chr)) {
|
|
||||||
m_isTextMode = true;
|
|
||||||
tkn = token::TokenText;
|
|
||||||
buf = chr;
|
|
||||||
} else if (isdigit(chr)) {
|
|
||||||
m_isNumberMode = true;
|
|
||||||
m_numberModeHasDecimal = false;
|
|
||||||
tkn = token::TokenNumber;
|
|
||||||
buf = chr;
|
|
||||||
} else if (chr == '.') {
|
|
||||||
m_isNumberMode = true;
|
|
||||||
m_numberModeHasDecimal = true;
|
|
||||||
tkn = token::TokenDecimal;
|
|
||||||
buf = "0" + chr;
|
|
||||||
} else {
|
|
||||||
tkn = token::TokenUnknown;
|
|
||||||
buf = chr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
_next = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert from Text into native Token.
|
return _next;
|
||||||
if (tkn == token::TokenText)
|
|
||||||
tkn = to_token(tkn, buf);
|
|
||||||
|
|
||||||
return std::make_pair(tkn, buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blitz::lexer::token blitz::lexer::to_token(token in, std::string text) {
|
std::filesystem::path blitz::lexer::file()
|
||||||
static std::pair<const char*, token> l_textToTokenList[] = {
|
{
|
||||||
// Binary
|
return std::filesystem::path(_file);
|
||||||
{ "not", token::TokenNot },
|
|
||||||
{ "and", token::TokenAnd },
|
|
||||||
{ "or", token::TokenOr },
|
|
||||||
{ "xor", token::TokenXor },
|
|
||||||
{ "shl", token::TokenShl },
|
|
||||||
{ "shr", token::TokenShr },
|
|
||||||
{ "sal", token::TokenSal },
|
|
||||||
{ "sar", token::TokenSar },
|
|
||||||
{ "false", token::TokenFalse },
|
|
||||||
{ "true", token::TokenTrue },
|
|
||||||
|
|
||||||
// Conversion
|
|
||||||
{ "float", token::TokenFloat },
|
|
||||||
{ "string", token::TokenString },
|
|
||||||
{ "hex", token::TokenHex },
|
|
||||||
{ "int", token::TokenInt },
|
|
||||||
|
|
||||||
// Control
|
|
||||||
{ "if", token::TokenIf },
|
|
||||||
{ "then", token::TokenThen },
|
|
||||||
{ "elseif", token::TokenElseIf },
|
|
||||||
{ "else", token::TokenElse },
|
|
||||||
{ "endif", token::TokenEndIf },
|
|
||||||
{ "select", token::TokenSelect },
|
|
||||||
{ "case", token::TokenCase },
|
|
||||||
{ "default", token::TokenDefault },
|
|
||||||
{ "goto", token::TokenGoto },
|
|
||||||
{ "gosub", token::TokenGosub },
|
|
||||||
{ "return", token::TokenReturn },
|
|
||||||
{ "function", token::TokenFunction },
|
|
||||||
{ "end", token::TokenEnd },
|
|
||||||
{ "stop", token::TokenStop },
|
|
||||||
|
|
||||||
// Loop
|
|
||||||
{ "for", token::TokenFor },
|
|
||||||
{ "to", token::TokenTo },
|
|
||||||
{ "next", token::TokenNext },
|
|
||||||
{ "while", token::TokenWhile },
|
|
||||||
{ "wend", token::TokenWend },
|
|
||||||
{ "repeat", token::TokenRepeat },
|
|
||||||
{ "until", token::TokenUntil },
|
|
||||||
{ "forever", token::TokenForever },
|
|
||||||
{ "exit", token::TokenExit },
|
|
||||||
|
|
||||||
// Math
|
|
||||||
{ "abs", token::TokenAbs },
|
|
||||||
{ "sign", token::TokenSign },
|
|
||||||
{ "cos", token::TokenCos },
|
|
||||||
{ "sin", token::TokenSin },
|
|
||||||
{ "tan", token::TokenTan },
|
|
||||||
{ "acos", token::TokenACos },
|
|
||||||
{ "asin", token::TokenASin },
|
|
||||||
{ "atan", token::TokenATan },
|
|
||||||
{ "atan2", token::TokenATan2 },
|
|
||||||
{ "log", token::TokenLog },
|
|
||||||
{ "log10", token::TokenLog10 },
|
|
||||||
{ "ceil", token::TokenCeil },
|
|
||||||
{ "floor", token::TokenFloor },
|
|
||||||
{ "mod", token::TokenMod },
|
|
||||||
{ "pi", token::TokenPi },
|
|
||||||
{ "exp", token::TokenExp },
|
|
||||||
{ "sqr", token::TokenSqr },
|
|
||||||
|
|
||||||
// Variables
|
|
||||||
{ "const", token::TokenConst },
|
|
||||||
{ "global", token::TokenGlobal },
|
|
||||||
{ "local", token::TokenLocal },
|
|
||||||
|
|
||||||
// Includes
|
|
||||||
{ "include", token::TokenInclude },
|
|
||||||
};
|
|
||||||
for (auto v : l_textToTokenList) {
|
|
||||||
if (stricmp(text.c_str(), v.first)) {
|
|
||||||
return v.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return in;
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
/// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
// Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2017-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
@@ -21,10 +21,11 @@ namespace blitz {
|
|||||||
std::pair<uint64_t, uint64_t> location;
|
std::pair<uint64_t, uint64_t> location;
|
||||||
std::string text;
|
std::string text;
|
||||||
enum class variant : uint64_t {
|
enum class variant : uint64_t {
|
||||||
|
NONE, // There is no token here.
|
||||||
UNKNOWN, // We have absolutely no fucking clue.
|
UNKNOWN, // We have absolutely no fucking clue.
|
||||||
ENDOFFILE, // End of the file.
|
ENDOFFILE, // End of the file.
|
||||||
NEWLINE, // New Line.
|
NEWLINE, // New Line.
|
||||||
SEPARATOR, // Command Separator.
|
//SEPARATOR, // Command Separator.
|
||||||
CONTROL, // All kinds of control signals
|
CONTROL, // All kinds of control signals
|
||||||
SYMBOL, // All kinds of symbols.
|
SYMBOL, // All kinds of symbols.
|
||||||
COMMENT, // ; Whatever
|
COMMENT, // ; Whatever
|
||||||
@@ -35,6 +36,9 @@ namespace blitz {
|
|||||||
} type;
|
} type;
|
||||||
|
|
||||||
std::string to_string();
|
std::string to_string();
|
||||||
|
|
||||||
|
bool operator==(blitz::token::variant rhs);
|
||||||
|
bool operator==(std::string const& rhs);
|
||||||
};
|
};
|
||||||
|
|
||||||
class lexer {
|
class lexer {
|
||||||
@@ -45,7 +49,7 @@ namespace blitz {
|
|||||||
std::pair<uint64_t, uint64_t> _location;
|
std::pair<uint64_t, uint64_t> _location;
|
||||||
|
|
||||||
blitz::token _current;
|
blitz::token _current;
|
||||||
blitz::token _override;
|
blitz::token _next;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~lexer();
|
~lexer();
|
||||||
@@ -60,5 +64,14 @@ namespace blitz {
|
|||||||
* This will replace the current token.
|
* This will replace the current token.
|
||||||
*/
|
*/
|
||||||
blitz::token next();
|
blitz::token next();
|
||||||
|
|
||||||
|
/** Peek at the next token in the given stream.
|
||||||
|
*
|
||||||
|
* The current token will remain in-tact.
|
||||||
|
*/
|
||||||
|
blitz::token peek();
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::filesystem::path file();
|
||||||
};
|
};
|
||||||
} // namespace blitz
|
} // namespace blitz
|
||||||
|
|||||||
@@ -1,54 +1,49 @@
|
|||||||
// AUTOGENERATED COPYRIGHT HEADER START
|
// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
// Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2017-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
#include <clocale>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "compiler.hpp"
|
#include "compiler.hpp"
|
||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
#include "lexer.hpp"
|
#include "lexer.hpp"
|
||||||
|
#include "parser.hpp"
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
std::cout << argv[1] << std::endl;
|
|
||||||
blitz::lexer lex(argv[1]);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (blitz::token token = lex.next(); (token.type != blitz::token::variant::ENDOFFILE); token = lex.next()) {
|
std::setlocale(LC_ALL, "en_US.UTF-8");
|
||||||
switch (token.type) {
|
|
||||||
case blitz::token::variant::COMMENT:
|
std::cout << argv[1] << std::endl;
|
||||||
std::cout << token.text;
|
std::list<std::shared_ptr<blitz::ast::node>> nodes;
|
||||||
break;
|
|
||||||
case blitz::token::variant::SYMBOL:
|
std::shared_ptr<blitz::lexer> lex2 = std::make_shared<blitz::lexer>(argv[1]);
|
||||||
std::cout << token.text << " ";
|
for (blitz::token token = lex2->next(); (token.type != blitz::token::variant::ENDOFFILE); token = lex2->next()) {
|
||||||
break;
|
std::cout << token.to_string() << " ";
|
||||||
case blitz::token::variant::TEXT:
|
if (token.type == blitz::token::variant::NEWLINE) {
|
||||||
case blitz::token::variant::INTEGER:
|
|
||||||
case blitz::token::variant::REAL:
|
|
||||||
std::cout << token.text << " ";
|
|
||||||
break;
|
|
||||||
case blitz::token::variant::STRING:
|
|
||||||
std::cout << "\"" << token.text << "\""
|
|
||||||
<< " ";
|
|
||||||
break;
|
|
||||||
case blitz::token::variant::NEWLINE:
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
std::cout << token.to_string() << " ";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.type == blitz::token::variant::UNKNOWN) {
|
if (token.type == blitz::token::variant::UNKNOWN) {
|
||||||
std::cin.get();
|
std::cin.get();
|
||||||
|
} else if (blitz::ast::declare::can_parse(lex2)) {
|
||||||
|
nodes.push_back(blitz::ast::declare::try_parse(lex2));
|
||||||
|
} else if (blitz::ast::value::can_parse(lex2)) {
|
||||||
|
nodes.push_back(blitz::ast::value::try_parse(lex2));
|
||||||
|
} else if (blitz::ast::variable::can_parse(lex2)) {
|
||||||
|
nodes.push_back(blitz::ast::variable::try_parse(lex2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (blitz::error const& ex) {
|
|
||||||
std::cout << ex.file() << std::endl;
|
|
||||||
std::cout << "Line " << ex.at().first << ", Char " << ex.at().second << ": " << ex.what() << std::endl;
|
|
||||||
} catch (std::runtime_error const& ex) {
|
|
||||||
std::cout << ex.what() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cin.get();
|
//std::cin.get();
|
||||||
return 0;
|
return 0;
|
||||||
|
} catch (blitz::error const& ex) {
|
||||||
|
std::cout << std::endl << ex.file() << std::endl;
|
||||||
|
std::cout << "Line " << ex.at().first << ", Char " << ex.at().second << ": " << ex.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
} catch (std::runtime_error const& ex) {
|
||||||
|
std::cout << std::endl << ex.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlitzBasic is a strange but powerful language in the right hands. While it has
|
// BlitzBasic is a strange but powerful language in the right hands. While it has
|
||||||
@@ -75,6 +70,7 @@ int main(int argc, char** argv)
|
|||||||
//
|
//
|
||||||
// 3. Function calls don't always need Parenthesis:
|
// 3. Function calls don't always need Parenthesis:
|
||||||
// ```
|
// ```
|
||||||
|
// Local myName
|
||||||
// Function myName() : End Function
|
// Function myName() : End Function
|
||||||
// If myName() Then : EndIf ; <- Calls myName
|
// If myName() Then : EndIf ; <- Calls myName
|
||||||
// myName ; <- Calls myName, because there is no = after it.
|
// myName ; <- Calls myName, because there is no = after it.
|
||||||
@@ -89,4 +85,4 @@ int main(int argc, char** argv)
|
|||||||
// Print Int(myName) ; <- Prints the address of the object contained in myName.
|
// Print Int(myName) ; <- Prints the address of the object contained in myName.
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// As this is a Basic language, there is no concept of undefined or uninitialized anything. Every behavior is well defined.
|
// As this is a Basic language, there is no concept of undefined or uninitialized anything. Every behavior is "well" defined even if confusing.
|
||||||
|
|||||||
+150
-106
@@ -1,115 +1,159 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
/// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
// Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2024-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
/*
|
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
#include "ast/function.hpp"
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <cctype>
|
||||||
#include <vector>
|
#include <cstdarg>
|
||||||
#include <stdarg.h>
|
#include "error.hpp"
|
||||||
|
|
||||||
blitz::parser::parser(std::string file) {
|
blitz::parser::~parser() {}
|
||||||
// Try and load the file
|
|
||||||
std::shared_ptr<std::ifstream> instream = std::make_shared<std::ifstream>(file);
|
blitz::parser::parser(std::filesystem::path file) : _file(file), _lexer(), _expr()
|
||||||
if (instream->bad() || !instream->good()) {
|
{
|
||||||
throw std::ios_base::failure("Failed to open file.");
|
_lexer = std::make_shared<blitz::lexer>(file);
|
||||||
}
|
|
||||||
m_files.push(std::make_pair(file, instream));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blitz::parser::~parser() {
|
|
||||||
while (m_files.size() > 0) {
|
|
||||||
std::shared_ptr<std::ifstream> file = std::dynamic_pointer_cast<std::ifstream>(m_files.top().second);
|
|
||||||
file->close();
|
|
||||||
m_files.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<blitz::ast::expression> blitz::parser::parse() {
|
//
|
||||||
std::unique_ptr<ast::ScopeExpression> scope = std::make_unique<ast::ScopeExpression>();
|
//std::shared_ptr<blitz::ast::node> blitz::parser::current()
|
||||||
|
//{
|
||||||
std::unique_ptr<ast::expression> expr;
|
// return _expr;
|
||||||
while ((expr = std::move(parse_expression())) != nullptr) {
|
//}
|
||||||
scope->AddExpression(std::move(expr));
|
//
|
||||||
}
|
//std::shared_ptr<blitz::ast::node> blitz::parser::next()
|
||||||
|
//{
|
||||||
return std::move(scope);
|
// // This should return an entire "line" of expressions in one go, i.e.:
|
||||||
}
|
// // 1. Local a = 1, b = a, c = b+a
|
||||||
|
// // -> Local(Variable(a, Expression(Integer(1))), Variable(b, Expresssion(Variable(a))), Variable(c, Expression(Add(Variable(b), Variable(a)))
|
||||||
void blitz::parser::log(const char* msg, ...) {
|
// // 2. Include "HelloWorld.bb"
|
||||||
std::vector<char> buf(65535);
|
// // -> Include(String("HelloWorld.bb"))
|
||||||
va_list val;
|
// // 3. Function HelloWorld()
|
||||||
va_start(val, msg);
|
// // -> Function(HelloWorld, ...)(
|
||||||
int rval = vsnprintf(buf.data(), buf.size(), msg, val);
|
// // Not quite sure if the above makes sense, we'd be returning many expressions outside of functions, but only one inside a function? Why even bother with the current/next crap then?
|
||||||
va_end(val);
|
// // Handling Include becomes a problem too. I guess we should actually return expressions on a line by line basis, and let the "compiler" figure out scope and stuff.
|
||||||
std::cout << buf.data() << '\n';
|
//
|
||||||
}
|
// // Grab the next token to figure out what behavior we should have.
|
||||||
|
// while (true) {
|
||||||
void blitz::parser::log_error(const char* msg, ...) {
|
// auto token = _lexer->next();
|
||||||
std::vector<char> buf(65535);
|
// try {
|
||||||
va_list val;
|
// switch (token.type) {
|
||||||
va_start(val, msg);
|
// case blitz::token::variant::ENDOFFILE:
|
||||||
int rval = vsnprintf(buf.data(), buf.size(), msg, val);
|
// // End of file means there's nothing left to parse.
|
||||||
va_end(val);
|
// _expr.reset();
|
||||||
std::cerr << buf.data() << '\n';
|
// return nullptr;
|
||||||
}
|
// case blitz::token::variant::COMMENT:
|
||||||
|
// case blitz::token::variant::NEWLINE:
|
||||||
std::pair<blitz::lexer::tokentype, std::string> blitz::parser::next() {
|
// case blitz::token::variant::SEPARATOR:
|
||||||
return m_lexer.next(m_files.top().second);
|
// // Ignore some things that aren't very useful right now.
|
||||||
}
|
// continue;
|
||||||
|
// case blitz::token::variant::TEXT:
|
||||||
std::unique_ptr<blitz::ast::expression> blitz::parser::parse_expression() {
|
// return try_parse(token);
|
||||||
while (true) {
|
// default:
|
||||||
auto tkn = next();
|
// throw nullptr;
|
||||||
log("%s", tkn.second.c_str());
|
// }
|
||||||
|
// } catch (blitz::error const& ex) {
|
||||||
switch (tkn.first) {
|
// throw ex;
|
||||||
case blitz::lexer::tokentype::TokenNewLine:
|
// } catch (std::exception const& ex) {
|
||||||
case blitz::lexer::tokentype::TokenComment:
|
// throw new blitz::error(_file, token.location, token.location, ex.what());
|
||||||
// Skip Comments, since we don't really need them for the AST.
|
// } catch (...) {
|
||||||
continue;
|
// throw new blitz::error(_file, token.location, token.location, blitz::format("Token %s unexpected at this point.", token.to_string().c_str()));
|
||||||
case blitz::lexer::tokentype::TokenPlus:
|
// }
|
||||||
case blitz::lexer::tokentype::TokenMinus:
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//std::shared_ptr<blitz::ast::node> blitz::parser::try_parse(blitz::token token)
|
||||||
|
//{
|
||||||
|
// // ToDo: Switch to a proper Unicode library. Maybe Boost?
|
||||||
|
// std::string ltext;
|
||||||
|
// std::transform(token.text.begin(), token.text.end(), ltext.begin(), [](std::string::value_type c) { return std::tolower(c); });
|
||||||
|
//
|
||||||
|
// if ((ltext == "local") || (ltext == "global")) {
|
||||||
|
// // Local/Global have the same parsing, but different functionality.
|
||||||
|
// // Should be:
|
||||||
|
// // Text Text [Symbol(=) Expression] [Symbol(,) Text [Symbol(=) Expression] [Symbol(,) ...]]
|
||||||
|
//
|
||||||
|
// } else if (ltext == "function") {
|
||||||
|
// //Example:
|
||||||
|
// // Function FunctionName[$,%,#,:TypeName,.StructName]([Variable[, Variable=Value[, ...]])
|
||||||
|
// // [Function Content ...]
|
||||||
|
// // EndFunction
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// } else if (ltext == "select") {
|
||||||
|
// } else if (ltext == "case") {
|
||||||
|
// } else if (ltext == "endselect") {
|
||||||
|
// } else if (ltext == "if") {
|
||||||
|
// } else if (ltext == "elif") {
|
||||||
|
// } else if (ltext == "endif") {
|
||||||
|
//
|
||||||
|
// } else if (ltext == "end") {
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return nullptr;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//std::shared_ptr<blitz::ast::node> blitz::parser::try_parse_expression() {
|
||||||
|
// // () + - / * = <> > < String Integer Float Variable
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//std::shared_ptr<blitz::ast::node> blitz::parser::try_parse_variable_expression()
|
||||||
|
//{
|
||||||
|
// // Text [Symbol(=) Expression(...)] [Symbol(,) [Text [Symbol(=) Expression(...)]]]
|
||||||
|
//
|
||||||
|
// auto label = _lexer->next();
|
||||||
|
// if (label != blitz::token::variant::TEXT) {
|
||||||
|
// throw new blitz::error(_file, label.location, label.location, blitz::format("Unexpected %s, expected Text.", label.to_string().c_str()));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// auto node = std::make_shared<blitz::ast::variable>(label);
|
||||||
|
//
|
||||||
|
// auto operand = _lexer->next();
|
||||||
|
// if (operand == "=") {
|
||||||
|
// //node->set_value(try_parse_expression());
|
||||||
|
// } else if (operand == blitz::token::variant::NEWLINE || operand == blitz::token::variant::SEPARATOR || (operand == blitz::token::variant::SYMBOL && operand == ",")) {
|
||||||
|
// return node;
|
||||||
|
// } else {
|
||||||
|
// throw new blitz::error(_file, label.location, operand.location, blitz::format("Unexpected %s, expected Symbol(=), NewLine, Separator, or Symbol(,).", operand.to_string().c_str()));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return node;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
default: // End Of File / Unknown
|
/* Expressions
|
||||||
case blitz::lexer::tokentype::TokenUnknown:
|
*
|
||||||
case blitz::lexer::tokentype::TokenEOF:
|
* Example Locations:
|
||||||
return nullptr;
|
* - Local Var = Expression
|
||||||
break;
|
* - Var = Expression
|
||||||
}
|
* - myFunction(Expression, ...)
|
||||||
}
|
* - If Expression Then
|
||||||
}
|
*
|
||||||
|
* Example Expressions:
|
||||||
std::unique_ptr<blitz::ast::NumberExpression> blitz::parser::parse_number(blitz::lexer::tokentype token, std::string value) {
|
* - 0 + 0, 0 - 0, 0 * 0, 0 / 0, 0 Shr 0, 0 Shl 0, 0 And 0, 0 Or 0, Not 0,
|
||||||
if (token != lexer::tokentype::TokenNumber) {
|
* -
|
||||||
log_error("Unexpected Token during parsing, expected number.");
|
*
|
||||||
return nullptr;
|
*
|
||||||
}
|
*
|
||||||
|
*
|
||||||
char* endptr = const_cast<char*>(value.c_str() + value.size());
|
*
|
||||||
int32_t parsed = strtol(value.c_str(), &endptr, 10);
|
*
|
||||||
if (errno == ERANGE) {
|
*
|
||||||
log_error("Number out of range.");
|
*
|
||||||
return nullptr;
|
*
|
||||||
}
|
*
|
||||||
|
*
|
||||||
return std::make_unique<blitz::ast::NumberExpression>(parsed);
|
*
|
||||||
}
|
*
|
||||||
|
*
|
||||||
std::unique_ptr<blitz::ast::DecimalExpression> blitz::parser::parse_decimal(blitz::lexer::tokentype token, std::string value) {
|
*
|
||||||
if (token != lexer::tokentype::TokenNumber) {
|
*
|
||||||
log_error("Unexpected Token during parsing, expected number.");
|
*
|
||||||
return nullptr;
|
*
|
||||||
}
|
*
|
||||||
|
*
|
||||||
char* endptr = const_cast<char*>(value.c_str() + value.size());
|
*
|
||||||
float_t parsed = strtof(value.c_str(), &endptr);
|
*
|
||||||
if (errno == ERANGE) {
|
*
|
||||||
log_error("Number out of range.");
|
*
|
||||||
return nullptr;
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_unique<blitz::ast::DecimalExpression>(parsed);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -1,54 +1,28 @@
|
|||||||
/// AUTOGENERATED COPYRIGHT HEADER START
|
/// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
// Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2017-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <memory>
|
||||||
#include "ast/ast.hpp"
|
#include "ast/ast.hpp"
|
||||||
#include "lexer.hpp"
|
#include "lexer.hpp"
|
||||||
|
|
||||||
namespace blitz {
|
namespace blitz {
|
||||||
class parser {
|
class parser {
|
||||||
std::filesystem::path _file;
|
std::filesystem::path _file;
|
||||||
|
std::shared_ptr<blitz::lexer> _lexer;
|
||||||
|
|
||||||
|
std::shared_ptr<blitz::ast::node> _expr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~parser();
|
~parser();
|
||||||
parser(std::filesystem::path file);
|
parser(std::filesystem::path file);
|
||||||
|
|
||||||
|
std::shared_ptr<blitz::ast::node> current();
|
||||||
|
std::shared_ptr<blitz::ast::node> next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<blitz::ast::node> try_parse(blitz::token token);
|
||||||
|
std::shared_ptr<blitz::ast::node> try_parse_variable();
|
||||||
};
|
};
|
||||||
} // namespace blitz
|
} // namespace blitz
|
||||||
|
|
||||||
/*
|
|
||||||
#include <fstream>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <stack>
|
|
||||||
#include <string>
|
|
||||||
#include "ast/value.hpp"
|
|
||||||
#include "lexer.hpp"
|
|
||||||
|
|
||||||
namespace blitz {
|
|
||||||
class parser {
|
|
||||||
public:
|
|
||||||
parser(std::string file);
|
|
||||||
~parser();
|
|
||||||
|
|
||||||
std::unique_ptr<ast::expression> parse();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void log(const char* msg, ...);
|
|
||||||
void log_error(const char* msg, ...);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::pair<blitz::lexer::tokentype, std::string> next();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<ast::expression> parse_expression();
|
|
||||||
std::unique_ptr<ast::NumberExpression> parse_number(blitz::lexer::tokentype token, std::string value);
|
|
||||||
std::unique_ptr<ast::DecimalExpression> parse_decimal(blitz::lexer::tokentype token, std::string value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
lexer m_lexer;
|
|
||||||
std::stack<std::pair<std::string, std::shared_ptr<std::istream>>> m_files;
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
// Copyright (C) 2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
#include "types.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
const std::pair<const char*, blitz::types::type> _map_to[] = {
|
||||||
|
{ "byte", blitz::types::type::INT8 }, //
|
||||||
|
{ "int8", blitz::types::type::INT8 }, //
|
||||||
|
{ "ubyte", blitz::types::type::UINT8 }, //
|
||||||
|
{ "uint8", blitz::types::type::UINT8 }, //
|
||||||
|
{ "short", blitz::types::type::INT16 }, //
|
||||||
|
{ "int16", blitz::types::type::INT16 }, //
|
||||||
|
{ "ushort", blitz::types::type::UINT16 }, //
|
||||||
|
{ "uint16", blitz::types::type::UINT16 }, //
|
||||||
|
{ "int", blitz::types::type::INT32 }, //
|
||||||
|
{ "int32", blitz::types::type::INT32 }, //
|
||||||
|
{ "uint", blitz::types::type::UINT32 }, //
|
||||||
|
{ "uint32", blitz::types::type::UINT32 }, //
|
||||||
|
{ "long ", blitz::types::type::INT64 }, //
|
||||||
|
{ "int64", blitz::types::type::INT64 }, //
|
||||||
|
{ "ulong", blitz::types::type::UINT64 }, //
|
||||||
|
{ "uint64", blitz::types::type::UINT64 }, //
|
||||||
|
{ "half", blitz::types::type::FLOAT16 }, //
|
||||||
|
{ "float16", blitz::types::type::FLOAT16 }, //
|
||||||
|
{ "real16", blitz::types::type::FLOAT16 }, //
|
||||||
|
{ "single", blitz::types::type::FLOAT32 }, //
|
||||||
|
{ "float", blitz::types::type::FLOAT32 }, //
|
||||||
|
{ "float32", blitz::types::type::FLOAT32 }, //
|
||||||
|
{ "real", blitz::types::type::FLOAT32 }, //
|
||||||
|
{ "real32", blitz::types::type::FLOAT32 }, //
|
||||||
|
{ "double", blitz::types::type::DOUBLE }, //
|
||||||
|
{ "float64", blitz::types::type::DOUBLE }, //
|
||||||
|
{ "real64", blitz::types::type::DOUBLE }, //
|
||||||
|
{ "string", blitz::types::type::STRING }, //
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string blitz::types::to_string(blitz::types::type type)
|
||||||
|
{
|
||||||
|
if (type == type::STRUCT) {
|
||||||
|
return "struct";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto kv : _map_to) {
|
||||||
|
if (type == kv.second) {
|
||||||
|
return kv.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
blitz::types::type blitz::types::from_string(std::string text)
|
||||||
|
{
|
||||||
|
std::transform(text.cbegin(), text.cend(), text.begin(), [](char from) {
|
||||||
|
if (from & 0b10000000) { // Exclude Unicode
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
return (char)std::tolower(from);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto kv : _map_to) {
|
||||||
|
if (text == kv.first) {
|
||||||
|
return kv.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blitz::types::type::UNKNOWN;
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
// Copyright (C) 2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
#pragma once
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace types {
|
||||||
|
enum class type : uint8_t {
|
||||||
|
UNKNOWN,
|
||||||
|
// 8-bit Integers
|
||||||
|
INT8,
|
||||||
|
BYTE = INT8,
|
||||||
|
UINT8,
|
||||||
|
UBYTE = UINT8,
|
||||||
|
// 16-bit Integers
|
||||||
|
INT16,
|
||||||
|
SHORT = INT16,
|
||||||
|
UINT16,
|
||||||
|
USHORT = UINT16,
|
||||||
|
// 32-bit Integers
|
||||||
|
INT32,
|
||||||
|
INT = INT32,
|
||||||
|
UINT32,
|
||||||
|
UINT = UINT32,
|
||||||
|
// 64-bit Integers
|
||||||
|
INT64,
|
||||||
|
LONG = INT64,
|
||||||
|
UINT64,
|
||||||
|
ULONG = UINT64,
|
||||||
|
// 16-bit Float
|
||||||
|
FLOAT16,
|
||||||
|
HALF = FLOAT16,
|
||||||
|
// 32-bit Float
|
||||||
|
FLOAT32,
|
||||||
|
FLOAT = FLOAT32,
|
||||||
|
SINGLE = FLOAT32,
|
||||||
|
// 64-bit Float
|
||||||
|
FLOAT64,
|
||||||
|
DOUBLE = FLOAT64,
|
||||||
|
// UTF-8 String
|
||||||
|
STRING,
|
||||||
|
// User-defined Struct
|
||||||
|
STRUCT,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string to_string(blitz::types::type type);
|
||||||
|
blitz::types::type from_string(std::string text);
|
||||||
|
};
|
||||||
|
} // namespace blitz
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
// Copyright (C) 2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
#include "util.hpp"
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
bool blitz::utility::is_symbol(int code)
|
||||||
|
{
|
||||||
|
switch (code) {
|
||||||
|
case ';': // Comment
|
||||||
|
case ':': // Command Separator
|
||||||
|
case '=': // Equal
|
||||||
|
case '<': // Less Than
|
||||||
|
case '>': // Greater Than
|
||||||
|
case '~': // Bitwise Not
|
||||||
|
case '^': // Exponential (X ^ Y = pow(X, Y))
|
||||||
|
case '+': // Plus
|
||||||
|
case '-': // Minus
|
||||||
|
case '*': // Multiply
|
||||||
|
case '/': // Divide
|
||||||
|
case ',': // Parameter Separation
|
||||||
|
case '%': // Integer Type
|
||||||
|
case '#': // Real Type
|
||||||
|
case '$': // String Type
|
||||||
|
case '.': // Structured Type
|
||||||
|
case '\\': // Structured Type Access
|
||||||
|
|
||||||
|
case '[': // Blitz Arrays
|
||||||
|
case ']':
|
||||||
|
|
||||||
|
case '(': // Call, Grouping, Dim
|
||||||
|
case ')':
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blitz::utility::is_white_space(int code)
|
||||||
|
{
|
||||||
|
switch (code) {
|
||||||
|
case ' ':
|
||||||
|
case '\t':
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blitz::utility::is_digit(int code)
|
||||||
|
{
|
||||||
|
return isdigit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blitz::utility::is_alpha(int code) {
|
||||||
|
return isalpha(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
char blitz::utility::utf8_safe_tolower(char code)
|
||||||
|
{
|
||||||
|
if (code & 0b10000000) { // Exclude Unicode
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
return (char)std::tolower(code);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
// Copyright (C) 2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
|
||||||
|
namespace blitz::utility {
|
||||||
|
bool is_symbol(int code);
|
||||||
|
|
||||||
|
bool is_white_space(int code);
|
||||||
|
|
||||||
|
bool is_digit(int code);
|
||||||
|
|
||||||
|
bool is_alpha(int code);
|
||||||
|
|
||||||
|
char utf8_safe_tolower(char code);
|
||||||
|
} // namespace blitz::utility
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
This directory contains automatically downloaded toolchains and generators.
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# AUTOGENERATED COPYRIGHT HEADER START
|
# AUTOGENERATED COPYRIGHT HEADER START
|
||||||
# Copyright (C) NaN-NaN undefined
|
|
||||||
# Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
# Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
# AUTOGENERATED COPYRIGHT HEADER END
|
# AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# AUTOGENERATED COPYRIGHT HEADER START
|
# AUTOGENERATED COPYRIGHT HEADER START
|
||||||
# Copyright (C) NaN-NaN undefined
|
|
||||||
# Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
# Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
# AUTOGENERATED COPYRIGHT HEADER END
|
# AUTOGENERATED COPYRIGHT HEADER END
|
||||||
/stranded2
|
/stranded2
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
; AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
; Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
; AUTOGENERATED COPYRIGHT HEADER END
|
||||||
; Ein simpler Lexer Test
|
; Ein simpler Lexer Test
|
||||||
Local Variable = 1.0
|
Local Variable = 1.0
|
||||||
Local Variable2$ = "Hallo Welt"
|
Local Variable2$ = "Hallo Welt"
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
; AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
; Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
; AUTOGENERATED COPYRIGHT HEADER END
|
||||||
Graphics 800,600,32,2
|
Graphics 800,600,32,2
|
||||||
SetBuffer BackBuffer()
|
SetBuffer BackBuffer()
|
||||||
|
|
||||||
|
|||||||
+155
@@ -0,0 +1,155 @@
|
|||||||
|
; AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
; Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
; AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
Local iValue = 1, iValue2 = 1.2, iValue3 = "Hello World"
|
||||||
|
; iValue should be 1
|
||||||
|
; iValue2 should be 1, and print a warning
|
||||||
|
; iValue3 should be 0 (converted from string), and print a warning
|
||||||
|
Local fValue# = 1, fValue2# = 1.2, fValue3# = "Hello World"
|
||||||
|
; fValue should be 1.0
|
||||||
|
; fValue2 should be 1.2
|
||||||
|
; fValue3 should be 0.0 (converted from string), and print a warning
|
||||||
|
Local sValue$ = 1, sValue2$ = 1.2, sValue3$ = "Hello World"
|
||||||
|
; sValue = "1", print warning
|
||||||
|
; sValue2 = "1.2", print warning
|
||||||
|
; sValue3 = "Hello World"
|
||||||
|
|
||||||
|
Function iFunction()
|
||||||
|
Return 1
|
||||||
|
End Function
|
||||||
|
Function iFunction2()
|
||||||
|
Return 1.2
|
||||||
|
End Function
|
||||||
|
Function iFunction3()
|
||||||
|
Return "Hello World"
|
||||||
|
End Function
|
||||||
|
; iFunction returns 1
|
||||||
|
; iFunction2 returns 1, prints warning
|
||||||
|
; iFunction3 returns 0, prints warning
|
||||||
|
|
||||||
|
Function fFunction#()
|
||||||
|
Return 1
|
||||||
|
End Function
|
||||||
|
Function fFunction2#()
|
||||||
|
Return 1.2
|
||||||
|
End Function
|
||||||
|
Function fFunction3#()
|
||||||
|
Return "Hello World"
|
||||||
|
End Function
|
||||||
|
; fFunction returns 1.0
|
||||||
|
; fFunction2 returns 1.2
|
||||||
|
; fFunction3 returns 0, prints warning
|
||||||
|
|
||||||
|
Function sFunction$()
|
||||||
|
Return 1
|
||||||
|
End Function
|
||||||
|
Function sFunction2$()
|
||||||
|
Return 1.2
|
||||||
|
End Function
|
||||||
|
Function sFunction3$()
|
||||||
|
Return "Hello World"
|
||||||
|
End Function
|
||||||
|
; sFunction returns "1", prints warning
|
||||||
|
; sFunction2 returns "1.2", prints warning
|
||||||
|
; sFunction3 returns "Hello World"
|
||||||
|
|
||||||
|
;-- AST Representation
|
||||||
|
;LocalVariables{
|
||||||
|
; Variable{Int32, iValue, toInt32(Int32(1))},
|
||||||
|
; Variable{Int32, iValue2, toInt32(Real32(1.2))},
|
||||||
|
; Variable{Int32, iValue3, toInt32(String(Hello World))},
|
||||||
|
;}
|
||||||
|
;LocalVariables{
|
||||||
|
; Variable{Real32, fValue, toReal32(Int32(1))},
|
||||||
|
; Variable{Real32, fValue2, toReal32(Real32(1.2))},
|
||||||
|
; Variable{Real32, fValue3, toReal32(String(Hello World))},
|
||||||
|
;}
|
||||||
|
;LocalVariables{
|
||||||
|
; Variable{String, sValue, toString(Int32(1))},
|
||||||
|
; Variable{String, sValue2, toString(Real32(1.2))},
|
||||||
|
; Variable{String, sValue3, toString("Hello World")},
|
||||||
|
;}
|
||||||
|
;
|
||||||
|
;Function{
|
||||||
|
; Text(iFunction),
|
||||||
|
; Int32,
|
||||||
|
; Parameters{
|
||||||
|
; },
|
||||||
|
; Content{
|
||||||
|
; Return(Int32(1))
|
||||||
|
; }
|
||||||
|
;}
|
||||||
|
;Function{
|
||||||
|
; Text(iFunction2),
|
||||||
|
; Int32,
|
||||||
|
; Parameters{
|
||||||
|
; },
|
||||||
|
; Content{
|
||||||
|
; Return(Real32(1.2))
|
||||||
|
; }
|
||||||
|
;}
|
||||||
|
;Function{
|
||||||
|
; Text(iFunction3),
|
||||||
|
; Int32,
|
||||||
|
; Parameters{
|
||||||
|
; },
|
||||||
|
; Content{
|
||||||
|
; Return(String("Hello World"))
|
||||||
|
; }
|
||||||
|
;}
|
||||||
|
;
|
||||||
|
;Function{
|
||||||
|
; Text(fFunction),
|
||||||
|
; Real32,
|
||||||
|
; Parameters{
|
||||||
|
; },
|
||||||
|
; Content{
|
||||||
|
; Return(Int32(1))
|
||||||
|
; }
|
||||||
|
;}
|
||||||
|
;Function{
|
||||||
|
; Text(fFunction2),
|
||||||
|
; Real32,
|
||||||
|
; Parameters{
|
||||||
|
; },
|
||||||
|
; Content{
|
||||||
|
; Return(Real32(1.2))
|
||||||
|
; }
|
||||||
|
;}
|
||||||
|
;Function{
|
||||||
|
; Text(fFunction3),
|
||||||
|
; Real32,
|
||||||
|
; Parameters{
|
||||||
|
; },
|
||||||
|
; Content{
|
||||||
|
; Return(String("Hello World"))
|
||||||
|
; }
|
||||||
|
;}
|
||||||
|
;
|
||||||
|
;Function{
|
||||||
|
; Text(sFunction),
|
||||||
|
; String,
|
||||||
|
; Parameters{
|
||||||
|
; },
|
||||||
|
; Content{
|
||||||
|
; Return(Int32(1))
|
||||||
|
; }
|
||||||
|
;}
|
||||||
|
;Function{
|
||||||
|
; Text(sFunction2),
|
||||||
|
; String,
|
||||||
|
; Parameters{
|
||||||
|
; },
|
||||||
|
; Content{
|
||||||
|
; Return(Real32(1.2))
|
||||||
|
; }
|
||||||
|
;}
|
||||||
|
;Function{
|
||||||
|
; Text(sFunction3),
|
||||||
|
; String,
|
||||||
|
; Parameters{
|
||||||
|
; },
|
||||||
|
; Content{
|
||||||
|
; Return(String("Hello World"))
|
||||||
|
; }
|
||||||
|
;}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
; AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
; Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
; AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
Local iValue = 2
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
; AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
; Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
; AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
Include "004-incl.bb"
|
||||||
|
; Should be parsed as:
|
||||||
|
; Include(String("test.bb"))
|
||||||
|
; which should then jump to parsing "test.bb" relative to the current file.
|
||||||
|
|
||||||
|
iValue = iValue + 4
|
||||||
|
; Should be parsed as Assign(Variable(iValue), Add(Variable(iValue), Int32(4)))
|
||||||
|
|
||||||
|
Print iValue
|
||||||
|
; Should be parsed as Call(Function(Print), Variable(iValue))
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
; AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
; Copyright (C) 2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
; AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
Variable
|
||||||
|
Variable%
|
||||||
|
Variable%%
|
||||||
|
Variable#
|
||||||
|
Variable##
|
||||||
|
Variable$
|
||||||
|
Variable$$
|
||||||
|
Variable:Int
|
||||||
|
Variable:Float
|
||||||
|
Variable:String
|
||||||
|
Variable:Int8
|
||||||
|
Variable:Int16
|
||||||
|
Variable:Int32
|
||||||
|
Variable:Int64
|
||||||
|
Variable:UInt8
|
||||||
|
Variable:UInt16
|
||||||
|
Variable:UInt32
|
||||||
|
Variable:UInt64
|
||||||
|
Variable:Half
|
||||||
|
Variable:Single
|
||||||
|
Variable:Double
|
||||||
|
Variable:Real
|
||||||
|
Variable:Float16
|
||||||
|
Variable:Float32
|
||||||
|
Variable:Float64
|
||||||
|
Variable:Real16
|
||||||
|
Variable:Real32
|
||||||
|
Variable:Real64
|
||||||
|
0
|
||||||
|
0x
|
||||||
|
0b
|
||||||
|
0b100
|
||||||
|
0b100u
|
||||||
|
0x100
|
||||||
|
0x100u
|
||||||
|
255
|
||||||
|
255u
|
||||||
|
65535
|
||||||
|
65535u
|
||||||
|
"Level Up"
|
||||||
|
"Hello World"
|
||||||
|
True
|
||||||
|
False
|
||||||
|
Null
|
||||||
|
Local myVar%, myVar2#
|
||||||
|
Global gVar$, gVar15:Int32
|
||||||
|
Global gHelloWorld$ = "Hello World", localVarThatIsntLocal
|
||||||
+82
-26
@@ -1,8 +1,6 @@
|
|||||||
// AUTOGENERATED COPYRIGHT HEADER START
|
// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
// Copyright (C) NaN-NaN undefined
|
|
||||||
// Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// AUTOGENERATED COPYRIGHT HEADER END
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
|
|
||||||
const ignoreList = [
|
const ignoreList = [
|
||||||
/^\.git$/gi,
|
/^\.git$/gi,
|
||||||
/^cmake\/clang$/gi,
|
/^cmake\/clang$/gi,
|
||||||
@@ -39,6 +37,10 @@ const formatStyleList = {
|
|||||||
], exts: [
|
], exts: [
|
||||||
".iss",
|
".iss",
|
||||||
".iss.in",
|
".iss.in",
|
||||||
|
".bb",
|
||||||
|
".b3d",
|
||||||
|
".b2d",
|
||||||
|
".bpl"
|
||||||
],
|
],
|
||||||
prepend: [
|
prepend: [
|
||||||
`; ${sectionStart}`,
|
`; ${sectionStart}`,
|
||||||
@@ -111,6 +113,7 @@ const PATH = require("node:path");
|
|||||||
const FS = require("node:fs");
|
const FS = require("node:fs");
|
||||||
const FSPROMISES = require("node:fs/promises");
|
const FSPROMISES = require("node:fs/promises");
|
||||||
const OS = require("os");
|
const OS = require("os");
|
||||||
|
const READLINE = require('node:readline');
|
||||||
|
|
||||||
if (!debug)
|
if (!debug)
|
||||||
console.debug = function() {}
|
console.debug = function() {}
|
||||||
@@ -191,11 +194,12 @@ class RateLimiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let abortAllWork = false;
|
let abortAllWork = false;
|
||||||
let gitRL = new RateLimiter(1);
|
let gitRL = new RateLimiter(2);
|
||||||
let workRL = new RateLimiter();
|
let workRL = new RateLimiter();
|
||||||
let gitCurrentFiles;
|
let gitCurrentFiles;
|
||||||
let gitUserName;
|
let gitUserName;
|
||||||
let gitUserMail;
|
let gitUserMail;
|
||||||
|
let gitSubmodules;
|
||||||
let gitDate = (new Date()).toISOString();
|
let gitDate = (new Date()).toISOString();
|
||||||
|
|
||||||
/** Run a process asynchronously, returning an array of messages.
|
/** Run a process asynchronously, returning an array of messages.
|
||||||
@@ -265,6 +269,8 @@ async function runProcessAsync(path, args, options) {
|
|||||||
async function git_isIgnored(path) {
|
async function git_isIgnored(path) {
|
||||||
console.debug(arguments.callee.name, Array.from(arguments));
|
console.debug(arguments.callee.name, Array.from(arguments));
|
||||||
let rpath = PATH.relative(process.cwd(), path).replaceAll(PATH.sep, PATH.posix.sep);
|
let rpath = PATH.relative(process.cwd(), path).replaceAll(PATH.sep, PATH.posix.sep);
|
||||||
|
|
||||||
|
// Check manual ignore list.
|
||||||
for (let ignore of ignoreList) {
|
for (let ignore of ignoreList) {
|
||||||
if (ignore instanceof RegExp) {
|
if (ignore instanceof RegExp) {
|
||||||
if (ignore.global) {
|
if (ignore.global) {
|
||||||
@@ -282,6 +288,15 @@ async function git_isIgnored(path) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this happens to be (in) a submodule.
|
||||||
|
let modules = await git_subModules()
|
||||||
|
for (let module of modules) {
|
||||||
|
if (rpath.startsWith(module)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally check if git ignores this file.
|
||||||
let result = await gitRL.queue(runProcessAsync, "git", ["check-ignore", path], {});
|
let result = await gitRL.queue(runProcessAsync, "git", ["check-ignore", path], {});
|
||||||
return (result[0] == 0)
|
return (result[0] == 0)
|
||||||
}
|
}
|
||||||
@@ -299,6 +314,31 @@ async function git_getCurrentAuthor() {
|
|||||||
return commitFormat.replace("%aI", gitDate).replace("%aN", gitUserName).replace("%aE", gitUserMail);
|
return commitFormat.replace("%aI", gitDate).replace("%aN", gitUserName).replace("%aE", gitUserMail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function git_subModules() {
|
||||||
|
if (!gitSubmodules) {
|
||||||
|
gitSubmodules = new Set();
|
||||||
|
|
||||||
|
let gitmodules = PATH.join(process.cwd(), ".gitmodules");
|
||||||
|
if ((await FSPROMISES.stat(gitmodules)).isFile()) {
|
||||||
|
let gmfs = FS.createReadStream(gitmodules);
|
||||||
|
const rl = READLINE.createInterface({
|
||||||
|
input: gmfs,
|
||||||
|
crlfDelay: Infinity
|
||||||
|
});
|
||||||
|
for await(const line of rl) {
|
||||||
|
let parts = line.trim().split("=");
|
||||||
|
if (parts.length > 1) {
|
||||||
|
if(parts[0].trim() == "path") {
|
||||||
|
gitSubmodules.add(parts[1].trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gitSubmodules;
|
||||||
|
}
|
||||||
|
|
||||||
async function git_isInCurrentCommit(file) {
|
async function git_isInCurrentCommit(file) {
|
||||||
console.debug(arguments.callee.name, Array.from(arguments));
|
console.debug(arguments.callee.name, Array.from(arguments));
|
||||||
if (!gitCurrentFiles) {
|
if (!gitCurrentFiles) {
|
||||||
@@ -444,39 +484,44 @@ async function updateFile(file) {
|
|||||||
}
|
}
|
||||||
console.log(`Updating file '${file}'...`);
|
console.log(`Updating file '${file}'...`);
|
||||||
|
|
||||||
|
// ToDo: Do we actually need to read the file first, or can we use the same rw stream?
|
||||||
// File contents.
|
// File contents.
|
||||||
let content = await FSPROMISES.readFile(file);
|
let contentBuf = await FSPROMISES.readFile(file);
|
||||||
let eol = (content.indexOf("\r\n") != -1 ? OS.EOL : "\n");
|
let eol = contentBuf.indexOf("\r\n") != -1 ? "\r\n" : "\n";
|
||||||
let insert = Buffer.from(header.join(eol) + eol);
|
let headerBuf = Buffer.from(header.join(eol) + eol, "utf8");
|
||||||
|
|
||||||
// Find the starting point.
|
// Find the starting point.
|
||||||
let startHeader = content.indexOf(sectionStart);
|
let startHeader = contentBuf.indexOf(Buffer.from(header[0], "utf8"));
|
||||||
startHeader = content.lastIndexOf(eol, startHeader);
|
if (startHeader != -1) {
|
||||||
startHeader += Buffer.from(eol).byteLength;
|
//startHeader = contentBuf.lastIndexOf(eolBuf, startHeader);
|
||||||
|
//startHeader += eolb.byteLength;
|
||||||
|
}
|
||||||
|
|
||||||
// Find the ending point.
|
// Find the ending point.
|
||||||
let endHeader = content.indexOf(sectionEnd);
|
let endHeader = contentBuf.lastIndexOf(Buffer.from(header[header.length - 1], "utf8"));
|
||||||
endHeader = content.indexOf(eol, endHeader);
|
if (endHeader != -1) {
|
||||||
endHeader += Buffer.from(eol).byteLength;
|
endHeader += Buffer.from(header[header.length - 1], "utf8").byteLength;
|
||||||
|
endHeader += Buffer.byteLength(eol, "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last check for early-exit here.
|
||||||
if (abortAllWork) {
|
if (abortAllWork) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let fd = await FSPROMISES.open(file, "w");
|
let fd = await FSPROMISES.open(file, "w");
|
||||||
let fp = [];
|
if (startHeader == -1 || (endHeader < startHeader)) {
|
||||||
if ((startHeader >= 0) && (endHeader > startHeader)) {
|
await fd.write(headerBuf, 0, null, 0);
|
||||||
|
await fd.write(contentBuf, 0, null, headerBuf.byteLength);
|
||||||
|
} else {
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
|
|
||||||
if (startHeader > 0) {
|
if (startHeader > 0) {
|
||||||
fd.write(content, 0, startHeader, 0);
|
await fd.write(contentBuf, 0, startHeader, 0);
|
||||||
pos += startHeader;
|
pos += startHeader;
|
||||||
}
|
}
|
||||||
fd.write(insert, 0, undefined, pos);
|
await fd.write(headerBuf, 0, null, pos); pos += headerBuf.byteLength;
|
||||||
pos += insert.byteLength;
|
await fd.write(contentBuf, endHeader, null, pos);
|
||||||
fd.write(content, endHeader, undefined, pos);
|
|
||||||
} else {
|
|
||||||
fd.write(insert, 0, undefined, 0);
|
|
||||||
fd.write(content, 0, undefined, insert.byteLength);
|
|
||||||
}
|
}
|
||||||
await fd.close();
|
await fd.close();
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@@ -490,11 +535,14 @@ async function updateFile(file) {
|
|||||||
|
|
||||||
async function scanPath(path) {
|
async function scanPath(path) {
|
||||||
console.debug(arguments.callee.name, Array.from(arguments));
|
console.debug(arguments.callee.name, Array.from(arguments));
|
||||||
|
|
||||||
// Abort here if the user aborted the process, or if the path is ignored.
|
// Abort here if the user aborted the process, or if the path is ignored.
|
||||||
if (abortAllWork) {
|
if (abortAllWork) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`Scanning path '${path}'...`);
|
||||||
|
|
||||||
let promises = [];
|
let promises = [];
|
||||||
|
|
||||||
await workRL.queue(async () => {
|
await workRL.queue(async () => {
|
||||||
@@ -511,7 +559,6 @@ async function scanPath(path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
console.log(`Scanning path '${fullname}'...`);
|
|
||||||
promises.push(scanPath(fullname));
|
promises.push(scanPath(fullname));
|
||||||
} else {
|
} else {
|
||||||
promises.push(updateFile(fullname));
|
promises.push(updateFile(fullname));
|
||||||
@@ -535,6 +582,7 @@ async function scanPath(path) {
|
|||||||
console.debug(root_path, PROCESS.argv, PROCESS.execArgv);
|
console.debug(root_path, PROCESS.argv, PROCESS.execArgv);
|
||||||
|
|
||||||
var args = PROCESS.argv.slice(2);
|
var args = PROCESS.argv.slice(2);
|
||||||
|
let promises = new Array();
|
||||||
while (args.length > 0) {
|
while (args.length > 0) {
|
||||||
// Try to place ourselves where git actually is.
|
// Try to place ourselves where git actually is.
|
||||||
while (!is_git_directory) {
|
while (!is_git_directory) {
|
||||||
@@ -543,7 +591,7 @@ async function scanPath(path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let entries = await FSPROMISES.readdir(PROCESS.cwd());
|
let entries = await FSPROMISES.readdir(PROCESS.cwd());
|
||||||
if (entries.includes(".git")) {
|
if (entries.includes(".git") && ((await FSPROMISES.stat(PATH.join(PROCESS.cwd(), ".git"))).isDirectory())) {
|
||||||
console.log(`Found .git at '${process.cwd()}'.`);
|
console.log(`Found .git at '${process.cwd()}'.`);
|
||||||
is_git_directory = true;
|
is_git_directory = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -558,6 +606,12 @@ async function scanPath(path) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (abortAllWork) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
git_getCurrentAuthor();
|
||||||
|
|
||||||
// Then proceed with normal work.
|
// Then proceed with normal work.
|
||||||
let path = PATH.normalize(PATH.relative(process.cwd(), PATH.resolve(root_path, args[0])));
|
let path = PATH.normalize(PATH.relative(process.cwd(), PATH.resolve(root_path, args[0])));
|
||||||
|
|
||||||
@@ -567,15 +621,17 @@ async function scanPath(path) {
|
|||||||
if (await git_isIgnored(path)) {
|
if (await git_isIgnored(path)) {
|
||||||
console.log(`Ignoring path '${path}'...`);
|
console.log(`Ignoring path '${path}'...`);
|
||||||
} else if(pathStat.isDirectory()) {
|
} else if(pathStat.isDirectory()) {
|
||||||
console.log(`Scanning path '${path}'...`);
|
//await scanPath(path);
|
||||||
await scanPath(path);
|
promises.push(scanPath(path));
|
||||||
} else {
|
} else {
|
||||||
await updateFile(path);
|
//await updateFile(path);
|
||||||
|
promises.push(updateFile(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slice off the first argument and continue.
|
// Slice off the first argument and continue.
|
||||||
args = args.slice(1);
|
args = args.slice(1);
|
||||||
}
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
console.log("Done");
|
console.log("Done");
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user