├── .gitignore
├── .vscode
├── c_cpp_properties.json
└── settings.json
├── CMakeLists.txt
├── LICENSE.md
├── README.md
├── examples
├── example.ps
└── example.psobj
├── logo.png
└── src
├── definitions.hpp
├── interpreter.cpp
├── lexer.cpp
├── main.cpp
├── parser.cpp
├── propscript.hpp
└── quickmath.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | [Bb]uild/
--------------------------------------------------------------------------------
/.vscode/c_cpp_properties.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "name": "Win64",
5 | "includePath": [
6 | "${workspaceFolder}/src/",
7 | "C:/vcpkg/installed/x64-windows/include/",
8 | "C:/Program Files/VulkanSDK/1.3.231.1/Include"
9 | ],
10 | "defines": [
11 | "_DEBUG",
12 | "UNICODE",
13 | "_UNICODE"
14 | ],
15 | "compileCommands": "${default}",
16 | "windowsSdkVersion": "10.0.18362.0",
17 | "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.27.29110/bin/Hostx64/x64/cl.exe",
18 | "cStandard": "c17",
19 | "cppStandard": "c++17",
20 | "intelliSenseMode": "windows-msvc-x64"
21 | }
22 | ],
23 | "version": 4
24 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cmake.configureSettings": {
3 | "CMAKE_TOOLCHAIN_FILE" : "C:/vcpkg/scripts/buildsystems/vcpkg.cmake",
4 | "VCPKG_TARGET_TRIPLET": "x64-windows"
5 | },
6 |
7 | "files.associations": {
8 | "atomic": "cpp",
9 | "bit": "cpp",
10 | "cctype": "cpp",
11 | "clocale": "cpp",
12 | "cmath": "cpp",
13 | "compare": "cpp",
14 | "concepts": "cpp",
15 | "cstddef": "cpp",
16 | "cstdint": "cpp",
17 | "cstdio": "cpp",
18 | "cstdlib": "cpp",
19 | "cstring": "cpp",
20 | "ctime": "cpp",
21 | "cwchar": "cpp",
22 | "exception": "cpp",
23 | "initializer_list": "cpp",
24 | "ios": "cpp",
25 | "iosfwd": "cpp",
26 | "iostream": "cpp",
27 | "istream": "cpp",
28 | "iterator": "cpp",
29 | "limits": "cpp",
30 | "memory": "cpp",
31 | "new": "cpp",
32 | "ostream": "cpp",
33 | "stdexcept": "cpp",
34 | "streambuf": "cpp",
35 | "system_error": "cpp",
36 | "tuple": "cpp",
37 | "type_traits": "cpp",
38 | "typeinfo": "cpp",
39 | "utility": "cpp",
40 | "xfacet": "cpp",
41 | "xiosbase": "cpp",
42 | "xlocale": "cpp",
43 | "xlocinfo": "cpp",
44 | "xlocnum": "cpp",
45 | "xmemory": "cpp",
46 | "xstddef": "cpp",
47 | "xstring": "cpp",
48 | "xtr1common": "cpp",
49 | "xutility": "cpp",
50 | "string": "cpp",
51 | "vector": "cpp",
52 | "*.rh": "cpp",
53 | "algorithm": "cpp",
54 | "optional": "cpp",
55 | "set": "cpp",
56 | "xtree": "cpp",
57 | "fstream": "cpp",
58 | "sstream": "cpp",
59 | "array": "cpp"
60 | }
61 | }
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.12)
2 | set(CMAKE_CXX_STANDARD 17)
3 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
4 |
5 | # allows us to set msvc working directory
6 | cmake_policy(SET CMP0091 NEW)
7 |
8 | # set source files:
9 | project(propscript VERSION 1.0)
10 | file(GLOB_RECURSE propscript_src CONFIGURE_DEPENDS "src/*.cpp")
11 | add_executable(${PROJECT_NAME} ${propscript_src})
12 |
13 | # set working directory:
14 | set_property(TARGET ${PROJECT_NAME} PROPERTY WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/")
15 | if(MSVC)
16 | set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/")
17 | set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DPI_AWARE "PerMonitor") # set dpi awareness (only works on windows)
18 | set_property(TARGET ${PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY MultiThreadedDLL)
19 | endif()
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Daniel Elwell
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # PropScript
6 | PropScript is a small, interpreted programming language designed for scripting. I wrote it to facilitate the development of Terra Toy, my video game. It has a similar syntax to Python and was written in ~2500 lines of code with 0 dependencies. Please note that this is merely a hobby project; It is lacking in many features, error messages are vague, and there are likely to be a number of yet undiscovered issues.
7 |
8 | ## Features
9 | - Basic Arithmetic (int, float, vec2, vec3, vec4, quaternion)
10 | - If/Else Statements
11 | - Functions (declared with "func" keyword)
12 | - For Loops (declared similar to python: "for var in range(...)")
13 | - Support for adding functions & constants defined in C++
14 | - Saving and loading the abstract syntax tree to disk (similar to an object file)
15 |
16 | Overall, the syntax is mostly identifcal to that of Python, with the one notable difference being that multi-line code blocks must be enclosed in curly braces, tabs and all other whitespace is completely ignored. Additionally, individual elements of vectors are accesed as arrays, not with a "." operator (such as ".x"). The first element of a vector is accesed as "myVector[0]", for example.
17 |
18 | ## Does NOT Support
19 | - While/Do-While Loops
20 | - String Literals
21 | - Structs/Classes
22 | - Arrays
23 | - And Much More :)
24 |
25 | ## Building
26 | The project can be built using the included CMake file, no dependencies are required. The main function shows how to lex, parse, and execute an example script. The example script, which prints prime numbers, can be found in "examples/example.ps".
27 |
--------------------------------------------------------------------------------
/examples/example.ps:
--------------------------------------------------------------------------------
1 | func is_prime(x)
2 | {
3 | for i in range(2, x / 2)
4 | if x % i == 0
5 | ret 0
6 |
7 | ret 1
8 | }
9 |
10 | for i in range(2, 100)
11 | if is_prime(i)
12 | print(i)
--------------------------------------------------------------------------------
/examples/example.psobj:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frozein/PropScript/cbb7613a23b943111c694bd4e3efbbb585609cda/examples/example.psobj
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frozein/PropScript/cbb7613a23b943111c694bd4e3efbbb585609cda/logo.png
--------------------------------------------------------------------------------
/src/definitions.hpp:
--------------------------------------------------------------------------------
1 | #ifndef DEFINITIONS_HPP
2 | #define DEFINITIONS_HPP
3 |
4 | #include
5 | #include
6 |
7 | #define PS_KEYWORD_FUNC "func"
8 | #define PS_KEYWORD_RETURN "ret"
9 | #define PS_KEYWORD_IF "if"
10 | #define PS_KEYWORD_ELSE "else"
11 | #define PS_KEYWORD_FOR "for"
12 | #define PS_KEYWORD_BREAK "break"
13 | #define PS_KEYWORD_CONTINUE "continue"
14 |
15 | //basically operators, but treated as keyword since we dont want lexer to treat them as ops:
16 | #define PS_KEYWORD_IN "in"
17 | #define PS_KEYWORD_AND "and"
18 | #define PS_KEYWORD_OR "or"
19 |
20 | const std::vector PS_KEYWORDS = {
21 | PS_KEYWORD_FUNC,
22 | PS_KEYWORD_RETURN,
23 | PS_KEYWORD_IF,
24 | PS_KEYWORD_ELSE,
25 | PS_KEYWORD_FOR,
26 | PS_KEYWORD_IN
27 | };
28 |
29 | //--------------------------------------------------------------------------------------------------------------------------------//
30 |
31 | #define PS_OP_MULT "*"
32 | #define PS_OP_DIV "/"
33 | #define PS_OP_MOD "%"
34 | #define PS_OP_ADD "+"
35 | #define PS_OP_SUB "-"
36 | #define PS_OP_EQUAL "="
37 | #define PS_OP_MULTEQUAL "*="
38 | #define PS_OP_DIVEQUAL "/="
39 | #define PS_OP_MODEQUAL "%="
40 | #define PS_OP_ADDEQUAL "+="
41 | #define PS_OP_SUBEQUAL "-="
42 | #define PS_OP_LESSTHAN "<"
43 | #define PS_OP_GREATERTHAN ">"
44 | #define PS_OP_LESSTHANEQUAL "<="
45 | #define PS_OP_GREATERTHANEQUAL ">="
46 | #define PS_OP_EQUALITY "=="
47 | #define PS_OP_NONEQUALITY "!="
48 |
49 | #define PS_SEPERATOR_PAREN_OPEN "("
50 | #define PS_SEPERATOR_PAREN_CLOSE ")"
51 | #define PS_SEPERATOR_CURLY_OPEN "{"
52 | #define PS_SEPERATOR_CURLY_CLOSE "}"
53 | #define PS_SEPERATOR_SQUARE_OPEN "["
54 | #define PS_SEPERATOR_SQUARE_CLOSE "]"
55 | #define PS_SEPERATOR_COMMA ","
56 |
57 | #define PS_COMMENT "#"
58 |
59 | //operators as seen by the parser
60 | const std::vector PS_OPERATORS = {
61 | PS_OP_ADD,
62 | PS_OP_SUB,
63 | PS_OP_MULT,
64 | PS_OP_DIV,
65 | PS_OP_MOD,
66 | PS_OP_EQUAL,
67 | PS_OP_ADDEQUAL,
68 | PS_OP_SUBEQUAL,
69 | PS_OP_MULTEQUAL,
70 | PS_OP_DIVEQUAL,
71 | PS_OP_LESSTHAN,
72 | PS_OP_GREATERTHAN,
73 | PS_OP_LESSTHANEQUAL,
74 | PS_OP_GREATERTHANEQUAL,
75 | PS_OP_EQUALITY,
76 | PS_OP_NONEQUALITY,
77 | PS_KEYWORD_AND,
78 | PS_KEYWORD_OR
79 | };
80 |
81 | //operators as seen by the lexer
82 | const std::vector PS_LEXER_OPERATORS = {
83 | PS_OP_ADD,
84 | PS_OP_SUB,
85 | PS_OP_MULT,
86 | PS_OP_DIV,
87 | PS_OP_MOD,
88 | PS_OP_EQUAL,
89 | PS_OP_ADDEQUAL,
90 | PS_OP_SUBEQUAL,
91 | PS_OP_MULTEQUAL,
92 | PS_OP_DIVEQUAL,
93 | PS_OP_LESSTHAN,
94 | PS_OP_GREATERTHAN,
95 | PS_OP_LESSTHANEQUAL,
96 | PS_OP_GREATERTHANEQUAL,
97 | PS_OP_EQUALITY,
98 | PS_OP_NONEQUALITY,
99 |
100 | PS_SEPERATOR_PAREN_OPEN,
101 | PS_SEPERATOR_PAREN_CLOSE,
102 | PS_SEPERATOR_CURLY_OPEN,
103 | PS_SEPERATOR_CURLY_CLOSE,
104 | PS_SEPERATOR_SQUARE_OPEN,
105 | PS_SEPERATOR_SQUARE_CLOSE,
106 | PS_SEPERATOR_COMMA,
107 |
108 | PS_COMMENT //added so lexer can easily identify comments
109 | };
110 |
111 | const std::vector PS_CLOSED_SEPERATORS = {
112 | PS_SEPERATOR_PAREN_CLOSE,
113 | PS_SEPERATOR_CURLY_CLOSE,
114 | PS_SEPERATOR_SQUARE_CLOSE,
115 | PS_SEPERATOR_COMMA
116 | };
117 |
118 | #endif
--------------------------------------------------------------------------------
/src/interpreter.cpp:
--------------------------------------------------------------------------------
1 | #include "propscript.hpp"
2 |
3 | #include
4 | #include
5 |
6 | #define _USE_MATH_DEFINES
7 | #include
8 |
9 | //--------------------------------------------------------------------------------------------------------------------------------//
10 |
11 | enum class PSruntimeError
12 | {
13 | INVALID_ASSIGNMENT,
14 | INVALID_OP,
15 | UNSUPPORTED_NODE_TYPE,
16 | UNDEFINED_VARIABLE,
17 | UNDEFINED_FUNCTION,
18 | INVALID_PARAMS,
19 | INVALID_INDEX,
20 | INVALID_CONDITION,
21 | INVALID_BREAK_CONTINUE,
22 | FUNCTION_REDEFINITION,
23 | ARGUMENT_NAME_REDEFINITION
24 | };
25 |
26 | //executes a set of statements with their own scope
27 | static void _ps_execute_statements(PSast* ast, const std::vector& nodes);
28 | //evaluates a single statement
29 | static PSdata _ps_evaluate_statement(PSast* ast, const PSnode& node, std::vector& addedFuncs, std::vector& addedVars);
30 | //executes a programmer-defined function
31 | static inline PSdata _ps_execute_function(PSast* ast, const PSnode& node, std::vector& addedFuncs, std::vector& addedVars);
32 |
33 | //gets the scalar value from a PSdata struct, or throws an error if the data type is not a scalar
34 | static inline float _ps_get_scalar(PSdata data, PSruntimeError potentialError, const PSnode& node);
35 | //throws an exception and sets the global error flags
36 | static inline void _ps_error(PSruntimeError error, PSnode errorNode);
37 |
38 | //OPERATOR FUNCTIONS:
39 | static inline PSdata _ps_mult(const PSdata& left, const PSdata& right, const PSnode& node);
40 | static inline PSdata _ps_div (const PSdata& left, const PSdata& right, const PSnode& node);
41 | static inline PSdata _ps_mod (const PSdata& left, const PSdata& right, const PSnode& node);
42 |
43 | static inline PSdata _ps_add(const PSdata& left, const PSdata& right, const PSnode& node);
44 | static inline PSdata _ps_sub(const PSdata& left, const PSdata& right, const PSnode& node);
45 |
46 | static inline PSdata _ps_equal(PSast* ast, const PSnode& var, const PSdata& val, std::vector& addedFuncs, std::vector& addedVars);
47 |
48 | static inline PSdata _ps_lessthan (const PSdata& left, const PSdata& right, const PSnode& node);
49 | static inline PSdata _ps_greaterthan (const PSdata& left, const PSdata& right, const PSnode& node);
50 | static inline PSdata _ps_lessthanequal (const PSdata& left, const PSdata& right, const PSnode& node);
51 | static inline PSdata _ps_greaterthanequal(const PSdata& left, const PSdata& right, const PSnode& node);
52 | static inline PSdata _ps_equality (const PSdata& left, const PSdata& right, const PSnode& node);
53 |
54 | static inline PSdata _ps_and(const PSdata& left, const PSdata& right, const PSnode& node);
55 | static inline PSdata _ps_or (const PSdata& left, const PSdata& right, const PSnode& node);
56 |
57 | //DEFAULT LIBRARY FUNCTIONS (more will be added as i need them):
58 | static PSdata _ps_range(const std::vector& params, const PSnode& node, void* userData);
59 | static PSdata _ps_print(const std::vector& params, const PSnode& node, void* userData);
60 | static PSdata _ps_rand (const std::vector& params, const PSnode& node, void* userData);
61 |
62 | static PSdata _ps_int (const std::vector& params, const PSnode& node, void* userData);
63 | static PSdata _ps_vec2 (const std::vector& params, const PSnode& node, void* userData);
64 | static PSdata _ps_vec3 (const std::vector& params, const PSnode& node, void* userData);
65 | static PSdata _ps_vec4 (const std::vector& params, const PSnode& node, void* userData);
66 | static PSdata _ps_quaternion(const std::vector& params, const PSnode& node, void* userData);
67 |
68 | static PSdata _ps_sqrt(const std::vector& params, const PSnode& node, void* userData);
69 | static PSdata _ps_pow (const std::vector& params, const PSnode& node, void* userData);
70 | static PSdata _ps_abs (const std::vector& params, const PSnode& node, void* userData);
71 |
72 | static PSdata _ps_sin (const std::vector& params, const PSnode& node, void* userData);
73 | static PSdata _ps_cos (const std::vector& params, const PSnode& node, void* userData);
74 | static PSdata _ps_tan (const std::vector& params, const PSnode& node, void* userData);
75 | static PSdata _ps_asin(const std::vector& params, const PSnode& node, void* userData);
76 | static PSdata _ps_acos(const std::vector& params, const PSnode& node, void* userData);
77 | static PSdata _ps_atan(const std::vector& params, const PSnode& node, void* userData);
78 |
79 | //--------------------------------------------------------------------------------------------------------------------------------//
80 |
81 | static PSruntimeError g_psError;
82 | static PSnode g_psErrorNode;
83 |
84 | const std::vector PS_DEFAULT_LIB_FUNCTIONS = {
85 | {"range" , _ps_range},
86 | {"print" , _ps_print},
87 | {"rand" , _ps_rand},
88 | {"int" , _ps_int},
89 | {"vec2" , _ps_vec2},
90 | {"vec3" , _ps_vec3},
91 | {"vec4" , _ps_vec4},
92 | {"quaternion", _ps_quaternion},
93 | {"sqrt" , _ps_sqrt},
94 | {"pow" , _ps_pow},
95 | {"abs" , _ps_abs},
96 | {"sin" , _ps_sin},
97 | {"cos" , _ps_cos},
98 | {"tan" , _ps_tan},
99 | {"asin" , _ps_asin},
100 | {"acos" , _ps_acos},
101 | {"atan" , _ps_atan},
102 | };
103 |
104 | const std::vector PS_DEFAULT_CONSTANTS = {
105 | {"M_PI" , PSdata(PSdata::FLOAT, (float)M_PI)},
106 | {"M_TAU", PSdata(PSdata::FLOAT, 2.0f * (float)M_PI)},
107 | {"M_E" , PSdata(PSdata::FLOAT, (float)M_E) }
108 | };
109 |
110 | static std::unordered_map g_psLibFunctions;
111 | static std::unordered_map g_psConstants;
112 | static void* g_psLibFunctionUserData = nullptr;
113 |
114 | static std::unordered_map g_psFunctions;
115 | static std::unordered_map g_psVariables;
116 |
117 | static bool g_psInLoop = false;
118 |
119 | static bool g_psReturnFlag = false;
120 | static bool g_psBreakFlag = false;
121 | static bool g_psContinueFlag = false;
122 | static PSdata g_psReturnVal = {};
123 |
124 | //--------------------------------------------------------------------------------------------------------------------------------//
125 |
126 | void ps_set_functions(const std::vector& functions)
127 | {
128 | g_psLibFunctions.clear();
129 |
130 | for(int i = 0; i < PS_DEFAULT_LIB_FUNCTIONS.size(); i++)
131 | g_psLibFunctions[PS_DEFAULT_LIB_FUNCTIONS[i].name] = PS_DEFAULT_LIB_FUNCTIONS[i];
132 |
133 | for(int i = 0; i < functions.size(); i++)
134 | g_psLibFunctions[functions[i].name] = functions[i];
135 | }
136 |
137 | void ps_set_constants(const std::vector& constants)
138 | {
139 | g_psConstants.clear();
140 |
141 | for(int i = 0; i < PS_DEFAULT_CONSTANTS.size(); i++)
142 | g_psConstants[PS_DEFAULT_CONSTANTS[i].name] = PS_DEFAULT_CONSTANTS[i].val;
143 |
144 | for(int i = 0; i < constants.size(); i++)
145 | g_psConstants[constants[i].name] = constants[i].val;
146 | }
147 |
148 | void ps_set_function_user_data(void* userData)
149 | {
150 | g_psLibFunctionUserData = userData;
151 | }
152 |
153 | void ps_throw_invalid_param_error(PSnode node)
154 | {
155 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
156 | }
157 |
158 | void ps_execute(PSast* ast)
159 | {
160 | if(g_psLibFunctions.size() == 0)
161 | ps_set_functions({});
162 |
163 | if(g_psConstants.size() == 0)
164 | ps_set_constants({});
165 |
166 | try
167 | {
168 | _ps_execute_statements(ast, ast->parentNodes);
169 |
170 | if(g_psReturnFlag)
171 | g_psReturnFlag = false;
172 | }
173 | catch(std::exception e)
174 | {
175 | std::string errMsg;
176 | PSnode testNode = g_psErrorNode;
177 | switch(g_psError)
178 | {
179 | case PSruntimeError::INVALID_ASSIGNMENT:
180 | errMsg = "INVALID ASSIGNMENT";
181 | break;
182 | case PSruntimeError::INVALID_OP:
183 | errMsg = "INVALID OPERATION";
184 | break;
185 | case PSruntimeError::UNSUPPORTED_NODE_TYPE:
186 | errMsg = "UNSUPPORTED NODE TYPE (i must've forgot to implement something in the interpreter)";
187 | break;
188 | case PSruntimeError::UNDEFINED_VARIABLE:
189 | errMsg = "UNDEFINED VARIABLE";
190 | break;
191 | case PSruntimeError::UNDEFINED_FUNCTION:
192 | errMsg = "UNDEFINED FUNCTION";
193 | break;
194 | case PSruntimeError::INVALID_PARAMS:
195 | errMsg = "INVALID PARAMETERS";
196 | break;
197 | case PSruntimeError::INVALID_INDEX:
198 | errMsg = "INVALID INDEX";
199 | break;
200 | case PSruntimeError::INVALID_CONDITION:
201 | errMsg = "INVALID CONDITION";
202 | break;
203 | case PSruntimeError::INVALID_BREAK_CONTINUE:
204 | errMsg = "INVALID BREAK/CONTINUE";
205 | break;
206 | case PSruntimeError::FUNCTION_REDEFINITION:
207 | errMsg = "FUNCTION REDEFINITION";
208 | break;
209 | case PSruntimeError::ARGUMENT_NAME_REDEFINITION:
210 | errMsg = "ARGUMENT NAME REDEFINITION";
211 | break;
212 | }
213 |
214 | std::cout << "PROPSCRIPT RUNTIME ERROR: " << errMsg << " ON LINE " << g_psErrorNode.lineNum << std::endl;
215 |
216 | //there might be uncleared vars/funcs if we run into an error:
217 | g_psFunctions.clear();
218 | g_psVariables.clear();
219 | }
220 | }
221 |
222 | //--------------------------------------------------------------------------------------------------------------------------------//
223 |
224 | static void _ps_execute_statements(PSast* ast, const std::vector& nodes)
225 | {
226 | std::vector addedFuncs;
227 | std::vector addedVars;
228 |
229 | for(int i = 0; i < nodes.size(); i++)
230 | {
231 | _ps_evaluate_statement(ast, ast->nodePool[nodes[i]], addedFuncs, addedVars);
232 |
233 | if(g_psReturnFlag || g_psBreakFlag || g_psContinueFlag)
234 | break;
235 | }
236 |
237 | for(int i = 0; i < addedFuncs.size(); i++)
238 | g_psFunctions.erase(addedFuncs[i]);
239 | for(int i = 0; i < addedVars.size(); i++)
240 | g_psVariables.erase(addedVars[i]);
241 | }
242 |
243 | static PSdata _ps_evaluate_statement(PSast* ast, const PSnode& node, std::vector& addedFuncs, std::vector& addedVars)
244 | {
245 | switch(node.type)
246 | {
247 | case PSnode::OP:
248 | {
249 | if(node.op.type == PSnode::OP::EQUAL)
250 | return _ps_equal(ast, ast->nodePool[node.op.left], _ps_evaluate_statement(ast, ast->nodePool[node.op.right], addedFuncs, addedVars), addedFuncs, addedVars);
251 |
252 | PSdata left = _ps_evaluate_statement(ast, ast->nodePool[node.op.left ], addedFuncs, addedVars);
253 | PSdata right = _ps_evaluate_statement(ast, ast->nodePool[node.op.right], addedFuncs, addedVars);
254 |
255 | switch(node.op.type)
256 | {
257 | case PSnode::OP::MULT:
258 | return _ps_mult(left, right, node);
259 | case PSnode::OP::DIV:
260 | return _ps_div(left, right, node);
261 | case PSnode::OP::MOD:
262 | return _ps_mod(left, right, node);
263 | case PSnode::OP::ADD:
264 | return _ps_add(left, right, node);
265 | case PSnode::OP::SUB:
266 | return _ps_sub(left, right, node);
267 | case PSnode::OP::MULTEQUAL:
268 | return _ps_equal(ast, ast->nodePool[node.op.left], _ps_mult(left, right, node), addedFuncs, addedVars);
269 | case PSnode::OP::DIVEQUAL:
270 | return _ps_equal(ast, ast->nodePool[node.op.left], _ps_div(left, right, node), addedFuncs, addedVars);
271 | case PSnode::OP::MODEQUAL:
272 | return _ps_equal(ast, ast->nodePool[node.op.left], _ps_mod(left, right, node), addedFuncs, addedVars);
273 | case PSnode::OP::ADDEQUAL:
274 | return _ps_equal(ast, ast->nodePool[node.op.left], _ps_add(left, right, node), addedFuncs, addedVars);
275 | case PSnode::OP::SUBEQUAL:
276 | return _ps_equal(ast, ast->nodePool[node.op.left], _ps_sub(left, right, node), addedFuncs, addedVars);
277 | case PSnode::OP::LESSTHAN:
278 | return _ps_lessthan(left, right, node);
279 | case PSnode::OP::GREATERTHAN:
280 | return _ps_greaterthan(left, right, node);
281 | case PSnode::OP::LESSTHANEQUAL:
282 | return _ps_lessthanequal(left, right, node);
283 | case PSnode::OP::GREATERTHANEQUAL:
284 | return _ps_greaterthanequal(left, right, node);
285 | case PSnode::OP::EQUALITY:
286 | return _ps_equality(left, right, node);
287 | case PSnode::OP::NONEQUALITY:
288 | {
289 | PSdata equality = _ps_equality(left, right, node);
290 | equality.intVal = !equality.intVal;
291 | return equality;
292 | }
293 | case PSnode::OP::AND:
294 | return _ps_and(left, right, node);
295 | case PSnode::OP::OR:
296 | return _ps_or(left, right, node);
297 | default:
298 | _ps_error(PSruntimeError::UNSUPPORTED_NODE_TYPE, node);
299 | }
300 |
301 | return {};
302 | }
303 | case PSnode::ID:
304 | {
305 | if(node.id.type == PSnode::ID::FUNC)
306 | {
307 | std::vector params;
308 | for(int i = 0; i < node.id.params.size(); i++)
309 | params.push_back(_ps_evaluate_statement(ast, ast->nodePool[node.id.params[i]], addedFuncs, addedVars));
310 |
311 | if(g_psLibFunctions.count(node.id.name) > 0)
312 | return g_psLibFunctions[node.id.name].func(params, node, g_psLibFunctionUserData);
313 | else if(g_psFunctions.count(node.id.name) > 0)
314 | return _ps_execute_function(ast, node, addedFuncs, addedVars);
315 | else
316 | _ps_error(PSruntimeError::UNDEFINED_FUNCTION, node);
317 | }
318 | else
319 | {
320 | if(g_psConstants.count(node.id.name) == 1)
321 | return g_psConstants[node.id.name];
322 |
323 | if(g_psVariables.count(node.id.name) == 0)
324 | _ps_error(PSruntimeError::UNDEFINED_VARIABLE, node);
325 |
326 | PSdata var = g_psVariables[node.id.name];
327 | if(node.id.params.size() == 0)
328 | return var;
329 |
330 | if(var.type == PSdata::INT || var.type == PSdata::FLOAT)
331 | _ps_error(PSruntimeError::INVALID_INDEX, node);
332 |
333 | PSdata index = _ps_evaluate_statement(ast, ast->nodePool[node.id.params[0]], addedFuncs, addedVars);
334 | if(index.type != PSdata::INT)
335 | _ps_error(PSruntimeError::INVALID_INDEX, node);
336 |
337 | PSdata result;
338 | result.type = PSdata::FLOAT;
339 |
340 | if(var.type == PSdata::VEC2 && index.intVal <= 1)
341 | result.floatVal = *((float*)&var.vec2Val + index.intVal);
342 | else if(var.type == PSdata::VEC3 && index.intVal <= 2)
343 | result.floatVal = *((float*)&var.vec3Val + index.intVal);
344 | else if(var.type == PSdata::VEC4 && index.intVal <= 3)
345 | result.floatVal = *((float*)&var.vec4Val + index.intVal);
346 | else
347 | _ps_error(PSruntimeError::INVALID_INDEX, node);
348 |
349 | return result;
350 | }
351 |
352 | return {};
353 | }
354 | case PSnode::NUMBER:
355 | {
356 | PSdata num;
357 |
358 | if(node.literal.type == PSnode::Literal::INT)
359 | {
360 | num.type = PSdata::INT;
361 | num.intVal = node.literal.intNum;
362 | }
363 | else
364 | {
365 | num.type = PSdata::FLOAT;
366 | num.floatVal = node.literal.floatNum;
367 | }
368 |
369 | return num;
370 | }
371 | case PSnode::KEYWORD:
372 | {
373 | switch(node.keyword.type)
374 | {
375 | case PSnode::Keyword::IF:
376 | {
377 | PSdata condition = _ps_evaluate_statement(ast, ast->nodePool[node.keyword.condition], addedVars, addedFuncs);
378 | if(_ps_get_scalar(condition, PSruntimeError::INVALID_CONDITION, node) != 0.0f)
379 | _ps_execute_statements(ast, node.keyword.code);
380 | else if(node.keyword.hasElse)
381 | _ps_execute_statements(ast, node.keyword.elseCode);
382 |
383 | return {};
384 | }
385 | case PSnode::Keyword::FOR:
386 | {
387 | if(ast->nodePool[node.keyword.condition].type != PSnode::OP ||
388 | ast->nodePool[node.keyword.condition].op.type != PSnode::OP::IN)
389 | _ps_error(PSruntimeError::INVALID_CONDITION, node);
390 |
391 | PSnode var = ast->nodePool[ast->nodePool[node.keyword.condition].op.left];
392 | if(var.type != PSnode::ID || var.id.type != PSnode::ID::VAR || g_psVariables.count(var.id.name) > 0)
393 | _ps_error(PSruntimeError::INVALID_CONDITION, node);
394 |
395 | std::vector forFuncs;
396 | std::vector forVars;
397 |
398 | PSdata range = _ps_evaluate_statement(ast, ast->nodePool[ast->nodePool[node.keyword.condition].op.right], forFuncs, forVars);
399 | if(range.type != PSdata::VEC2)
400 | _ps_error(PSruntimeError::INVALID_CONDITION, node);
401 |
402 | bool outermostLoop = false;
403 | if(!g_psInLoop)
404 | {
405 | outermostLoop = true;
406 | g_psInLoop = true;
407 | }
408 |
409 | int32_t min = (int32_t)ceilf (range.vec2Val.x);
410 | int32_t max = (int32_t)floorf(range.vec2Val.y);
411 | for(int32_t i = min; i <= max; i++)
412 | {
413 | PSdata val;
414 | val.type = PSdata::INT;
415 | val.intVal = i;
416 | _ps_equal(ast, var, val, forFuncs, forVars);
417 |
418 | _ps_execute_statements(ast, node.keyword.code);
419 |
420 | if(g_psBreakFlag)
421 | {
422 | g_psBreakFlag = false;
423 | break;
424 | }
425 |
426 | if(g_psContinueFlag)
427 | {
428 | g_psContinueFlag = false;
429 | continue;
430 | }
431 | }
432 |
433 | for(int i = 0; i < forFuncs.size(); i++)
434 | g_psFunctions.erase(forFuncs[i]);
435 | for(int i = 0; i < forVars.size(); i++)
436 | g_psVariables.erase(forVars[i]);
437 |
438 | if(outermostLoop)
439 | g_psInLoop = false;
440 |
441 | return {};
442 | }
443 | case PSnode::Keyword::FUNC:
444 | {
445 | if(g_psFunctions.count(node.keyword.name) > 0)
446 | _ps_error(PSruntimeError::FUNCTION_REDEFINITION, node);
447 |
448 | g_psFunctions[node.keyword.name] = node;
449 | addedFuncs.push_back(node.keyword.name);
450 |
451 | return {};
452 | }
453 | case PSnode::Keyword::RETURN:
454 | {
455 | g_psReturnFlag = true;
456 |
457 | if(node.keyword.returnVal < UINT32_MAX)
458 | g_psReturnVal = _ps_evaluate_statement(ast, ast->nodePool[node.keyword.returnVal], addedFuncs, addedVars);
459 | else
460 | g_psReturnVal = {};
461 |
462 | return {};
463 | }
464 | case PSnode::Keyword::BREAK:
465 | {
466 | if(!g_psInLoop)
467 | _ps_error(PSruntimeError::INVALID_BREAK_CONTINUE, node);
468 |
469 | g_psBreakFlag = true;
470 | return {};
471 | }
472 | case PSnode::Keyword::CONTINUE:
473 | {
474 | if(!g_psInLoop)
475 | _ps_error(PSruntimeError::INVALID_BREAK_CONTINUE, node);
476 |
477 | g_psContinueFlag = true;
478 | return {};
479 | }
480 | default:
481 | _ps_error(PSruntimeError::UNSUPPORTED_NODE_TYPE, node);
482 | }
483 |
484 | return {};
485 | }
486 | default:
487 | _ps_error(PSruntimeError::UNSUPPORTED_NODE_TYPE, node);
488 | return {};
489 | }
490 | }
491 |
492 | static inline PSdata _ps_execute_function(PSast* ast, const PSnode& node, std::vector& addedFuncs, std::vector& addedVars)
493 | {
494 | PSnode funcNode = g_psFunctions[node.id.name];
495 |
496 | if(funcNode.keyword.paramNames.size() != node.id.params.size())
497 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
498 |
499 | std::unordered_map funcVars;
500 |
501 | for(int i = 0; i < funcNode.keyword.paramNames.size(); i++)
502 | {
503 | if(funcVars.count(funcNode.keyword.paramNames[i]) > 0)
504 | _ps_error(PSruntimeError::ARGUMENT_NAME_REDEFINITION, funcNode);
505 |
506 | funcVars[funcNode.keyword.paramNames[i]] = _ps_evaluate_statement(ast, ast->nodePool[node.id.params[i]], addedFuncs, addedVars);
507 | }
508 |
509 | funcVars.swap(g_psVariables);
510 | _ps_execute_statements(ast, funcNode.keyword.code);
511 | funcVars.swap(g_psVariables);
512 |
513 | for(int i = 0; i < funcNode.keyword.paramNames.size(); i++)
514 | g_psVariables.erase(funcNode.keyword.paramNames[i]);
515 |
516 | if(g_psReturnFlag)
517 | {
518 | g_psReturnFlag = false;
519 | return g_psReturnVal;
520 | }
521 | else
522 | return {};
523 | }
524 |
525 | //--------------------------------------------------------------------------------------------------------------------------------//
526 |
527 | static inline float _ps_get_scalar(PSdata data, PSruntimeError potentialError, const PSnode& node)
528 | {
529 | if(data.type == PSdata::INT)
530 | return (float)data.intVal;
531 | else if(data.type == PSdata::FLOAT)
532 | return data.floatVal;
533 | else
534 | _ps_error(potentialError, node);
535 |
536 | return 0.0f;
537 | }
538 |
539 | static inline void _ps_error(PSruntimeError error, PSnode errorNode)
540 | {
541 | g_psError = error;
542 | g_psErrorNode = errorNode;
543 | throw std::exception();
544 | }
545 |
546 | //--------------------------------------------------------------------------------------------------------------------------------//
547 |
548 | static inline PSdata _ps_mult(const PSdata& left, const PSdata& right, const PSnode& node)
549 | {
550 | PSdata result;
551 |
552 | if(left.type == PSdata::INT && right.type == PSdata::INT)
553 | {
554 | result.type = PSdata::INT;
555 | result.intVal = left.intVal * right.intVal;
556 | }
557 | else if(left.type == PSdata::FLOAT && right.type == PSdata::FLOAT)
558 | {
559 | result.type = PSdata::FLOAT;
560 | result.floatVal = left.floatVal * right.floatVal;
561 | }
562 | else if((left.type == PSdata::INT && right.type == PSdata::FLOAT) ||
563 | (left.type == PSdata::FLOAT && right.type == PSdata::INT))
564 | {
565 | result.type = PSdata::FLOAT;
566 | result.floatVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) * _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
567 | }
568 | else if(left.type == PSdata::VEC2 && right.type == PSdata::VEC2)
569 | {
570 | result.type = PSdata::VEC2;
571 | result.vec2Val = left.vec2Val * right.vec2Val;
572 | }
573 | else if(left.type == PSdata::VEC3 && right.type == PSdata::VEC3)
574 | {
575 | result.type = PSdata::VEC3;
576 | result.vec3Val = left.vec3Val * right.vec3Val;
577 | }
578 | else if(left.type == PSdata::VEC4 && right.type == PSdata::VEC4)
579 | {
580 | result.type = PSdata::VEC4;
581 | result.vec4Val = left.vec4Val * right.vec4Val;
582 | }
583 | else if(left.type == PSdata::QUATERNION && right.type == PSdata::QUATERNION)
584 | {
585 | result.type = PSdata::QUATERNION;
586 | result.quatVal = left.quatVal * right.quatVal;
587 | }
588 | else if(left.type == PSdata::VEC2 && (right.type == PSdata::INT || right.type == PSdata::FLOAT))
589 | {
590 | result.type = PSdata::VEC2;
591 | result.vec2Val = left.vec2Val * _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
592 | }
593 | else if((left.type == PSdata::INT || left.type == PSdata::FLOAT) && right.type == PSdata::VEC2)
594 | {
595 | result.type = PSdata::VEC2;
596 | result.vec2Val = right.vec2Val * _ps_get_scalar(left, PSruntimeError::INVALID_OP, node);
597 | }
598 | else if(left.type == PSdata::VEC3 && (right.type == PSdata::INT || right.type == PSdata::FLOAT))
599 | {
600 | result.type = PSdata::VEC3;
601 | result.vec3Val = left.vec3Val * _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
602 | }
603 | else if((left.type == PSdata::INT || left.type == PSdata::FLOAT) && right.type == PSdata::VEC3)
604 | {
605 | result.type = PSdata::VEC3;
606 | result.vec3Val = right.vec3Val * _ps_get_scalar(left, PSruntimeError::INVALID_OP, node);
607 | }
608 | else if(left.type == PSdata::VEC4 && (right.type == PSdata::INT || right.type == PSdata::FLOAT))
609 | {
610 | result.type = PSdata::VEC4;
611 | result.vec4Val = left.vec4Val * _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
612 | }
613 | else if((left.type == PSdata::INT || left.type == PSdata::FLOAT) && right.type == PSdata::VEC4)
614 | {
615 | result.type = PSdata::VEC4;
616 | result.vec4Val = right.vec4Val * _ps_get_scalar(left, PSruntimeError::INVALID_OP, node);
617 | }
618 | else if(left.type == PSdata::QUATERNION && (right.type == PSdata::INT || right.type == PSdata::FLOAT))
619 | {
620 | result.type = PSdata::QUATERNION;
621 | result.quatVal = left.quatVal * _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
622 | }
623 | else if((left.type == PSdata::INT || left.type == PSdata::FLOAT) && right.type == PSdata::QUATERNION)
624 | {
625 | result.type = PSdata::QUATERNION;
626 | result.quatVal = right.quatVal * _ps_get_scalar(left, PSruntimeError::INVALID_OP, node);
627 | }
628 | else
629 | _ps_error(PSruntimeError::INVALID_OP, node);
630 |
631 | return result;
632 | }
633 |
634 | static inline PSdata _ps_div(const PSdata& left, const PSdata& right, const PSnode& node)
635 | {
636 | PSdata result;
637 |
638 | if(left.type == PSdata::INT && right.type == PSdata::INT)
639 | {
640 | result.type = PSdata::INT;
641 | result.intVal = left.intVal / right.intVal;
642 | }
643 | else if(left.type == PSdata::FLOAT && right.type == PSdata::FLOAT)
644 | {
645 | result.type = PSdata::FLOAT;
646 | result.floatVal = left.floatVal / right.floatVal;
647 | }
648 | else if((left.type == PSdata::INT && right.type == PSdata::FLOAT) ||
649 | (left.type == PSdata::FLOAT && right.type == PSdata::INT))
650 | {
651 | result.type = PSdata::FLOAT;
652 | result.floatVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) / _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
653 | }
654 | else if(left.type == PSdata::VEC2 && right.type == PSdata::VEC2)
655 | {
656 | result.type = PSdata::VEC2;
657 | result.vec2Val = left.vec2Val / right.vec2Val;
658 | }
659 | else if(left.type == PSdata::VEC3 && right.type == PSdata::VEC3)
660 | {
661 | result.type = PSdata::VEC3;
662 | result.vec3Val = left.vec3Val / right.vec3Val;
663 | }
664 | else if(left.type == PSdata::VEC4 && right.type == PSdata::VEC4)
665 | {
666 | result.type = PSdata::VEC4;
667 | result.vec4Val = left.vec4Val / right.vec4Val;
668 | }
669 | else if(left.type == PSdata::VEC2 && (right.type == PSdata::INT || right.type == PSdata::FLOAT))
670 | {
671 | result.type = PSdata::VEC2;
672 | result.vec2Val = left.vec2Val / _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
673 | }
674 | else if((left.type == PSdata::INT || left.type == PSdata::FLOAT) && right.type == PSdata::VEC2)
675 | {
676 | result.type = PSdata::VEC2;
677 | result.vec2Val = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) / right.vec2Val;
678 | }
679 | else if(left.type == PSdata::VEC3 && (right.type == PSdata::INT || right.type == PSdata::FLOAT))
680 | {
681 | result.type = PSdata::VEC3;
682 | result.vec3Val = left.vec3Val / _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
683 | }
684 | else if((left.type == PSdata::INT || left.type == PSdata::FLOAT) && right.type == PSdata::VEC3)
685 | {
686 | result.type = PSdata::VEC3;
687 | result.vec3Val = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) / right.vec3Val;
688 | }
689 | else if(left.type == PSdata::VEC4 && (right.type == PSdata::INT || right.type == PSdata::FLOAT))
690 | {
691 | result.type = PSdata::VEC4;
692 | result.vec4Val = left.vec4Val / _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
693 | }
694 | else if((left.type == PSdata::INT || left.type == PSdata::FLOAT) && right.type == PSdata::VEC4)
695 | {
696 | result.type = PSdata::VEC4;
697 | result.vec4Val = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) / right.vec4Val;
698 | }
699 | else if(left.type == PSdata::QUATERNION && (right.type == PSdata::INT || right.type == PSdata::FLOAT))
700 | {
701 | result.type = PSdata::QUATERNION;
702 | result.quatVal = left.quatVal / _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
703 | }
704 | else if((left.type == PSdata::INT || left.type == PSdata::FLOAT) && right.type == PSdata::QUATERNION)
705 | {
706 | result.type = PSdata::QUATERNION;
707 | result.quatVal = right.quatVal / _ps_get_scalar(left, PSruntimeError::INVALID_OP, node);
708 | }
709 | else
710 | _ps_error(PSruntimeError::INVALID_OP, node);
711 |
712 | return result;
713 | }
714 |
715 | static inline PSdata _ps_mod(const PSdata& left, const PSdata& right, const PSnode& node)
716 | {
717 | PSdata result;
718 |
719 | if(left.type == PSdata::INT && right.type == PSdata::INT)
720 | {
721 | result.type = PSdata::INT;
722 | result.intVal = left.intVal % right.intVal;
723 | }
724 | else
725 | _ps_error(PSruntimeError::INVALID_OP, node);
726 |
727 | return result;
728 | }
729 |
730 | static inline PSdata _ps_add(const PSdata& left, const PSdata& right, const PSnode& node)
731 | {
732 | PSdata result;
733 |
734 | if(left.type == PSdata::INT && right.type == PSdata::INT)
735 | {
736 | result.type = PSdata::INT;
737 | result.intVal = left.intVal + right.intVal;
738 | }
739 | else if(left.type == PSdata::FLOAT && right.type == PSdata::FLOAT)
740 | {
741 | result.type = PSdata::FLOAT;
742 | result.floatVal = left.floatVal + right.floatVal;
743 | }
744 | else if((left.type == PSdata::INT && right.type == PSdata::FLOAT) ||
745 | (left.type == PSdata::FLOAT && right.type == PSdata::INT))
746 | {
747 | result.type = PSdata::FLOAT;
748 | result.floatVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) + _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
749 | }
750 | else if(left.type == PSdata::VEC2 && right.type == PSdata::VEC2)
751 | {
752 | result.type = PSdata::VEC2;
753 | result.vec2Val = left.vec2Val + right.vec2Val;
754 | }
755 | else if(left.type == PSdata::VEC3 && right.type == PSdata::VEC3)
756 | {
757 | result.type = PSdata::VEC3;
758 | result.vec3Val = left.vec3Val + right.vec3Val;
759 | }
760 | else if(left.type == PSdata::VEC4 && right.type == PSdata::VEC4)
761 | {
762 | result.type = PSdata::VEC4;
763 | result.vec4Val = left.vec4Val + right.vec4Val;
764 | }
765 | else if(left.type == PSdata::QUATERNION && right.type == PSdata::QUATERNION)
766 | {
767 | result.type = PSdata::QUATERNION;
768 | result.quatVal = left.quatVal + right.quatVal;
769 | }
770 | else
771 | _ps_error(PSruntimeError::INVALID_OP, node);
772 |
773 | return result;
774 | }
775 |
776 | static inline PSdata _ps_sub(const PSdata& left, const PSdata& right, const PSnode& node)
777 | {
778 | PSdata result;
779 |
780 | if(left.type == PSdata::INT && right.type == PSdata::INT)
781 | {
782 | result.type = PSdata::INT;
783 | result.intVal = left.intVal - right.intVal;
784 | }
785 | else if(left.type == PSdata::FLOAT && right.type == PSdata::FLOAT)
786 | {
787 | result.type = PSdata::FLOAT;
788 | result.floatVal = left.floatVal - right.floatVal;
789 | }
790 | else if((left.type == PSdata::INT && right.type == PSdata::FLOAT) ||
791 | (left.type == PSdata::FLOAT && right.type == PSdata::INT))
792 | {
793 | result.type = PSdata::FLOAT;
794 | result.floatVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) - _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
795 | }
796 | else if(left.type == PSdata::VEC2 && right.type == PSdata::VEC2)
797 | {
798 | result.type = PSdata::VEC2;
799 | result.vec2Val = left.vec2Val - right.vec2Val;
800 | }
801 | else if(left.type == PSdata::VEC3 && right.type == PSdata::VEC3)
802 | {
803 | result.type = PSdata::VEC3;
804 | result.vec3Val = left.vec3Val - right.vec3Val;
805 | }
806 | else if(left.type == PSdata::VEC4 && right.type == PSdata::VEC4)
807 | {
808 | result.type = PSdata::VEC4;
809 | result.vec4Val = left.vec4Val - right.vec4Val;
810 | }
811 | else if(left.type == PSdata::QUATERNION && right.type == PSdata::QUATERNION)
812 | {
813 | result.type = PSdata::QUATERNION;
814 | result.quatVal = left.quatVal - right.quatVal;
815 | }
816 | else
817 | _ps_error(PSruntimeError::INVALID_OP, node);
818 |
819 | return result;
820 | }
821 |
822 | static inline PSdata _ps_equal(PSast* ast, const PSnode& var, const PSdata& val, std::vector& addedFuncs, std::vector& addedVars)
823 | {
824 | if(var.type != PSnode::ID || var.id.type != PSnode::ID::VAR)
825 | _ps_error(PSruntimeError::INVALID_ASSIGNMENT, var);
826 |
827 | if(val.type == PSdata::VOID)
828 | _ps_error(PSruntimeError::INVALID_ASSIGNMENT, var);
829 |
830 | if(g_psVariables.count(var.id.name) > 0)
831 | {
832 | if(g_psVariables[var.id.name].type == PSdata::FLOAT && val.type == PSdata::INT)
833 | {
834 | g_psVariables[var.id.name].floatVal = (float)val.intVal;
835 | return g_psVariables[var.id.name];
836 | }
837 | else if(var.id.params.size() == 1)
838 | {
839 | PSdata index = _ps_evaluate_statement(ast, ast->nodePool[var.id.params[0]], addedFuncs, addedVars);
840 | if(index.type != PSdata::INT)
841 | _ps_error(PSruntimeError::INVALID_INDEX, var);
842 |
843 | PSdata* varRef = &g_psVariables[var.id.name];
844 | float floatVal = _ps_get_scalar(val, PSruntimeError::INVALID_ASSIGNMENT, var);
845 |
846 | if(varRef->type == PSdata::VEC2 && index.intVal <= 1)
847 | *((float*)&varRef->vec2Val + index.intVal) = floatVal;
848 | else if(varRef->type == PSdata::VEC3 && index.intVal <= 2)
849 | *((float*)&varRef->vec3Val + index.intVal) = floatVal;
850 | else if(varRef->type == PSdata::VEC4 && index.intVal <= 3)
851 | *((float*)&varRef->vec4Val + index.intVal) = floatVal;
852 | else
853 | _ps_error(PSruntimeError::INVALID_INDEX, var);
854 |
855 | PSdata result;
856 | result.type = PSdata::FLOAT;
857 | result.floatVal = floatVal;
858 | return result;
859 | }
860 | else if(g_psVariables[var.id.name].type != val.type)
861 | _ps_error(PSruntimeError::INVALID_ASSIGNMENT, var);
862 | }
863 | else if(var.id.params.size() != 0)
864 | _ps_error(PSruntimeError::INVALID_INDEX, var);
865 | else
866 | addedVars.push_back(var.id.name);
867 |
868 | g_psVariables[var.id.name] = val;
869 | return val;
870 | }
871 |
872 | static inline PSdata _ps_lessthan(const PSdata& left, const PSdata& right, const PSnode& node)
873 | {
874 | PSdata result;
875 | result.type = PSdata::INT;
876 |
877 | result.intVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) < _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
878 |
879 | return result;
880 | }
881 |
882 | static inline PSdata _ps_greaterthan(const PSdata& left, const PSdata& right, const PSnode& node)
883 | {
884 | PSdata result;
885 | result.type = PSdata::INT;
886 |
887 | result.intVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) > _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
888 |
889 | return result;
890 | }
891 |
892 | static inline PSdata _ps_lessthanequal(const PSdata& left, const PSdata& right, const PSnode& node)
893 | {
894 | PSdata result;
895 | result.type = PSdata::INT;
896 |
897 | result.intVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) <= _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
898 |
899 | return result;
900 | }
901 |
902 | static inline PSdata _ps_greaterthanequal(const PSdata& left, const PSdata& right, const PSnode& node)
903 | {
904 | PSdata result;
905 | result.type = PSdata::INT;
906 |
907 | result.intVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) >= _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
908 |
909 | return result;
910 | }
911 |
912 | static inline PSdata _ps_equality(const PSdata& left, const PSdata& right, const PSnode& node)
913 | {
914 | PSdata result;
915 | result.type = PSdata::INT;
916 |
917 | if((left.type == PSdata::INT || left.type == PSdata::FLOAT) && (right.type == PSdata::INT || right.type == PSdata::FLOAT))
918 | result.intVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) == _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
919 | else if(left.type == PSdata::VEC2 && right.type == PSdata::VEC2)
920 | result.intVal = left.vec2Val == right.vec2Val;
921 | else if(left.type == PSdata::VEC3 && right.type == PSdata::VEC3)
922 | result.intVal = left.vec3Val == right.vec3Val;
923 | else if(left.type == PSdata::VEC4 && right.type == PSdata::VEC4)
924 | result.intVal = left.vec4Val == right.vec4Val;
925 | else
926 | _ps_error(PSruntimeError::INVALID_OP, node);
927 |
928 | return result;
929 | }
930 |
931 | static inline PSdata _ps_and(const PSdata& left, const PSdata& right, const PSnode& node)
932 | {
933 | PSdata result;
934 | result.type = PSdata::INT;
935 |
936 | result.intVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) && _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
937 |
938 | return result;
939 | }
940 |
941 | static inline PSdata _ps_or (const PSdata& left, const PSdata& right, const PSnode& node)
942 | {
943 | PSdata result;
944 | result.type = PSdata::INT;
945 |
946 | result.intVal = _ps_get_scalar(left, PSruntimeError::INVALID_OP, node) || _ps_get_scalar(right, PSruntimeError::INVALID_OP, node);
947 |
948 | return result;
949 | }
950 |
951 | //--------------------------------------------------------------------------------------------------------------------------------//
952 |
953 | static PSdata _ps_range(const std::vector& params, const PSnode& node, void* userData)
954 | {
955 | if(params.size() != 2 || params[0].type != PSdata::INT || params[1].type != PSdata::INT)
956 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
957 |
958 | PSdata result;
959 | result.type = PSdata::VEC2;
960 | result.vec2Val = {(float)params[0].intVal, (float)params[1].intVal};
961 | return result;
962 | }
963 |
964 | static PSdata _ps_print(const std::vector& params, const PSnode& node, void* userData)
965 | {
966 | for(int i = 0; i < params.size(); i++)
967 | {
968 | switch(params[i].type)
969 | {
970 | case PSdata::INT:
971 | std::cout << params[i].intVal;
972 | break;
973 | case PSdata::FLOAT:
974 | std::cout << params[i].floatVal;
975 | break;
976 | case PSdata::VEC2:
977 | std::cout << "(" << params[i].vec2Val.x << ", " << params[i].vec2Val.y << ")";
978 | break;
979 | case PSdata::VEC3:
980 | std::cout << "(" << params[i].vec3Val.x << ", " << params[i].vec3Val.y << ", " << params[i].vec3Val.z << ")";
981 | break;
982 | case PSdata::VEC4:
983 | std::cout << "(" << params[i].vec4Val.x << ", " << params[i].vec4Val.y << ", " << params[i].vec4Val.z << ", " << params[i].vec4Val.w << ")";
984 | break;
985 | default:
986 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
987 | }
988 |
989 | if(i < params.size() - 1)
990 | std::cout << ", ";
991 | }
992 |
993 | std::cout << std::endl;
994 |
995 | return {};
996 | }
997 |
998 | float _ps_scalar_rand(float min, float max)
999 | {
1000 | return (float)rand() / RAND_MAX * (max - min) + min;
1001 | }
1002 |
1003 | static PSdata _ps_rand(const std::vector& params, const PSnode& node, void* userData)
1004 | {
1005 | if(params.size() != 2)
1006 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1007 |
1008 | PSdata result;
1009 |
1010 | if(params[0].type == PSdata::VEC2 && params[1].type == PSdata::VEC2)
1011 | {
1012 | qm::vec2 min = params[0].vec2Val;
1013 | qm::vec2 max = params[1].vec2Val;
1014 |
1015 | result.type = PSdata::VEC2;
1016 | result.vec2Val.x = _ps_scalar_rand(min.x, max.x);
1017 | result.vec2Val.y = _ps_scalar_rand(min.y, max.y);
1018 | }
1019 | else if(params[0].type == PSdata::VEC3 && params[1].type == PSdata::VEC3)
1020 | {
1021 | qm::vec3 min = params[0].vec3Val;
1022 | qm::vec3 max = params[1].vec3Val;
1023 |
1024 | result.type = PSdata::VEC3;
1025 | result.vec3Val.x = _ps_scalar_rand(min.x, max.x);
1026 | result.vec3Val.y = _ps_scalar_rand(min.y, max.y);
1027 | result.vec3Val.z = _ps_scalar_rand(min.z, max.z);
1028 | }
1029 | else if(params[0].type == PSdata::VEC4 && params[1].type == PSdata::VEC4)
1030 | {
1031 | qm::vec4 min = params[0].vec4Val;
1032 | qm::vec4 max = params[1].vec4Val;
1033 |
1034 | result.type = PSdata::VEC4;
1035 | result.vec4Val.x = _ps_scalar_rand(min.x, max.x);
1036 | result.vec4Val.y = _ps_scalar_rand(min.y, max.y);
1037 | result.vec4Val.z = _ps_scalar_rand(min.z, max.z);
1038 | result.vec4Val.w = _ps_scalar_rand(min.w, max.w);
1039 | }
1040 | else if(params[0].type == PSdata::INT && params[1].type == PSdata::INT)
1041 | {
1042 | int min = params[0].intVal;
1043 | int max = params[1].intVal;
1044 |
1045 | result.type = PSdata::INT;
1046 | result.intVal = rand() % (max - min) + min;
1047 | }
1048 | else
1049 | {
1050 | float min = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1051 | float max = _ps_get_scalar(params[1], PSruntimeError::INVALID_PARAMS, node);
1052 |
1053 | result.type = PSdata::FLOAT;
1054 | result.floatVal = _ps_scalar_rand(min, max);
1055 | }
1056 |
1057 | return result;
1058 | }
1059 |
1060 | static PSdata _ps_int(const std::vector& params, const PSnode& node, void* userData)
1061 | {
1062 | if(params.size() != 1)
1063 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1064 |
1065 | PSdata result;
1066 | result.type = PSdata::INT;
1067 | result.intVal = (int)_ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1068 |
1069 | return result;
1070 | }
1071 |
1072 | static PSdata _ps_vec2(const std::vector& params, const PSnode& node, void* userData)
1073 | {
1074 | PSdata result;
1075 | result.type = PSdata::VEC2;
1076 |
1077 | switch(params.size())
1078 | {
1079 | case 0:
1080 | {
1081 | result.vec2Val = {0.0f, 0.0f};
1082 | break;
1083 | }
1084 | case 1:
1085 | {
1086 | float val = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1087 | result.vec2Val = {val, val};
1088 | break;
1089 | }
1090 | case 2:
1091 | {
1092 | float x = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1093 | float y = _ps_get_scalar(params[1], PSruntimeError::INVALID_PARAMS, node);
1094 | result.vec2Val = {x, y};
1095 | break;
1096 | }
1097 | default:
1098 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1099 | }
1100 |
1101 | return result;
1102 | }
1103 |
1104 | static PSdata _ps_vec3(const std::vector& params, const PSnode& node, void* userData)
1105 | {
1106 | PSdata result;
1107 | result.type = PSdata::VEC3;
1108 |
1109 | switch(params.size())
1110 | {
1111 | case 0:
1112 | {
1113 | result.vec3Val = {0.0f, 0.0f, 0.0f};
1114 | break;
1115 | }
1116 | case 1:
1117 | {
1118 | float val = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1119 | result.vec3Val = {val, val, val};
1120 | break;
1121 | }
1122 | case 2:
1123 | {
1124 | if(params[0].type != PSdata::VEC2)
1125 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1126 |
1127 | qm::vec2 vecVal = params[0].vec2Val;
1128 | float val = _ps_get_scalar(params[1], PSruntimeError::INVALID_PARAMS, node);
1129 | result.vec3Val = {vecVal.x, vecVal.y, val};
1130 | break;
1131 | }
1132 | case 3:
1133 | {
1134 | float x = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1135 | float y = _ps_get_scalar(params[1], PSruntimeError::INVALID_PARAMS, node);
1136 | float z = _ps_get_scalar(params[2], PSruntimeError::INVALID_PARAMS, node);
1137 | result.vec3Val = {x, y, z};
1138 | break;
1139 | }
1140 | default:
1141 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1142 | }
1143 |
1144 | return result;
1145 | }
1146 |
1147 | static PSdata _ps_vec4(const std::vector& params, const PSnode& node, void* userData)
1148 | {
1149 | PSdata result;
1150 | result.type = PSdata::VEC4;
1151 |
1152 | switch(params.size())
1153 | {
1154 | case 0:
1155 | {
1156 | result.vec4Val = {0.0f, 0.0f, 0.0f, 0.0f};
1157 | break;
1158 | }
1159 | case 1:
1160 | {
1161 | float val = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1162 | result.vec4Val = {val, val, val, val};
1163 | break;
1164 | }
1165 | case 2:
1166 | {
1167 | if(params[0].type != PSdata::VEC3)
1168 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1169 |
1170 | qm::vec3 vecVal = params[0].vec3Val;
1171 | float val = _ps_get_scalar(params[1], PSruntimeError::INVALID_PARAMS, node);
1172 | result.vec4Val = {vecVal.x, vecVal.y, vecVal.z, val};
1173 | break;
1174 | }
1175 | case 3:
1176 | {
1177 | float x = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1178 | float y = _ps_get_scalar(params[1], PSruntimeError::INVALID_PARAMS, node);
1179 | float z = _ps_get_scalar(params[2], PSruntimeError::INVALID_PARAMS, node);
1180 | float w = _ps_get_scalar(params[3], PSruntimeError::INVALID_PARAMS, node);
1181 | result.vec4Val = {x, y, z, w};
1182 | break;
1183 | }
1184 | default:
1185 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1186 | }
1187 |
1188 | return result;
1189 | }
1190 |
1191 | static PSdata _ps_quaternion(const std::vector& params, const PSnode& node, void* userData)
1192 | {
1193 | if(params.size() > 0 && params[0].type != PSdata::VEC3)
1194 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1195 |
1196 | PSdata result;
1197 | result.type = PSdata::QUATERNION;
1198 |
1199 | if(params.size() == 0)
1200 | {
1201 | result.quatVal = qm::quaternion_identity();
1202 | }
1203 | else if(params.size() == 1)
1204 | {
1205 | qm::vec3 angles = params[0].vec3Val;
1206 | angles.x = qm::rad_to_deg(angles.x);
1207 | angles.y = qm::rad_to_deg(angles.y);
1208 | angles.z = qm::rad_to_deg(angles.z);
1209 | result.quatVal = qm::quaternion_from_euler(angles);
1210 | }
1211 | else if(params.size() == 2)
1212 | {
1213 | qm::vec3 axis = params[0].vec3Val;
1214 | float angle = _ps_get_scalar(params[1], PSruntimeError::INVALID_PARAMS, node);
1215 | angle = qm::rad_to_deg(angle);
1216 | result.quatVal = qm::quaternion_from_axis_angle(axis, angle);
1217 | }
1218 | else
1219 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1220 |
1221 | return result;
1222 | }
1223 |
1224 | static PSdata _ps_sqrt(const std::vector& params, const PSnode& node, void* userData)
1225 | {
1226 | if(params.size() != 1)
1227 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1228 |
1229 | float input = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1230 |
1231 | PSdata result;
1232 | result.type = PSdata::FLOAT;
1233 | result.floatVal = sqrtf(input);
1234 |
1235 | return result;
1236 | }
1237 |
1238 | static PSdata _ps_pow(const std::vector& params, const PSnode& node, void* userData)
1239 | {
1240 | if(params.size() != 2)
1241 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1242 |
1243 | float base = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1244 | float exp = _ps_get_scalar(params[1], PSruntimeError::INVALID_PARAMS, node);
1245 |
1246 | PSdata result;
1247 | result.type = PSdata::FLOAT;
1248 | result.floatVal = powf(base, exp);
1249 |
1250 | return result;
1251 | }
1252 |
1253 | static PSdata _ps_abs(const std::vector& params, const PSnode& node, void* userData)
1254 | {
1255 | if(params.size() != 1)
1256 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1257 |
1258 | PSdata result;
1259 | result.type = params[0].type;
1260 |
1261 | if(params[0].type == PSdata::INT)
1262 | result.intVal = abs(params[0].intVal);
1263 | else if(params[0].type == PSdata::FLOAT)
1264 | result.floatVal = fabsf(params[0].floatVal);
1265 | else if(params[0].type == PSdata::VEC2)
1266 | {
1267 | result.vec2Val.x = fabsf(params[0].vec2Val.x);
1268 | result.vec2Val.y = fabsf(params[0].vec2Val.y);
1269 | }
1270 | else if(params[0].type == PSdata::VEC3)
1271 | {
1272 | result.vec3Val.x = fabsf(params[0].vec3Val.x);
1273 | result.vec3Val.y = fabsf(params[0].vec3Val.y);
1274 | result.vec3Val.z = fabsf(params[0].vec3Val.z);
1275 | }
1276 | else if(params[0].type == PSdata::VEC4)
1277 | {
1278 | result.vec4Val.x = fabsf(params[0].vec4Val.x);
1279 | result.vec4Val.y = fabsf(params[0].vec4Val.y);
1280 | result.vec4Val.z = fabsf(params[0].vec4Val.z);
1281 | result.vec4Val.w = fabsf(params[0].vec4Val.w);
1282 | }
1283 | else
1284 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1285 |
1286 | return result;
1287 | }
1288 |
1289 | static PSdata _ps_sin(const std::vector& params, const PSnode& node, void* userData)
1290 | {
1291 | if(params.size() != 1)
1292 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1293 |
1294 | float input = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1295 |
1296 | PSdata result;
1297 | result.type = PSdata::FLOAT;
1298 | result.floatVal = sinf(input);
1299 |
1300 | return result;
1301 | }
1302 |
1303 | static PSdata _ps_cos(const std::vector& params, const PSnode& node, void* userData)
1304 | {
1305 | if(params.size() != 1)
1306 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1307 |
1308 | float input = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1309 |
1310 | PSdata result;
1311 | result.type = PSdata::FLOAT;
1312 | result.floatVal = cosf(input);
1313 |
1314 | return result;
1315 | }
1316 |
1317 | static PSdata _ps_tan(const std::vector& params, const PSnode& node, void* userData)
1318 | {
1319 | if(params.size() != 1)
1320 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1321 |
1322 | float input = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1323 |
1324 | PSdata result;
1325 | result.type = PSdata::FLOAT;
1326 | result.floatVal = tanf(input);
1327 |
1328 | return result;
1329 | }
1330 |
1331 | static PSdata _ps_asin(const std::vector& params, const PSnode& node, void* userData)
1332 | {
1333 | if(params.size() != 1)
1334 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1335 |
1336 | float input = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1337 |
1338 | PSdata result;
1339 | result.type = PSdata::FLOAT;
1340 | result.floatVal = asinf(input);
1341 |
1342 | return result;
1343 | }
1344 |
1345 | static PSdata _ps_acos(const std::vector& params, const PSnode& node, void* userData)
1346 | {
1347 | if(params.size() != 1)
1348 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1349 |
1350 | float input = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1351 |
1352 | PSdata result;
1353 | result.type = PSdata::FLOAT;
1354 | result.floatVal = acosf(input);
1355 |
1356 | return result;
1357 | }
1358 |
1359 | static PSdata _ps_atan(const std::vector& params, const PSnode& node, void* userData)
1360 | {
1361 | if(params.size() != 1)
1362 | _ps_error(PSruntimeError::INVALID_PARAMS, node);
1363 |
1364 | float input = _ps_get_scalar(params[0], PSruntimeError::INVALID_PARAMS, node);
1365 |
1366 | PSdata result;
1367 | result.type = PSdata::FLOAT;
1368 | result.floatVal = atanf(input);
1369 |
1370 | return result;
1371 | }
--------------------------------------------------------------------------------
/src/lexer.cpp:
--------------------------------------------------------------------------------
1 | #include "propscript.hpp"
2 |
3 | #include
4 | #include
5 |
6 | //--------------------------------------------------------------------------------------------------------------------------------//
7 |
8 | //returns whether or not the given character exists in an operator at the given index
9 | bool is_op_char(char ch, size_t idx);
10 | //returns whether a given string is an operator
11 | bool is_op_str(std::string op);
12 | //adds an id token to the token list if idLen is not 0
13 | void try_add_id_token(std::string idString, size_t& idLen, uint32_t lineNum, std::vector& tokens);
14 |
15 | //--------------------------------------------------------------------------------------------------------------------------------//
16 |
17 | size_t g_maxOpLen;
18 |
19 | //--------------------------------------------------------------------------------------------------------------------------------//
20 |
21 | std::vector ps_lex_file(std::string path)
22 | {
23 | //GET MAX OPERATOR LEN:
24 | for(int i = 0; i < PS_LEXER_OPERATORS.size(); i++)
25 | if(PS_LEXER_OPERATORS[i].length() > g_maxOpLen)
26 | g_maxOpLen = PS_LEXER_OPERATORS[i].length();
27 |
28 | //LEX:
29 | std::vector tokens;
30 | std::ifstream file(path);
31 | if(!file.is_open())
32 | {
33 | std::cout << "PROPSCRIPT LEX ERROR: FAILED TO OPEN \"" << path << "\" FOR READING" << std::endl;
34 | return {};
35 | }
36 |
37 | bool inComment = false;
38 |
39 | uint32_t curLine = 1;
40 |
41 | std::string idString(64, ' ');
42 | size_t idLen = 0;
43 |
44 | while(!file.eof())
45 | {
46 | char curCh = file.get();
47 | if(file.eof())
48 | break;
49 |
50 | if(curCh == '\n')
51 | {
52 | try_add_id_token(idString, idLen, curLine, tokens);
53 |
54 | //remove duplicate newlines:
55 | if(tokens.size() > 1 && tokens[tokens.size() - 1].type != PStoken::NEWLINE)
56 | {
57 | PStoken newlineToken;
58 | newlineToken.type = PStoken::NEWLINE;
59 | newlineToken.lineNum = curLine;
60 | tokens.push_back(newlineToken);
61 | }
62 |
63 | inComment = false;
64 | curLine++;
65 | }
66 | else if(inComment)
67 | {
68 | continue;
69 | }
70 | else if(std::isspace(curCh))
71 | {
72 | try_add_id_token(idString, idLen, curLine, tokens);
73 | }
74 | else if(is_op_char(curCh, 0))
75 | {
76 | try_add_id_token(idString, idLen, curLine, tokens);
77 |
78 | std::string opString(g_maxOpLen, curCh);
79 | size_t opLen = 1;
80 |
81 | while(is_op_char(file.peek(), opLen))
82 | {
83 | curCh = file.get();
84 | opString[opLen++] = curCh;
85 | }
86 |
87 | while(!is_op_str(opString.substr(0, opLen)))
88 | {
89 | opLen--;
90 | file.unget();
91 | }
92 |
93 | opString = opString.substr(0, opLen);
94 |
95 | if(opString == PS_COMMENT)
96 | inComment = true;
97 | else if(opLen > 0)
98 | {
99 | PStoken opToken;
100 | opToken.type = PStoken::OP;
101 | opToken.str = opString;
102 | opToken.lineNum = curLine;
103 | tokens.push_back(opToken);
104 | }
105 | }
106 | else
107 | {
108 | idString[idLen++] = curCh;
109 | if(idLen >= idString.length())
110 | idString.resize(idString.length() * 2);
111 | }
112 | }
113 |
114 | try_add_id_token(idString, idLen, curLine, tokens);
115 |
116 | //make sure tokens end with newline:
117 | if(tokens[tokens.size() - 1].type != PStoken::NEWLINE)
118 | {
119 | PStoken newlineToken;
120 | newlineToken.type = PStoken::NEWLINE;
121 | newlineToken.lineNum = curLine;
122 | tokens.push_back(newlineToken);
123 | }
124 |
125 | file.close();
126 | return tokens;
127 | }
128 |
129 | //--------------------------------------------------------------------------------------------------------------------------------//
130 |
131 | bool is_op_char(char ch, size_t idx)
132 | {
133 | for(int i = 0; i < PS_LEXER_OPERATORS.size(); i++)
134 | if(PS_LEXER_OPERATORS[i][idx] == ch)
135 | return true;
136 |
137 | return false;
138 | }
139 |
140 | bool is_op_str(std::string str)
141 | {
142 | for(int i = 0; i < PS_LEXER_OPERATORS.size(); i++)
143 | if(PS_LEXER_OPERATORS[i] == str)
144 | return true;
145 |
146 | return false;
147 | }
148 |
149 | void try_add_id_token(std::string idString, size_t& idLen, uint32_t lineNum, std::vector& tokens)
150 | {
151 | if(idLen <= 0)
152 | return;
153 |
154 | PStoken idToken;
155 | idToken.type = PStoken::ID;
156 |
157 | idString = idString.substr(0, idLen);
158 | if(idString == PS_KEYWORD_AND || idString == PS_KEYWORD_OR || idString == PS_KEYWORD_IN)
159 | idToken.type = PStoken::OP;
160 |
161 | idToken.str = idString;
162 | idToken.lineNum = lineNum;
163 | tokens.push_back(idToken);
164 | idLen = 0;
165 | }
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include "propscript.hpp"
2 | #include
3 | #include
4 |
5 | int main()
6 | {
7 | std::vector tokens = ps_lex_file("examples/example.ps");
8 |
9 | PSast* ast = ps_parse_tokens(tokens);
10 | if(!ast)
11 | return -1;
12 |
13 | ps_execute(ast);
14 |
15 | ps_save_ast("examples/example.psobj", ast);
16 | ps_free_ast(ast);
17 |
18 | return 0;
19 | }
--------------------------------------------------------------------------------
/src/parser.cpp:
--------------------------------------------------------------------------------
1 | #include "propscript.hpp"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | //--------------------------------------------------------------------------------------------------------------------------------//
9 |
10 | enum class PSparseError
11 | {
12 | EXPECTED_CLOSING_PAREN,
13 | UNEXPECTED_OPERATOR,
14 | EXPECTED_OPERATOR,
15 | INVALID_TOKEN,
16 | EXPECTED_OPENING_CURLY
17 | };
18 |
19 | //parses a single statement
20 | static PSnodeHandle _ps_parse_statement(PSast* ast, const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens);
21 |
22 | //parses any token that is not an operator or control flow (statement in parens, variable, function, literal)
23 | static PSnodeHandle _ps_parse_non_op(PSast* ast, const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens);
24 | //parses a statement that is in parenthesis
25 | static PSnodeHandle _ps_parse_statement_in_parens(PSast* ast, const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens);
26 | //parses an identifier (variable, function, literal)
27 | static PSnodeHandle _ps_parse_id(PSast* ast, const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens);
28 | //returns the node for a given operator
29 | static PSnode _ps_get_op_node(PSast* ast, const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens);
30 |
31 | //adds a node to the ast
32 | static inline PSnodeHandle _ps_add_node(PSast* ast, PSnode node);
33 | //removes a newline from the token list if there are unclosed parenthesis
34 | static inline void _ps_continue_statement(const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens);
35 | //removes a newline from the token list
36 | static inline void _ps_remove_newline(const std::vector& tokens, uint32_t& curTokenIdx);
37 | //returns the operator precedence of a given operator
38 | static inline int _ps_precedence(PSnode::OP::Type op);
39 | //throws an exception if the given token is not an identifier
40 | static inline void _ps_force_id(PStoken token);
41 | //throws an exception and sets the global error variables
42 | static void _ps_error(PSparseError error, PStoken errorToken);
43 |
44 | //--------------------------------------------------------------------------------------------------------------------------------//
45 |
46 | static PSparseError g_psError;
47 | static PStoken g_psErrorToken;
48 |
49 | const std::unordered_map PS_STRING_TO_OP_TYPE = {
50 | {PS_KEYWORD_IN , PSnode::OP::Type::IN},
51 | {PS_OP_MULT , PSnode::OP::Type::MULT},
52 | {PS_OP_DIV , PSnode::OP::Type::DIV},
53 | {PS_OP_MOD , PSnode::OP::Type::MOD},
54 | {PS_OP_ADD , PSnode::OP::Type::ADD},
55 | {PS_OP_SUB , PSnode::OP::Type::SUB},
56 | {PS_OP_EQUAL , PSnode::OP::Type::EQUAL},
57 | {PS_OP_MULTEQUAL , PSnode::OP::Type::MULTEQUAL},
58 | {PS_OP_DIVEQUAL , PSnode::OP::Type::DIVEQUAL},
59 | {PS_OP_MODEQUAL , PSnode::OP::Type::MODEQUAL},
60 | {PS_OP_ADDEQUAL , PSnode::OP::Type::ADDEQUAL},
61 | {PS_OP_SUBEQUAL , PSnode::OP::Type::SUBEQUAL},
62 | {PS_OP_LESSTHAN , PSnode::OP::Type::LESSTHAN},
63 | {PS_OP_GREATERTHAN , PSnode::OP::Type::GREATERTHAN},
64 | {PS_OP_LESSTHANEQUAL , PSnode::OP::Type::LESSTHANEQUAL},
65 | {PS_OP_GREATERTHANEQUAL, PSnode::OP::Type::GREATERTHANEQUAL},
66 | {PS_OP_EQUALITY , PSnode::OP::Type::EQUALITY},
67 | {PS_OP_NONEQUALITY , PSnode::OP::Type::NONEQUALITY},
68 | };
69 |
70 | //--------------------------------------------------------------------------------------------------------------------------------//
71 |
72 | PSast* ps_parse_tokens(const std::vector& tokens)
73 | {
74 | PSast* result = new PSast;
75 |
76 | try
77 | {
78 | uint32_t curTokenIdx = 0;
79 | uint32_t numOpenParens = 0;
80 |
81 | while(curTokenIdx < tokens.size())
82 | {
83 | result->parentNodes.push_back(_ps_parse_statement(result, tokens, curTokenIdx, numOpenParens));
84 | _ps_remove_newline(tokens, curTokenIdx);
85 | }
86 | }
87 | catch(std::exception e)
88 | {
89 | std::string errMsg;
90 | switch(g_psError)
91 | {
92 | case PSparseError::EXPECTED_CLOSING_PAREN:
93 | errMsg = "EXPECTED CLOSING PARENTHESIS";
94 | break;
95 | case PSparseError::UNEXPECTED_OPERATOR:
96 | errMsg = "UNEXPECTED OPERATOR \"" + g_psErrorToken.str + "\"";
97 | break;
98 | case PSparseError::EXPECTED_OPERATOR:
99 | errMsg = "EXPECTED OPERATOR";
100 | break;
101 | case PSparseError::INVALID_TOKEN:
102 | errMsg = "INVALID TOKEN \"" + g_psErrorToken.str + "\"";
103 | break;
104 | case PSparseError::EXPECTED_OPENING_CURLY:
105 | errMsg = "EXPECTED OPENING CURLY BRACE";
106 | break;
107 | }
108 |
109 | std::cout << "PROPSCRIPT PARSE ERROR: " << errMsg << " ON LINE " << g_psErrorToken.lineNum << std::endl;
110 | delete result;
111 | return nullptr;
112 | }
113 |
114 | return result;
115 | }
116 |
117 | void ps_free_ast(PSast* ast)
118 | {
119 | delete ast;
120 | }
121 |
122 | void ps_save_ast(std::string path, PSast* ast)
123 | {
124 | std::ofstream file(path, std::ios_base::binary);
125 | if(!file.is_open())
126 | {
127 | std::cout << "ERROR OPENING FILE \"" << path << "\" FOR WRITING" << std::endl;
128 | return;
129 | }
130 |
131 | ps_save_ast(file, ast);
132 |
133 | file.close();
134 | }
135 |
136 | void ps_save_ast(std::ofstream& file, PSast* ast)
137 | {
138 | size_t parentNodeSize = ast->parentNodes.size();
139 | size_t nodePoolSize = ast->nodePool.size();
140 |
141 | file.write((const char*)&parentNodeSize, sizeof(size_t));
142 | file.write((const char*)ast->parentNodes.data(), sizeof(PSnodeHandle) * parentNodeSize);
143 |
144 | file.write((const char*)&nodePoolSize, sizeof(size_t));
145 | for(int i = 0; i < nodePoolSize; i++)
146 | {
147 | PSnode& node = ast->nodePool[i];
148 | file.write((const char*)&node.type, sizeof(PSnode::Type));
149 | file.write((const char*)&node.lineNum, sizeof(uint32_t));
150 |
151 | switch(node.type)
152 | {
153 | case PSnode::Type::OP:
154 | {
155 | file.write((const char*)&node.op, sizeof(PSnode::OP));
156 | file.write((const char*)&node.op.left, sizeof(PSnodeHandle));
157 | file.write((const char*)&node.op.right, sizeof(PSnodeHandle));
158 | break;
159 | }
160 | case PSnode::Type::KEYWORD:
161 | {
162 | file.write((const char*)&node.keyword.type, sizeof(PSnode::Keyword::Type));
163 |
164 | size_t codeSize = node.keyword.code.size();
165 | size_t elseCodeSize = node.keyword.elseCode.size();
166 | size_t paramNameSize = node.keyword.paramNames.size();
167 |
168 | file.write((const char*)&codeSize, sizeof(size_t));
169 | file.write((const char*)node.keyword.code.data(), sizeof(PSnodeHandle) * codeSize);
170 |
171 | file.write((const char*)&node.keyword.condition, sizeof(PSnodeHandle));
172 |
173 | file.write((const char*)&node.keyword.hasElse, sizeof(bool));
174 | file.write((const char*)&elseCodeSize, sizeof(size_t));
175 | file.write((const char*)node.keyword.elseCode.data(), sizeof(PSnodeHandle) * elseCodeSize);
176 |
177 | size_t nameLen = node.keyword.name.length();
178 | file.write((const char*)&nameLen, sizeof(size_t));
179 | file.write((const char*)node.keyword.name.data(), sizeof(char) * nameLen);
180 | file.write((const char*)¶mNameSize, sizeof(size_t));
181 | for(int j = 0; j < paramNameSize; j++)
182 | {
183 | size_t paramNameLen = node.keyword.paramNames[j].length();
184 | file.write((const char*)¶mNameLen, sizeof(size_t));
185 | file.write((const char*)node.keyword.paramNames[j].data(), sizeof(char) * paramNameLen);
186 | }
187 |
188 | file.write((const char*)&node.keyword.returnVal, sizeof(PSnodeHandle));
189 |
190 | break;
191 | }
192 | case PSnode::ID:
193 | {
194 | file.write((const char*)&node.id.type, sizeof(PSnode::ID::Type));
195 |
196 | size_t nameLen = node.id.name.length();
197 | file.write((const char*)&nameLen, sizeof(size_t));
198 | file.write((const char*)node.id.name.data(), sizeof(char) * nameLen);
199 |
200 | size_t paramSize = node.id.params.size();
201 | file.write((const char*)¶mSize, sizeof(size_t));
202 | file.write((const char*)node.id.params.data(), sizeof(PSnodeHandle) * paramSize);
203 |
204 | break;
205 | }
206 | case PSnode::NUMBER:
207 | {
208 | file.write((const char*)&node.literal.type, sizeof(PSnode::Literal::Type));
209 | file.write((const char*)&node.literal.intNum, sizeof(int32_t));
210 | file.write((const char*)&node.literal.floatNum, sizeof(float));
211 |
212 | break;
213 | }
214 | }
215 | }
216 | }
217 |
218 | PSast* ps_load_ast(std::string path)
219 | {
220 | std::ifstream file(path, std::ios_base::binary);
221 | if(!file.is_open())
222 | {
223 | std::cout << "ERROR OPENING FILE \"" << path << "\" FOR READING" << std::endl;
224 | return nullptr;
225 | }
226 |
227 | PSast* result = ps_load_ast(file);
228 |
229 | file.close();
230 | return result;
231 | }
232 |
233 | PSast* ps_load_ast(std::ifstream& file)
234 | {
235 | PSast* result = new PSast;
236 |
237 | size_t parentNodeSize;
238 | size_t nodePoolSize;
239 |
240 | file.read((char*)&parentNodeSize, sizeof(size_t));
241 | result->parentNodes.resize(parentNodeSize);
242 | file.read((char*)result->parentNodes.data(), sizeof(PSnodeHandle) * parentNodeSize);
243 |
244 | file.read((char*)&nodePoolSize, sizeof(size_t));
245 | for(int i = 0; i < nodePoolSize; i++)
246 | {
247 | PSnode node;
248 | file.read((char*)&node.type, sizeof(PSnode::Type));
249 | file.read((char*)&node.lineNum, sizeof(uint32_t));
250 |
251 | switch(node.type)
252 | {
253 | case PSnode::Type::OP:
254 | {
255 | file.read((char*)&node.op, sizeof(PSnode::OP));
256 | file.read((char*)&node.op.left, sizeof(PSnodeHandle));
257 | file.read((char*)&node.op.right, sizeof(PSnodeHandle));
258 | break;
259 | }
260 | case PSnode::Type::KEYWORD:
261 | {
262 | file.read((char*)&node.keyword.type, sizeof(PSnode::Keyword::Type));
263 |
264 | size_t codeSize;
265 | size_t elseCodeSize;
266 | size_t paramNameSize;
267 |
268 | file.read((char*)&codeSize, sizeof(size_t));
269 | node.keyword.code.resize(codeSize);
270 | file.read((char*)node.keyword.code.data(), sizeof(PSnodeHandle) * codeSize);
271 |
272 | file.read((char*)&node.keyword.condition, sizeof(PSnodeHandle));
273 |
274 | file.read((char*)&node.keyword.hasElse, sizeof(bool));
275 | file.read((char*)&elseCodeSize, sizeof(size_t));
276 | node.keyword.elseCode.resize(elseCodeSize);
277 | file.read((char*)node.keyword.elseCode.data(), sizeof(PSnodeHandle) * elseCodeSize);
278 |
279 | size_t nameLen;
280 | file.read((char*)&nameLen, sizeof(size_t));
281 | node.keyword.name.resize(nameLen);
282 | file.read(node.keyword.name.data(), sizeof(char) * nameLen);
283 | file.read((char*)¶mNameSize, sizeof(size_t));
284 | node.keyword.paramNames.resize(paramNameSize);
285 | for(int j = 0; j < paramNameSize; j++)
286 | {
287 | size_t paramNameLen;
288 | file.read((char*)¶mNameLen, sizeof(size_t));
289 | node.keyword.paramNames[j].resize(paramNameLen);
290 | file.read(node.keyword.paramNames[j].data(), sizeof(char) * paramNameLen);
291 | }
292 |
293 | file.read((char*)&node.keyword.returnVal, sizeof(PSnodeHandle));
294 |
295 | break;
296 | }
297 | case PSnode::ID:
298 | {
299 | file.read((char*)&node.id.type, sizeof(PSnode::ID::Type));
300 |
301 | size_t nameLen;
302 | file.read((char*)&nameLen, sizeof(size_t));
303 | node.id.name.resize(nameLen);
304 | file.read(node.id.name.data(), sizeof(char) * nameLen);
305 |
306 | size_t paramSize;
307 | file.read((char*)¶mSize, sizeof(size_t));
308 | node.id.params.resize(paramSize);
309 | file.read((char*)node.id.params.data(), sizeof(PSnodeHandle) * paramSize);
310 |
311 | break;
312 | }
313 | case PSnode::NUMBER:
314 | {
315 | file.read((char*)&node.literal.type, sizeof(PSnode::Literal::Type));
316 | file.read((char*)&node.literal.intNum, sizeof(int32_t));
317 | file.read((char*)&node.literal.floatNum, sizeof(float));
318 |
319 | break;
320 | }
321 | }
322 |
323 | result->nodePool.push_back(node);
324 | }
325 |
326 | return result;
327 | }
328 |
329 | //--------------------------------------------------------------------------------------------------------------------------------//
330 |
331 | static PSnodeHandle _ps_parse_statement(PSast* ast, const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens)
332 | {
333 | //CHECK IF CONTROL FLOW STATEMENT:
334 | if(tokens[curTokenIdx].str == PS_KEYWORD_IF || tokens[curTokenIdx].str == PS_KEYWORD_FOR)
335 | {
336 | bool isFor = tokens[curTokenIdx].str == PS_KEYWORD_FOR;
337 |
338 | if(numOpenParens > 0)
339 | _ps_error(PSparseError::INVALID_TOKEN, tokens[curTokenIdx]);
340 |
341 | PSnode controlNode;
342 | controlNode.type = PSnode::KEYWORD;
343 | controlNode.lineNum = tokens[curTokenIdx].lineNum;
344 | controlNode.keyword.type = isFor ? PSnode::Keyword::FOR : PSnode::Keyword::IF;
345 |
346 | //GET CONDITION:
347 | controlNode.keyword.condition = _ps_parse_statement(ast, tokens, ++curTokenIdx, numOpenParens);
348 | _ps_remove_newline(tokens, curTokenIdx);
349 |
350 | //GET CODE:
351 | if(tokens[curTokenIdx].str == PS_SEPERATOR_CURLY_OPEN) //multi-line
352 | {
353 | curTokenIdx++;
354 | _ps_remove_newline(tokens, curTokenIdx);
355 |
356 | while(tokens[curTokenIdx].str != PS_SEPERATOR_CURLY_CLOSE)
357 | {
358 | controlNode.keyword.code.push_back(_ps_parse_statement(ast, tokens, curTokenIdx, numOpenParens));
359 | _ps_remove_newline(tokens, curTokenIdx);
360 | }
361 |
362 | curTokenIdx++;
363 | }
364 | else //single-line
365 | controlNode.keyword.code.push_back(_ps_parse_statement(ast, tokens, curTokenIdx, numOpenParens));
366 |
367 | //IF FOR LOOP, NO ELSE STATEMENT POSSIBLE SO JUST RETURN
368 | if(isFor)
369 | return _ps_add_node(ast, controlNode);
370 |
371 | //CHECK FOR ELSE:
372 | _ps_remove_newline(tokens, curTokenIdx);
373 |
374 | if(tokens[curTokenIdx].str == PS_KEYWORD_ELSE)
375 | {
376 | controlNode.keyword.hasElse = true;
377 |
378 | curTokenIdx++;
379 | _ps_remove_newline(tokens, curTokenIdx);
380 | if(tokens[curTokenIdx].str == PS_SEPERATOR_CURLY_OPEN) //multi-line
381 | {
382 | curTokenIdx++;
383 | _ps_remove_newline(tokens, curTokenIdx);
384 |
385 | while(tokens[curTokenIdx].str != PS_SEPERATOR_CURLY_CLOSE)
386 | {
387 | controlNode.keyword.elseCode.push_back(_ps_parse_statement(ast, tokens, curTokenIdx, numOpenParens));
388 | _ps_remove_newline(tokens, curTokenIdx);
389 | }
390 |
391 | curTokenIdx++;
392 | }
393 | else //single-line / else-if
394 | controlNode.keyword.elseCode.push_back(_ps_parse_statement(ast, tokens, curTokenIdx, numOpenParens));
395 | }
396 | else
397 | controlNode.keyword.hasElse = false;
398 |
399 | return _ps_add_node(ast, controlNode);
400 | }
401 |
402 | //CHECK IF FUNCTION DEFINITION:
403 | if(tokens[curTokenIdx].str == PS_KEYWORD_FUNC)
404 | {
405 | PSnode funcNode;
406 | funcNode.type = PSnode::KEYWORD;
407 | funcNode.lineNum = tokens[curTokenIdx].lineNum;
408 | funcNode.keyword.type = PSnode::Keyword::FUNC;
409 |
410 | curTokenIdx++;
411 | _ps_remove_newline(tokens, curTokenIdx);
412 |
413 | _ps_force_id(tokens[curTokenIdx]);
414 |
415 | funcNode.keyword.name = tokens[curTokenIdx].str;
416 | curTokenIdx++;
417 | _ps_remove_newline(tokens, curTokenIdx);
418 |
419 | if(tokens[curTokenIdx].str == PS_SEPERATOR_PAREN_OPEN) //has parameters
420 | {
421 | curTokenIdx++;
422 | numOpenParens++;
423 | _ps_continue_statement(tokens, curTokenIdx, numOpenParens);
424 |
425 | //argument names:
426 | while(true)
427 | {
428 | _ps_force_id(tokens[curTokenIdx]);
429 | funcNode.keyword.paramNames.push_back(tokens[curTokenIdx].str);
430 | curTokenIdx++;
431 |
432 | if(tokens[curTokenIdx].str == PS_SEPERATOR_PAREN_CLOSE)
433 | break;
434 | else if(tokens[curTokenIdx].str != PS_SEPERATOR_COMMA)
435 | _ps_error(PSparseError::EXPECTED_OPERATOR, tokens[curTokenIdx]);
436 |
437 | curTokenIdx++;
438 | _ps_continue_statement(tokens, curTokenIdx, numOpenParens);
439 | }
440 |
441 | curTokenIdx++;
442 | numOpenParens--;
443 | }
444 |
445 | _ps_remove_newline(tokens, curTokenIdx);
446 |
447 | //GET CODE:
448 | if(tokens[curTokenIdx].str != PS_SEPERATOR_CURLY_OPEN) //ensure open curly brace found
449 | _ps_error(PSparseError::EXPECTED_OPENING_CURLY, tokens[curTokenIdx]);
450 |
451 | curTokenIdx++;
452 | _ps_remove_newline(tokens, curTokenIdx);
453 |
454 | while(tokens[curTokenIdx].str != PS_SEPERATOR_CURLY_CLOSE)
455 | {
456 | funcNode.keyword.code.push_back(_ps_parse_statement(ast, tokens, curTokenIdx, numOpenParens));
457 | _ps_remove_newline(tokens, curTokenIdx);
458 | }
459 |
460 | curTokenIdx++;
461 |
462 | return _ps_add_node(ast, funcNode);
463 | }
464 |
465 | //CHECK IF RETURN STATEMENT:
466 | if(tokens[curTokenIdx].str == PS_KEYWORD_RETURN)
467 | {
468 | PSnode returnNode;
469 | returnNode.type = PSnode::KEYWORD;
470 | returnNode.lineNum = tokens[curTokenIdx].lineNum;
471 | returnNode.keyword.type = PSnode::Keyword::RETURN;
472 |
473 | curTokenIdx++;
474 | if(tokens[curTokenIdx].type != PStoken::NEWLINE &&
475 | std::find(PS_CLOSED_SEPERATORS.begin(), PS_CLOSED_SEPERATORS.end(), tokens[curTokenIdx].str) == PS_CLOSED_SEPERATORS.end()) //get return value if not a void return
476 | returnNode.keyword.returnVal = _ps_parse_statement(ast, tokens, curTokenIdx, numOpenParens);
477 | else
478 | returnNode.keyword.returnVal = UINT32_MAX;
479 |
480 | return _ps_add_node(ast, returnNode);
481 | }
482 |
483 | //CHECK IF BREAK/CONTINUE STATEMENT:
484 | if(tokens[curTokenIdx].str == PS_KEYWORD_BREAK || tokens[curTokenIdx].str == PS_KEYWORD_CONTINUE)
485 | {
486 | PSnode breakNode;
487 | breakNode.type = PSnode::KEYWORD;
488 | breakNode.lineNum = tokens[curTokenIdx].lineNum;
489 | breakNode.keyword.type = tokens[curTokenIdx].str == PS_KEYWORD_BREAK ? PSnode::Keyword::BREAK : PSnode::Keyword::CONTINUE;
490 |
491 | curTokenIdx++;
492 | if(tokens[curTokenIdx].type != PStoken::NEWLINE &&
493 | std::find(PS_CLOSED_SEPERATORS.begin(), PS_CLOSED_SEPERATORS.end(), tokens[curTokenIdx].str) == PS_CLOSED_SEPERATORS.end()) //get return value if not a void return
494 | _ps_error(PSparseError::INVALID_TOKEN, tokens[curTokenIdx]);
495 |
496 | return _ps_add_node(ast, breakNode);
497 | }
498 |
499 | //MUST BE REGULAR OPERATION:
500 | PSnodeHandle left;
501 | PSnodeHandle right;
502 |
503 | //GET LEFT TOKEN:
504 | left = _ps_parse_non_op(ast, tokens, curTokenIdx, numOpenParens);
505 |
506 | //CHECK IF LINE ENDED:
507 | if(tokens[curTokenIdx].type == PStoken::NEWLINE || tokens[curTokenIdx].str == PS_SEPERATOR_CURLY_OPEN ||
508 | std::find(PS_CLOSED_SEPERATORS.begin(), PS_CLOSED_SEPERATORS.end(), tokens[curTokenIdx].str) != PS_CLOSED_SEPERATORS.end())
509 | return left;
510 |
511 | //GET OP TOKEN:
512 | PSnode opNode = _ps_get_op_node(ast, tokens, curTokenIdx, numOpenParens);
513 |
514 | //GET RIGHT TOKEN:
515 | right = _ps_parse_non_op(ast, tokens, curTokenIdx, numOpenParens);
516 |
517 | //CONSTRUCT OP NODE:
518 | opNode.op.inParens = false;
519 | opNode.op.left = left;
520 | opNode.op.right = right;
521 |
522 | //ITERATE TO GET REST OF NODES:
523 | while(curTokenIdx < tokens.size() && tokens[curTokenIdx].type != PStoken::NEWLINE && tokens[curTokenIdx].str != PS_SEPERATOR_CURLY_OPEN &&
524 | std::find(PS_CLOSED_SEPERATORS.begin(), PS_CLOSED_SEPERATORS.end(), tokens[curTokenIdx].str) == PS_CLOSED_SEPERATORS.end())
525 | {
526 | PSnode newOp = _ps_get_op_node(ast, tokens, curTokenIdx, numOpenParens);
527 |
528 | right = _ps_parse_non_op(ast, tokens, curTokenIdx, numOpenParens);
529 |
530 | //ADD TO EXISTING NODES WITH CORRECT ORDER OF OPERATIONS:
531 | if(_ps_precedence(newOp.op.type) >= _ps_precedence(opNode.op.type))
532 | {
533 | newOp.op.left = _ps_add_node(ast, opNode);
534 | newOp.op.right = right;
535 | opNode = newOp;
536 | }
537 | else
538 | {
539 | //find rightmost op with lesser order:
540 | PSnode* rightMost = &opNode;
541 | PSnode rightModeRight = ast->nodePool[rightMost->op.right];
542 | while(rightModeRight.type == PSnode::OP &&
543 | _ps_precedence(newOp.op.type) < _ps_precedence(rightModeRight.op.type) &&
544 | !rightModeRight.op.inParens)
545 | {
546 | rightMost = &ast->nodePool[rightMost->op.right];
547 | rightModeRight = ast->nodePool[rightMost->op.right];
548 | }
549 |
550 | newOp.op.left = rightMost->op.right;
551 | newOp.op.right = right;
552 | rightMost->op.right = _ps_add_node(ast, newOp);
553 | }
554 | }
555 |
556 | return _ps_add_node(ast, opNode);
557 | }
558 |
559 | //--------------------------------------------------------------------------------------------------------------------------------//
560 |
561 | static PSnodeHandle _ps_parse_non_op(PSast* ast, const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens)
562 | {
563 | PSnodeHandle node;
564 |
565 | if(tokens[curTokenIdx].str == PS_SEPERATOR_PAREN_OPEN)
566 | node = _ps_parse_statement_in_parens(ast, tokens, curTokenIdx, numOpenParens);
567 | else
568 | node = _ps_parse_id(ast, tokens, curTokenIdx, numOpenParens);
569 |
570 | _ps_continue_statement(tokens, curTokenIdx, numOpenParens);
571 | return node;
572 | }
573 |
574 | static PSnodeHandle _ps_parse_statement_in_parens(PSast* ast, const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens)
575 | {
576 | PSnodeHandle node = _ps_parse_statement(ast, tokens, ++curTokenIdx, ++numOpenParens);
577 | if(tokens[curTokenIdx].str != PS_SEPERATOR_PAREN_CLOSE)
578 | _ps_error(PSparseError::EXPECTED_CLOSING_PAREN, tokens[curTokenIdx]);
579 |
580 | ast->nodePool[node].op.inParens = true;
581 |
582 | numOpenParens--;
583 | curTokenIdx++;
584 | return node;
585 | }
586 |
587 | static PSnodeHandle _ps_parse_id(PSast* ast, const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens)
588 | {
589 | bool negative = false;
590 | if(tokens[curTokenIdx].str == PS_OP_SUB)
591 | {
592 | negative = true;
593 | curTokenIdx++;
594 | }
595 |
596 | _ps_force_id(tokens[curTokenIdx]);
597 |
598 | //FUNCTION:
599 | if(tokens[curTokenIdx + 1].str == PS_SEPERATOR_PAREN_OPEN)
600 | {
601 | PSnode funcNode;
602 | funcNode.type = PSnode::ID;
603 | funcNode.lineNum = tokens[curTokenIdx].lineNum;
604 | funcNode.id.type = PSnode::ID::FUNC;
605 | funcNode.id.name = tokens[curTokenIdx].str;
606 |
607 | curTokenIdx += 2;
608 | numOpenParens++;
609 | _ps_continue_statement(tokens, curTokenIdx, numOpenParens);
610 |
611 | //0 argument function:
612 | if(tokens[curTokenIdx].str == PS_SEPERATOR_PAREN_CLOSE)
613 | {
614 | curTokenIdx++;
615 | numOpenParens--;
616 | return _ps_add_node(ast, funcNode);
617 | }
618 |
619 | //arguments:
620 | while(true)
621 | {
622 | funcNode.id.params.push_back(_ps_parse_statement(ast, tokens, curTokenIdx, numOpenParens));
623 |
624 | if(tokens[curTokenIdx].str == PS_SEPERATOR_PAREN_CLOSE)
625 | break;
626 | else if(tokens[curTokenIdx].str != PS_SEPERATOR_COMMA)
627 | _ps_error(PSparseError::EXPECTED_OPERATOR, tokens[curTokenIdx]);
628 |
629 | curTokenIdx++;
630 | _ps_continue_statement(tokens, curTokenIdx, numOpenParens);
631 | }
632 |
633 | curTokenIdx++;
634 | numOpenParens--;
635 |
636 | if(negative)
637 | {
638 | PSnode minusNode;
639 | minusNode.type = PSnode::OP;
640 | minusNode.lineNum = tokens[curTokenIdx].lineNum;
641 | minusNode.op.type = PSnode::OP::SUB;
642 |
643 | PSnode negOne;
644 | negOne.type = PSnode::NUMBER;
645 | negOne.lineNum = tokens[curTokenIdx].lineNum;
646 | negOne.literal.type = PSnode::Literal::INT;
647 | negOne.literal.intNum = -1;
648 |
649 | minusNode.op.left = _ps_add_node(ast, negOne);
650 | minusNode.op.right = _ps_add_node(ast, funcNode);
651 | return _ps_add_node(ast, minusNode);
652 | }
653 | else
654 | return _ps_add_node(ast, funcNode);
655 | }
656 |
657 | PStoken token = tokens[curTokenIdx++];
658 |
659 | //NUMBER:
660 | if(std::isdigit(token.str[0]) || token.str[0] == '.')
661 | {
662 | bool isFloat = false;
663 | for(int i = 0; i < token.str.length(); i++)
664 | {
665 | if(token.str[i] == '.')
666 | {
667 | if(isFloat)
668 | _ps_error(PSparseError::INVALID_TOKEN, tokens[curTokenIdx]);
669 | else
670 | isFloat = true;
671 | }
672 | else if(!std::isdigit(token.str[i]))
673 | _ps_error(PSparseError::INVALID_TOKEN, tokens[curTokenIdx]);
674 | }
675 |
676 | PSnode numNode;
677 | numNode.type = PSnode::NUMBER;
678 | numNode.lineNum = tokens[curTokenIdx - 1].lineNum;
679 |
680 | if(isFloat)
681 | {
682 | numNode.literal.type = PSnode::Literal::FLOAT;
683 | numNode.literal.floatNum = std::stof(token.str);
684 | if(negative)
685 | numNode.literal.floatNum *= -1.0f;
686 | }
687 | else
688 | {
689 | numNode.literal.type = PSnode::Literal::INT;
690 | numNode.literal.intNum = std::stoi(token.str);
691 | if(negative)
692 | numNode.literal.intNum *= -1;
693 | }
694 |
695 | return _ps_add_node(ast, numNode);
696 | }
697 |
698 | //VARIABLE:
699 | PSnode varNode;
700 | varNode.type = PSnode::ID;
701 | varNode.lineNum = tokens[curTokenIdx].lineNum;
702 | varNode.id.type = PSnode::ID::VAR;
703 | varNode.id.name = token.str;
704 |
705 | //index into variable:
706 | if(tokens[curTokenIdx].str == PS_SEPERATOR_SQUARE_OPEN)
707 | {
708 | numOpenParens++;
709 | _ps_continue_statement(tokens, curTokenIdx, numOpenParens);
710 |
711 | varNode.id.params.push_back(_ps_parse_statement(ast, tokens, ++curTokenIdx, numOpenParens));
712 |
713 | _ps_continue_statement(tokens, curTokenIdx, numOpenParens);
714 | if(tokens[curTokenIdx].str != PS_SEPERATOR_SQUARE_CLOSE)
715 | _ps_error(PSparseError::EXPECTED_CLOSING_PAREN, tokens[curTokenIdx]);
716 |
717 | numOpenParens--;
718 | curTokenIdx++;
719 | }
720 |
721 | if(negative)
722 | {
723 | PSnode minusNode;
724 | minusNode.type = PSnode::OP;
725 | minusNode.lineNum = tokens[curTokenIdx].lineNum;
726 | minusNode.op.type = PSnode::OP::SUB;
727 |
728 | PSnode negOne;
729 | negOne.type = PSnode::NUMBER;
730 | negOne.lineNum = tokens[curTokenIdx].lineNum;
731 | negOne.literal.type = PSnode::Literal::INT;
732 | negOne.literal.intNum = -1;
733 |
734 | minusNode.op.left = _ps_add_node(ast, negOne);
735 | minusNode.op.right = _ps_add_node(ast, varNode);
736 | return _ps_add_node(ast, minusNode);
737 | }
738 | else
739 | return _ps_add_node(ast, varNode);
740 | }
741 |
742 | static PSnode _ps_get_op_node(PSast* ast, const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens)
743 | {
744 | //ENSURE ACTUALLY AN OP:
745 | if(tokens[curTokenIdx].type != PStoken::OP)
746 | _ps_error(PSparseError::EXPECTED_OPERATOR, tokens[curTokenIdx]);
747 |
748 | PSnode opNode;
749 | opNode.type = PSnode::OP;
750 | opNode.lineNum = tokens[curTokenIdx].lineNum;
751 | if(PS_STRING_TO_OP_TYPE.count(tokens[curTokenIdx].str) == 0)
752 | _ps_error(PSparseError::INVALID_TOKEN, tokens[curTokenIdx]);
753 | else
754 | opNode.op.type = PS_STRING_TO_OP_TYPE.at(tokens[curTokenIdx].str);
755 |
756 | curTokenIdx++;
757 | _ps_continue_statement(tokens, curTokenIdx, numOpenParens);
758 | return opNode;
759 | }
760 |
761 | //--------------------------------------------------------------------------------------------------------------------------------//
762 |
763 | static inline PSnodeHandle _ps_add_node(PSast* ast, PSnode node)
764 | {
765 | ast->nodePool.push_back(node);
766 | return ast->nodePool.size() - 1;
767 | }
768 |
769 | static inline void _ps_continue_statement(const std::vector& tokens, uint32_t& curTokenIdx, uint32_t& numOpenParens)
770 | {
771 | if(tokens[curTokenIdx].type == PStoken::NEWLINE && numOpenParens != 0)
772 | {
773 | curTokenIdx++;
774 | if(curTokenIdx >= tokens.size())
775 | _ps_error(PSparseError::EXPECTED_CLOSING_PAREN, tokens[curTokenIdx - 1]);
776 | }
777 | }
778 |
779 | static inline void _ps_remove_newline(const std::vector& tokens, uint32_t& curTokenIdx)
780 | {
781 | if(tokens[curTokenIdx].type == PStoken::NEWLINE)
782 | curTokenIdx++;
783 | }
784 |
785 | static inline int _ps_precedence(PSnode::OP::Type op)
786 | {
787 | return (int)op / 10;
788 | }
789 |
790 | static inline void _ps_force_id(PStoken token)
791 | {
792 | if(token.type != PStoken::ID)
793 | _ps_error(PSparseError::UNEXPECTED_OPERATOR, token);
794 |
795 | if(std::find(PS_KEYWORDS.begin(), PS_KEYWORDS.end(), token.str) != PS_KEYWORDS.end())
796 | _ps_error(PSparseError::INVALID_TOKEN, token);
797 | }
798 |
799 | static void _ps_error(PSparseError error, PStoken errorToken)
800 | {
801 | g_psError = error;
802 | g_psErrorToken = errorToken;
803 | throw std::exception();
804 | }
--------------------------------------------------------------------------------
/src/propscript.hpp:
--------------------------------------------------------------------------------
1 | #ifndef PROPSCRIPT_HPP
2 | #define PROPSCRIPT_HPP
3 |
4 | #include "definitions.hpp"
5 | #include "vector"
6 | #include "string"
7 |
8 | #include
9 | #include "quickmath.hpp"
10 |
11 | //--------------------------------------------------------------------------------------------------------------------------------//
12 | //LEXER AND PARSER STRUCTS:
13 |
14 | //a lexical token
15 | struct PStoken
16 | {
17 | enum Type
18 | {
19 | ID,
20 | OP,
21 | NEWLINE
22 | } type;
23 |
24 | std::string str;
25 | uint32_t lineNum;
26 | };
27 |
28 | //a handle to an abstract syntax tree node
29 | typedef uint32_t PSnodeHandle;
30 |
31 | //an abstract syntax tree node
32 | struct PSnode
33 | {
34 | enum Type : uint32_t
35 | {
36 | OP,
37 | KEYWORD,
38 | ID,
39 | NUMBER
40 | } type;
41 | uint32_t lineNum;
42 |
43 | //----------------------//
44 | //OPERATOR:
45 |
46 | struct OP
47 | {
48 | enum Type : uint32_t
49 | {
50 | IN = 0, //operator precedence
51 |
52 | MULT = 10,
53 | DIV,
54 | MOD,
55 |
56 | ADD = 20,
57 | SUB,
58 |
59 | EQUAL = 30,
60 | MULTEQUAL,
61 | DIVEQUAL,
62 | MODEQUAL,
63 | ADDEQUAL,
64 | SUBEQUAL,
65 |
66 | LESSTHAN = 40,
67 | GREATERTHAN,
68 | LESSTHANEQUAL,
69 | GREATERTHANEQUAL,
70 | EQUALITY,
71 | NONEQUALITY,
72 |
73 | AND = 50,
74 | OR
75 | } type;
76 |
77 | PSnodeHandle left; //the left side of the operator
78 | PSnodeHandle right; //the right side of the operator
79 | bool inParens; //whether the operator is in parenthesis
80 | } op;
81 |
82 | //----------------------//
83 | //KEYWORD:
84 |
85 | struct Keyword
86 | {
87 | enum Type
88 | {
89 | IF,
90 | FOR,
91 | FUNC,
92 | RETURN,
93 | BREAK,
94 | CONTINUE
95 | } type;
96 |
97 | //code for body of control flow statement:
98 | std::vector code;
99 |
100 | //if statement condition / for stataement iteration rule:
101 | PSnodeHandle condition;
102 |
103 | //else statement stuff:
104 | bool hasElse;
105 | std::vector elseCode;
106 |
107 | //function names:
108 | std::string name;
109 | std::vector paramNames;
110 |
111 | //return value:
112 | PSnodeHandle returnVal;
113 | } keyword;
114 |
115 | //----------------------//
116 | //IDENTIFIER:
117 |
118 | struct ID
119 | {
120 | enum Type : uint32_t
121 | {
122 | FUNC,
123 | VAR
124 | } type;
125 |
126 | std::string name;
127 | std::vector params; //if type is a variable, also represents the index into that variable
128 | } id;
129 |
130 | //----------------------//
131 | //LITERAL:
132 |
133 | struct Literal
134 | {
135 | enum Type : uint32_t
136 | {
137 | INT,
138 | FLOAT
139 | } type;
140 |
141 | int32_t intNum;
142 | float floatNum;
143 | } literal;
144 |
145 | PSnode () {};
146 | ~PSnode() {};
147 | };
148 |
149 | //an abstract syntax tree
150 | struct PSast
151 | {
152 | std::vector parentNodes;
153 | std::vector nodePool;
154 | };
155 |
156 | //--------------------------------------------------------------------------------------------------------------------------------//
157 | //INTERPRETER STRUCTS:
158 |
159 | //a generic struct representing any possible data type
160 | struct PSdata
161 | {
162 | enum Type
163 | {
164 | VOID,
165 |
166 | INT,
167 | FLOAT,
168 | VEC2,
169 | VEC3,
170 | VEC4,
171 | QUATERNION
172 | } type;
173 |
174 | union
175 | {
176 | int32_t intVal;
177 | float floatVal;
178 | qm::vec2 vec2Val;
179 | qm::vec3 vec3Val;
180 | qm::vec4 vec4Val;
181 | qm::quaternion quatVal;
182 | };
183 |
184 | PSdata() {};
185 | PSdata(Type t, int32_t val) { type = t, intVal = val; };
186 | PSdata(Type t, float val) { type = t, floatVal = val; };
187 | PSdata(Type t, qm::vec2 val) { type = t, vec2Val = val; };
188 | PSdata(Type t, qm::vec3 val) { type = t, vec3Val = val; };
189 | PSdata(Type t, qm::vec4 val) { type = t, vec4Val = val; };
190 | PSdata(Type t, qm::quaternion val) { type = t, quatVal = val; };
191 | };
192 |
193 | //a function signature
194 | struct PSfunctionSignature
195 | {
196 | std::string name;
197 | PSdata (*func)(const std::vector& params, const PSnode& node, void* userData);
198 | };
199 |
200 | //a constant value
201 | struct PSconstant
202 | {
203 | std::string name;
204 | PSdata val;
205 | };
206 |
207 | //--------------------------------------------------------------------------------------------------------------------------------//
208 |
209 | /* Lexes and tokenizes a source file
210 | * @param path the path to the source file
211 | * @returns the vector of tokens extracted from the spurce file
212 | */
213 | std::vector ps_lex_file(std::string path);
214 |
215 | /* Parses a list of tokens into an abstract syntax tree
216 | * @param tokens the list of tokens
217 | * @returns the generated abstract syntax tree
218 | */
219 | PSast* ps_parse_tokens(const std::vector& tokens);
220 | /* Frees an abstract syntax tree
221 | * @param ast the abstract syntax tree to free
222 | */
223 | void ps_free_ast(PSast* ast);
224 | /* Serializes an abstract syntax tree to disk
225 | * @param path the path to the file to save to
226 | * @param ast the abstact syntax tree to save
227 | */
228 | void ps_save_ast(std::string path, PSast* ast);
229 | /* Serializes an abstract syntax tree to disk
230 | * @param file the file pointer to save to
231 | * @param ast the abstact syntax tree to save
232 | */
233 | void ps_save_ast(std::ofstream& file, PSast* ast);
234 | /* Loads an abstract syntax tree from disk
235 | * @param path the path to the file to load from
236 | * @returns the loaded abstract syntax tree
237 | */
238 | PSast* ps_load_ast(std::string path);
239 | /* Loads an abstract syntax tree from disk
240 | * @param file the file pointer to load from
241 | * @returns the loaded abstract syntax tree
242 | */
243 | PSast* ps_load_ast(std::ifstream& file);
244 |
245 | /* Sets a list of user defined functions to include in execution
246 | * @param functions the list of user defined functions to include
247 | */
248 | void ps_set_functions(const std::vector& functions);
249 | /* Sets a list of user defined constants to include in execution
250 | * @param constnats the list of user defined constants to include
251 | */
252 | void ps_set_constants(const std::vector& constants);
253 | /* Sets the pointer to the user data that gets passed to each function call
254 | * @param userData the pointer to be passed to each function call
255 | */
256 | void ps_set_function_user_data(void* userData);
257 | /* Throws an invalid parameter error, call inside a user-defined function if the parameter list is invalid
258 | * @param node the node passed to the function
259 | */
260 | void ps_throw_invalid_param_error(PSnode node);
261 | /* Executes the code in an abstract syntax tree
262 | * @param ast the abstract syntax tree to execute
263 | */
264 | void ps_execute(PSast* ast);
265 |
266 | #endif
--------------------------------------------------------------------------------
/src/quickmath.hpp:
--------------------------------------------------------------------------------
1 | /* ------------------------------------------------------------------------
2 | *
3 | * quickmath.hpp
4 | * author: Daniel Elwell (2022)
5 | * license: MIT
6 | * description: a single-header library for common vector, matrix, and quaternion math
7 | * functions designed for games and graphics programming.
8 | *
9 | * ------------------------------------------------------------------------
10 | *
11 | * all the types and functions provided by this library are under the the "qm" namespace
12 | *
13 | * if you wish not to use SSE3 intrinsics (if they are not supported for example),
14 | * change the macro on line 101 to "#define QM_USE_SSE 0"
15 | *
16 | * if you wish not to include in your project, simply change the macro on line 109
17 | * to "#define QM_INCLUDE_IOSTREAM 0" and any iostream related functions will not be compiled
18 | *
19 | * to disable the need to link with the C runtime library, change the macros beginning
20 | * on line 116 and the #include on line 114 to the appropirate functions/files
21 | *
22 | * ------------------------------------------------------------------------
23 | *
24 | * the following functions are defined:
25 | * (vecn means a vector of dimension, 2, 3, or 4, named vec2, vec3, and vec4)
26 | * (matn means a matrix of dimensions 3x3 or 4x4, named mat3 and mat4)
27 | *
28 | * vecn dot (vecn v1, vecn v2);
29 | * vec3 cross (vec3 v1, vec3 v2);
30 | * float length (vecn v);
31 | * vecn normalize (vecn v);
32 | * float distance (vecn v1, vecn v2);
33 | * vecn min (vecn v1, vecn v2);
34 | * vecn max (vecn v1, vecn v2);
35 | *
36 | * matn matn_identity ();
37 | * matn transpose (matn m);
38 | * matn inverse (matn m);
39 | *
40 | * mat3 translate (vec2 t);
41 | * mat4 translate (vec3 t);
42 | * mat3 scale (vec2 s);
43 | * mat4 scale (vec3 s);
44 | * mat3 rotate (float angle);
45 | * mat4 rotate (vec3 axis, float angle);
46 | * mat4 rotate (vec3 euler);
47 | * mat3 top_left (mat4 m);
48 | *
49 | * mat4 perspective (float fov, float aspect, float near, float far);
50 | * mat4 orthographic (float left, float right, float bot, float top, float near, float far);
51 | * mat4 look (vec3 pos, vec3 dir , vec3 up);
52 | * mat4 lookat (vec3 pos, vec3 target, vec3 up);
53 | *
54 | * quaternion quaternion_identity ();
55 | * quaternion dot (quaternion q1, quaternion q2);
56 | * float length (quaternion q);
57 | * quaternion normalize (quaternion q);
58 | * quaternion conjugate (quaternion q);
59 | * quaternion inverse (quaternion q);
60 | * quaternion slerp (quaternion q1, quaternion q2, float a);
61 | * quaternion quaternion_from_axis_angle (vec3 axis, float angle);
62 | * quaternion quaternion_from_euler (vec3 angles);
63 | * mat4 quaternion_to_mat4 (quaternion q);
64 | *
65 | * the following operators are defined:
66 | * (vecn means a vector of dimension, 2, 3, or 4, named vec2, vec3, and vec4)
67 | * (matn means a matrix of dimensions 3x3 or 4x4, named mat3 and mat4)
68 | *
69 | * vecn + vecn -> vecn
70 | * vecn - vecn -> vecn
71 | * vecn * vecn -> vecn
72 | * vecn / vecn -> vecn
73 | * vecn * float -> vecn
74 | * float * vecn -> vecn
75 | * vecn / float -> vecn
76 | * float / vecn -> vecn
77 | * vecn == vecn -> bool
78 | * vecn != vecn -> bool
79 | *
80 | * matn + matn -> matn
81 | * matn - matn -> matn
82 | * matn * matn -> matn
83 | * matn * vecn -> vecn
84 | *
85 | * quaternion + quaternion -> quaternion
86 | * quaternion - quaternion -> quaternion
87 | * quaternion * quaternion -> quaternion
88 | * quaternion * float -> quaternion
89 | * float * quaternion -> quaternion
90 | * quaternion / float -> quaternion
91 | * float / quaternion -> quaternion
92 | * quaternion == quaternion -> bool
93 | * quaternion != quaternion -> bool
94 | */
95 |
96 | #ifndef QM_MATH_H
97 | #define QM_MATH_H
98 |
99 | //if you wish NOT to use SSE3 SIMD intrinsics, simply change the
100 | //#define to 0
101 | #define QM_USE_SSE 1
102 | #if QM_USE_SSE
103 | #include
104 | #include
105 | #endif
106 |
107 | //if you wish NOT to include iostream, simply change the
108 | //#define to 0
109 | #define QM_INCLUDE_IOSTREAM 1
110 | #if QM_INCLUDE_IOSTREAM
111 | #include
112 | #endif
113 |
114 | //if you wish to not use any of the CRT functions, you must #define your
115 | //own versions of the below functions and #include the appropriate header
116 | #include
117 |
118 | #define QM_SQRTF sqrtf
119 | #define QM_SINF sinf
120 | #define QM_COSF cosf
121 | #define QM_TANF tanf
122 | #define QM_ACOSF acosf
123 |
124 | namespace qm
125 | {
126 |
127 | //----------------------------------------------------------------------//
128 | //STRUCT DEFINITIONS:
129 |
130 | //a 2-dimensional vector of floats
131 | union vec2
132 | {
133 | float v[2] = {};
134 | struct{ float x, y; };
135 | struct{ float w, h; };
136 |
137 | vec2() {};
138 | vec2(float _x, float _y) { x = _x, y = _y; };
139 |
140 | inline float& operator[](size_t i) { return v[i]; };
141 | };
142 |
143 | //a 3-dimensional vector of floats
144 | union vec3
145 | {
146 | float v[3] = {};
147 | struct{ float x, y, z; };
148 | struct{ float w, h, d; };
149 | struct{ float r, g, b; };
150 |
151 | vec3() {};
152 | vec3(float _x, float _y, float _z) { x = _x, y = _y, z = _z; };
153 | vec3(vec2 _xy, float _z) { x = _xy.x, y = _xy.y, z = _z; };
154 | vec3(float _x, vec3 _yz) { x = _x, y = _yz.x, z = _yz.y; };
155 |
156 | inline float& operator[](size_t i) { return v[i]; };
157 | };
158 |
159 | //a 4-dimensional vector of floats
160 | union vec4
161 | {
162 | float v[4] = {};
163 | struct{ float x, y, z, w; };
164 | struct{ float r, g, b, a; };
165 |
166 | #if QM_USE_SSE
167 |
168 | __m128 packed;
169 |
170 | #endif
171 |
172 | vec4() {};
173 | vec4(float _x, float _y, float _z, float _w) { x = _x, y = _y, z = _z, w = _w; };
174 | vec4(vec3 _xyz, float _w) { x = _xyz.x, y = _xyz.y, z = _xyz.z, w = _w; };
175 | vec4(float _x, vec3 _yzw) { x = _x, y = _yzw.x, z = _yzw.y, w = _yzw.z; };
176 | vec4(vec2 _xy, vec2 _zw) { x = _xy.x, y = _xy.y, z = _zw.x, w = _zw.y; };
177 |
178 | inline float& operator[](size_t i) { return v[i]; };
179 | };
180 |
181 | //-----------------------------//
182 | //matrices are column-major
183 |
184 | union mat3
185 | {
186 | float m[3][3] = {};
187 | vec3 v[3];
188 |
189 | mat3() {};
190 |
191 | inline vec3& operator[](size_t i) { return v[i]; };
192 | };
193 |
194 | union mat4
195 | {
196 | float m[4][4] = {};
197 | vec4 v[4];
198 |
199 | #if QM_USE_SSE
200 |
201 | __m128 packed[4]; //array of columns
202 |
203 | #endif
204 |
205 | mat4() {};
206 |
207 | inline vec4& operator[](size_t i) { return v[i]; };
208 | };
209 |
210 | //-----------------------------//
211 |
212 | union quaternion
213 | {
214 | float q[4] = {};
215 | struct{ float x, y, z, w; };
216 |
217 | #if QM_USE_SSE
218 |
219 | __m128 packed;
220 |
221 | #endif
222 |
223 | quaternion() {};
224 | quaternion(float _x, float _y, float _z, float _w) { x = _x, y = _y, z = _z, w = _w; };
225 | quaternion(vec3 _xyz, float _w) { x = _xyz.x, y = _xyz.y, z = _xyz.z, w = _w; };
226 | quaternion(float _x, vec3 _yzw) { x = _x, y = _yzw.x, z = _yzw.y, w = _yzw.z; };
227 | quaternion(vec2 _xy, vec2 _zw) { x = _xy.x, y = _xy.y, z = _zw.x, w = _zw.y; };
228 |
229 | inline float operator[](size_t i) { return q[i]; };
230 | };
231 |
232 | //----------------------------------------------------------------------//
233 | //HELPER FUNCS:
234 |
235 | #define QM_MIN(x, y) ((x) < (y) ? (x) : (y))
236 | #define QM_MAX(x, y) ((x) > (y) ? (x) : (y))
237 | #define QM_ABS(x) ((x) > 0 ? (x) : -(x))
238 |
239 | inline float rad_to_deg(float rad)
240 | {
241 | return rad * 57.2957795131f;
242 | }
243 |
244 | inline float deg_to_rad(float deg)
245 | {
246 | return deg * 0.01745329251f;
247 | }
248 |
249 | #if QM_USE_SSE
250 |
251 | inline __m128 mat4_mult_column_sse(__m128 c1, mat4 m2)
252 | {
253 | __m128 result;
254 |
255 | result = _mm_mul_ps(_mm_shuffle_ps(c1, c1, _MM_SHUFFLE(0, 0, 0, 0)), m2.packed[0]);
256 | result = _mm_add_ps(result, _mm_mul_ps(_mm_shuffle_ps(c1, c1, _MM_SHUFFLE(1, 1, 1, 1)), m2.packed[1]));
257 | result = _mm_add_ps(result, _mm_mul_ps(_mm_shuffle_ps(c1, c1, _MM_SHUFFLE(2, 2, 2, 2)), m2.packed[2]));
258 | result = _mm_add_ps(result, _mm_mul_ps(_mm_shuffle_ps(c1, c1, _MM_SHUFFLE(3, 3, 3, 3)), m2.packed[3]));
259 |
260 | return result;
261 | }
262 |
263 | #endif
264 |
265 | //----------------------------------------------------------------------//
266 | //VECTOR FUNCTIONS:
267 |
268 | #if QM_INCLUDE_IOSTREAM
269 |
270 | //output:
271 |
272 | inline std::ostream& operator<<(std::ostream& os, const vec2& v)
273 | {
274 | os << v.x << ", " << v.y;
275 | return os;
276 | }
277 |
278 | inline std::ostream& operator<<(std::ostream& os, const vec3& v)
279 | {
280 | os << v.x << ", " << v.y << ", " << v.z;
281 | return os;
282 | }
283 |
284 | inline std::ostream& operator<<(std::ostream& os, const vec4& v)
285 | {
286 | os << v.x << ", " << v.y << ", " << v.z << ", " << v.w;
287 | return os;
288 | }
289 |
290 | //input:
291 |
292 | inline std::istream& operator>>(std::istream& is, vec2& v)
293 | {
294 | is >> v.x >> v.y;
295 | return is;
296 | }
297 |
298 | inline std::istream& operator>>(std::istream& is, vec3& v)
299 | {
300 | is >> v.x >> v.y >> v.z;
301 | return is;
302 | }
303 |
304 | inline std::istream& operator>>(std::istream& is, vec4& v)
305 | {
306 | is >> v.x >> v.y >> v.z >> v.w;
307 | return is;
308 | }
309 |
310 | #endif
311 |
312 | //addition:
313 |
314 | inline vec2 operator+(const vec2& v1, const vec2& v2)
315 | {
316 | vec2 result;
317 |
318 | result.x = v1.x + v2.x;
319 | result.y = v1.y + v2.y;
320 |
321 | return result;
322 | }
323 |
324 | inline vec3 operator+(const vec3& v1, const vec3& v2)
325 | {
326 | vec3 result;
327 |
328 | result.x = v1.x + v2.x;
329 | result.y = v1.y + v2.y;
330 | result.z = v1.z + v2.z;
331 |
332 | return result;
333 | }
334 |
335 | inline vec4 operator+(const vec4& v1, const vec4& v2)
336 | {
337 | vec4 result;
338 |
339 | #if QM_USE_SSE
340 |
341 | result.packed = _mm_add_ps(v1.packed, v2.packed);
342 |
343 | #else
344 |
345 | result.x = v1.x + v2.x;
346 | result.y = v1.y + v2.y;
347 | result.z = v1.z + v2.z;
348 | result.w = v1.w + v2.w;
349 |
350 | #endif
351 |
352 | return result;
353 | }
354 |
355 | //subtraction:
356 |
357 | inline vec2 operator-(const vec2& v1, const vec2& v2)
358 | {
359 | vec2 result;
360 |
361 | result.x = v1.x - v2.x;
362 | result.y = v1.y - v2.y;
363 |
364 | return result;
365 | }
366 |
367 | inline vec3 operator-(const vec3& v1, const vec3& v2)
368 | {
369 | vec3 result;
370 |
371 | result.x = v1.x - v2.x;
372 | result.y = v1.y - v2.y;
373 | result.z = v1.z - v2.z;
374 |
375 | return result;
376 | }
377 |
378 | inline vec4 operator-(const vec4& v1, const vec4& v2)
379 | {
380 | vec4 result;
381 |
382 | #if QM_USE_SSE
383 |
384 | result.packed = _mm_sub_ps(v1.packed, v2.packed);
385 |
386 | #else
387 |
388 | result.x = v1.x - v2.x;
389 | result.y = v1.y - v2.y;
390 | result.z = v1.z - v2.z;
391 | result.w = v1.w - v2.w;
392 |
393 | #endif
394 |
395 | return result;
396 | }
397 |
398 | //multiplication:
399 |
400 | inline vec2 operator*(const vec2& v1, const vec2& v2)
401 | {
402 | vec2 result;
403 |
404 | result.x = v1.x * v2.x;
405 | result.y = v1.y * v2.y;
406 |
407 | return result;
408 | }
409 |
410 | inline vec3 operator*(const vec3& v1, const vec3& v2)
411 | {
412 | vec3 result;
413 |
414 | result.x = v1.x * v2.x;
415 | result.y = v1.y * v2.y;
416 | result.z = v1.z * v2.z;
417 |
418 | return result;
419 | }
420 |
421 | inline vec4 operator*(const vec4& v1, const vec4& v2)
422 | {
423 | vec4 result;
424 |
425 | #if QM_USE_SSE
426 |
427 | result.packed = _mm_mul_ps(v1.packed, v2.packed);
428 |
429 | #else
430 |
431 | result.x = v1.x * v2.x;
432 | result.y = v1.y * v2.y;
433 | result.z = v1.z * v2.z;
434 | result.w = v1.w * v2.w;
435 |
436 | #endif
437 |
438 | return result;
439 | }
440 |
441 | //division:
442 |
443 | inline vec2 operator/(const vec2& v1, const vec2& v2)
444 | {
445 | vec2 result;
446 |
447 | result.x = v1.x / v2.x;
448 | result.y = v1.y / v2.y;
449 |
450 | return result;
451 | }
452 |
453 | inline vec3 operator/(const vec3& v1, const vec3& v2)
454 | {
455 | vec3 result;
456 |
457 | result.x = v1.x / v2.x;
458 | result.y = v1.y / v2.y;
459 | result.z = v1.z / v2.z;
460 |
461 | return result;
462 | }
463 |
464 | inline vec4 operator/(const vec4& v1, const vec4& v2)
465 | {
466 | vec4 result;
467 |
468 | #if QM_USE_SSE
469 |
470 | result.packed = _mm_div_ps(v1.packed, v2.packed);
471 |
472 | #else
473 |
474 | result.x = v1.x / v2.x;
475 | result.y = v1.y / v2.y;
476 | result.z = v1.z / v2.z;
477 | result.w = v1.w / v2.w;
478 |
479 | #endif
480 |
481 | return result;
482 | }
483 |
484 | //scalar multiplication:
485 |
486 | inline vec2 operator*(const vec2& v, float s)
487 | {
488 | vec2 result;
489 |
490 | result.x = v.x * s;
491 | result.y = v.y * s;
492 |
493 | return result;
494 | }
495 |
496 | inline vec3 operator*(const vec3& v, float s)
497 | {
498 | vec3 result;
499 |
500 | result.x = v.x * s;
501 | result.y = v.y * s;
502 | result.z = v.z * s;
503 |
504 | return result;
505 | }
506 |
507 | inline vec4 operator*(const vec4& v, float s)
508 | {
509 | vec4 result;
510 |
511 | #if QM_USE_SSE
512 |
513 | __m128 scale = _mm_set1_ps(s);
514 | result.packed = _mm_mul_ps(v.packed, scale);
515 |
516 | #else
517 |
518 | result.x = v.x * s;
519 | result.y = v.y * s;
520 | result.z = v.z * s;
521 | result.w = v.w * s;
522 |
523 | #endif
524 |
525 | return result;
526 | }
527 |
528 | inline vec2 operator*(float s, const vec2& v)
529 | {
530 | return v * s;
531 | }
532 |
533 | inline vec3 operator*(float s, const vec3& v)
534 | {
535 | return v * s;
536 | }
537 |
538 | inline vec4 operator*(float s, const vec4& v)
539 | {
540 | return v * s;
541 | }
542 |
543 | //scalar division:
544 |
545 | inline vec2 operator/(const vec2& v, float s)
546 | {
547 | vec2 result;
548 |
549 | result.x = v.x / s;
550 | result.y = v.y / s;
551 |
552 | return result;
553 | }
554 |
555 | inline vec3 operator/(const vec3& v, float s)
556 | {
557 | vec3 result;
558 |
559 | result.x = v.x / s;
560 | result.y = v.y / s;
561 | result.z = v.z / s;
562 |
563 | return result;
564 | }
565 |
566 | inline vec4 operator/(const vec4& v, float s)
567 | {
568 | vec4 result;
569 |
570 | #if QM_USE_SSE
571 |
572 | __m128 scale = _mm_set1_ps(s);
573 | result.packed = _mm_div_ps(v.packed, scale);
574 |
575 | #else
576 |
577 | result.x = v.x / s;
578 | result.y = v.y / s;
579 | result.z = v.z / s;
580 | result.w = v.w / s;
581 |
582 | #endif
583 |
584 | return result;
585 | }
586 |
587 | inline vec2 operator/(float s, const vec2& v)
588 | {
589 | vec2 result;
590 |
591 | result.x = s / v.x;
592 | result.y = s / v.y;
593 |
594 | return result;
595 | }
596 |
597 | inline vec3 operator/(float s, const vec3& v)
598 | {
599 | vec3 result;
600 |
601 | result.x = s / v.x;
602 | result.y = s / v.y;
603 | result.z = s / v.z;
604 |
605 | return result;
606 | }
607 |
608 | inline vec4 operator/(float s, const vec4& v)
609 | {
610 | vec4 result;
611 |
612 | #if QM_USE_SSE
613 |
614 | __m128 scale = _mm_set1_ps(s);
615 | result.packed = _mm_div_ps(scale, v.packed);
616 |
617 | #else
618 |
619 | result.x = s / v.x;
620 | result.y = s / v.y;
621 | result.z = s / v.z;
622 | result.w = s / v.w;
623 |
624 | #endif
625 |
626 | return result;
627 | }
628 |
629 | //dot product:
630 |
631 | inline float dot(const vec2& v1, const vec2& v2)
632 | {
633 | float result;
634 |
635 | result = v1.x * v2.x + v1.y * v2.y;
636 |
637 | return result;
638 | }
639 |
640 | inline float dot(const vec3& v1, const vec3& v2)
641 | {
642 | float result;
643 |
644 | result = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
645 |
646 | return result;
647 | }
648 |
649 | inline float dot(const vec4& v1, const vec4& v2)
650 | {
651 | float result;
652 |
653 | #if QM_USE_SSE
654 |
655 | __m128 r = _mm_mul_ps(v1.packed, v2.packed);
656 | r = _mm_hadd_ps(r, r);
657 | r = _mm_hadd_ps(r, r);
658 | _mm_store_ss(&result, r);
659 |
660 | #else
661 |
662 | result = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w;
663 |
664 | #endif
665 |
666 | return result;
667 | }
668 |
669 | //cross product
670 |
671 | inline vec3 cross(const vec3& v1, const vec3& v2)
672 | {
673 | vec3 result;
674 |
675 | result.x = (v1.y * v2.z) - (v1.z * v2.y);
676 | result.y = (v1.z * v2.x) - (v1.x * v2.z);
677 | result.z = (v1.x * v2.y) - (v1.y * v2.x);
678 |
679 | return result;
680 | }
681 |
682 | //length:
683 |
684 | inline float length(const vec2& v)
685 | {
686 | float result;
687 |
688 | result = QM_SQRTF(dot(v, v));
689 |
690 | return result;
691 | }
692 |
693 | inline float length(const vec3& v)
694 | {
695 | float result;
696 |
697 | result = QM_SQRTF(dot(v, v));
698 |
699 | return result;
700 | }
701 |
702 | inline float length(const vec4& v)
703 | {
704 | float result;
705 |
706 | result = QM_SQRTF(dot(v, v));
707 |
708 | return result;
709 | }
710 |
711 | //normalize:
712 |
713 | inline vec2 normalize(const vec2& v)
714 | {
715 | vec2 result;
716 |
717 | float len = length(v);
718 | if(len != 0.0f)
719 | result = v / len;
720 |
721 | return result;
722 | }
723 |
724 | inline vec3 normalize(const vec3& v)
725 | {
726 | vec3 result;
727 |
728 | float len = length(v);
729 | if(len != 0.0f)
730 | result = v / len;
731 |
732 | return result;
733 | }
734 |
735 | inline vec4 normalize(const vec4& v)
736 | {
737 | vec4 result;
738 |
739 | float len = length(v);
740 | result = v / len;
741 |
742 | return result;
743 | }
744 |
745 | //distance:
746 |
747 | inline float distance(const vec2& v1, const vec2& v2)
748 | {
749 | float result;
750 |
751 | vec2 to = v1 - v2;
752 | result = length(to);
753 |
754 | return result;
755 | }
756 |
757 | inline float distance(const vec3& v1, const vec3& v2)
758 | {
759 | float result;
760 |
761 | vec3 to = v1 - v2;
762 | result = length(to);
763 |
764 | return result;
765 | }
766 |
767 | inline float distance(const vec4& v1, const vec4& v2)
768 | {
769 | float result;
770 |
771 | vec4 to = v1 - v2;
772 | result = length(to);
773 |
774 | return result;
775 | }
776 |
777 | //equality:
778 |
779 | inline bool operator==(const vec2& v1, const vec2& v2)
780 | {
781 | bool result;
782 |
783 | result = (v1.x == v2.x) && (v1.y == v2.y);
784 |
785 | return result;
786 | }
787 |
788 | inline bool operator==(const vec3& v1, const vec3& v2)
789 | {
790 | bool result;
791 |
792 | result = (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z);
793 |
794 | return result;
795 | }
796 |
797 | inline bool operator==(const vec4& v1, const vec4& v2)
798 | {
799 | bool result;
800 |
801 | //TODO: there are SIMD instructions for floating point equality, find a way to get a single bool from them
802 | result = (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z) && (v1.w == v2.w);
803 |
804 | return result;
805 | }
806 |
807 | inline bool operator!=(const vec2& v1, const vec2& v2)
808 | {
809 | bool result;
810 |
811 | result = (v1.x != v2.x) || (v1.y != v2.y);
812 |
813 | return result;
814 | }
815 |
816 | inline bool operator!=(const vec3& v1, const vec3& v2)
817 | {
818 | bool result;
819 |
820 | result = (v1.x != v2.x) || (v1.y != v2.y) || (v1.z != v2.z);
821 |
822 | return result;
823 | }
824 |
825 | inline bool operator!=(const vec4& v1, const vec4& v2)
826 | {
827 | bool result;
828 |
829 | result = (v1.x != v2.x) || (v1.y != v2.y) || (v1.z != v2.z) || (v1.w != v2.w);
830 |
831 | return result;
832 | }
833 |
834 | //min:
835 |
836 | inline vec2 min(const vec2& v1, const vec2& v2)
837 | {
838 | vec2 result;
839 |
840 | result.x = QM_MIN(v1.x, v2.x);
841 | result.y = QM_MIN(v1.y, v2.y);
842 |
843 | return result;
844 | }
845 |
846 | inline vec3 min(const vec3& v1, const vec3& v2)
847 | {
848 | vec3 result;
849 |
850 | result.x = QM_MIN(v1.x, v2.x);
851 | result.y = QM_MIN(v1.y, v2.y);
852 | result.z = QM_MIN(v1.z, v2.z);
853 |
854 | return result;
855 | }
856 |
857 | inline vec4 min(const vec4& v1, const vec4& v2)
858 | {
859 | vec4 result;
860 |
861 | #if QM_USE_SSE
862 |
863 | result.packed = _mm_min_ps(v1.packed, v2.packed);
864 |
865 | #else
866 |
867 | result.x = QM_MIN(v1.x, v2.x);
868 | result.y = QM_MIN(v1.y, v2.y);
869 | result.z = QM_MIN(v1.z, v2.z);
870 | result.w = QM_MIN(v1.w, v2.w);
871 |
872 | #endif
873 |
874 | return result;
875 | }
876 |
877 | //max:
878 |
879 | inline vec2 max(const vec2& v1, const vec2& v2)
880 | {
881 | vec2 result;
882 |
883 | result.x = QM_MAX(v1.x, v2.x);
884 | result.y = QM_MAX(v1.y, v2.y);
885 |
886 | return result;
887 | }
888 |
889 | inline vec3 max(const vec3& v1, const vec3& v2)
890 | {
891 | vec3 result;
892 |
893 | result.x = QM_MAX(v1.x, v2.x);
894 | result.y = QM_MAX(v1.y, v2.y);
895 | result.z = QM_MAX(v1.z, v2.z);
896 |
897 | return result;
898 | }
899 |
900 | inline vec4 max(const vec4& v1, const vec4& v2)
901 | {
902 | vec4 result;
903 |
904 | #if QM_USE_SSE
905 |
906 | result.packed = _mm_max_ps(v1.packed, v2.packed);
907 |
908 | #else
909 |
910 | result.x = QM_MAX(v1.x, v2.x);
911 | result.y = QM_MAX(v1.y, v2.y);
912 | result.z = QM_MAX(v1.z, v2.z);
913 | result.w = QM_MAX(v1.w, v2.w);
914 |
915 | #endif
916 |
917 | return result;
918 | }
919 |
920 | //----------------------------------------------------------------------//
921 | //MATRIX FUNCTIONS:
922 |
923 | #if QM_INCLUDE_IOSTREAM
924 |
925 | //output:
926 |
927 | inline std::ostream& operator<<(std::ostream& os, const mat3& m)
928 | {
929 | os << m.v[0] << std::endl << m.v[1] << std::endl << m.v[2];
930 | return os;
931 | }
932 |
933 | inline std::ostream& operator<<(std::ostream& os, const mat4& m)
934 | {
935 | os << m.v[0] << std::endl << m.v[1] << std::endl << m.v[2] << std::endl << m.v[3];
936 | return os;
937 | }
938 |
939 | //input:
940 |
941 | inline std::istream& operator>>(std::istream& is, mat3& m)
942 | {
943 | is >> m.v[0] >> m.v[1] >> m.v[2];
944 | return is;
945 | }
946 |
947 | inline std::istream& operator>>(std::istream& is, mat4& m)
948 | {
949 | is >> m.v[0] >> m.v[1] >> m.v[2] >> m.v[3];
950 | return is;
951 | }
952 |
953 | #endif
954 |
955 | //initialization:
956 |
957 | inline mat3 mat3_identity()
958 | {
959 | mat3 result;
960 |
961 | result.m[0][0] = 1.0f;
962 | result.m[1][1] = 1.0f;
963 | result.m[2][2] = 1.0f;
964 |
965 | return result;
966 | }
967 |
968 | inline mat4 mat4_identity()
969 | {
970 | mat4 result;
971 |
972 | result.m[0][0] = 1.0f;
973 | result.m[1][1] = 1.0f;
974 | result.m[2][2] = 1.0f;
975 | result.m[3][3] = 1.0f;
976 |
977 | return result;
978 | }
979 |
980 | //addition:
981 |
982 | inline mat3 operator+(const mat3& m1, const mat3& m2)
983 | {
984 | mat3 result;
985 |
986 | result.m[0][0] = m1.m[0][0] + m2.m[0][0];
987 | result.m[0][1] = m1.m[0][1] + m2.m[0][1];
988 | result.m[0][2] = m1.m[0][2] + m2.m[0][2];
989 | result.m[1][0] = m1.m[1][0] + m2.m[1][0];
990 | result.m[1][1] = m1.m[1][1] + m2.m[1][1];
991 | result.m[1][2] = m1.m[1][2] + m2.m[1][2];
992 | result.m[2][0] = m1.m[2][0] + m2.m[2][0];
993 | result.m[2][1] = m1.m[2][1] + m2.m[2][1];
994 | result.m[2][2] = m1.m[2][2] + m2.m[2][2];
995 |
996 | return result;
997 | }
998 |
999 | inline mat4 operator+(const mat4& m1, const mat4& m2)
1000 | {
1001 | mat4 result;
1002 |
1003 | #if QM_USE_SSE
1004 |
1005 | result.packed[0] = _mm_add_ps(m1.packed[0], m2.packed[0]);
1006 | result.packed[1] = _mm_add_ps(m1.packed[1], m2.packed[1]);
1007 | result.packed[2] = _mm_add_ps(m1.packed[2], m2.packed[2]);
1008 | result.packed[3] = _mm_add_ps(m1.packed[3], m2.packed[3]);
1009 |
1010 | #else
1011 |
1012 | result.m[0][0] = m1.m[0][0] + m2.m[0][0];
1013 | result.m[0][1] = m1.m[0][1] + m2.m[0][1];
1014 | result.m[0][2] = m1.m[0][2] + m2.m[0][2];
1015 | result.m[0][3] = m1.m[0][3] + m2.m[0][3];
1016 | result.m[1][0] = m1.m[1][0] + m2.m[1][0];
1017 | result.m[1][1] = m1.m[1][1] + m2.m[1][1];
1018 | result.m[1][2] = m1.m[1][2] + m2.m[1][2];
1019 | result.m[1][3] = m1.m[1][3] + m2.m[1][3];
1020 | result.m[2][0] = m1.m[2][0] + m2.m[2][0];
1021 | result.m[2][1] = m1.m[2][1] + m2.m[2][1];
1022 | result.m[2][2] = m1.m[2][2] + m2.m[2][2];
1023 | result.m[2][3] = m1.m[2][3] + m2.m[2][3];
1024 | result.m[3][0] = m1.m[3][0] + m2.m[3][0];
1025 | result.m[3][1] = m1.m[3][1] + m2.m[3][1];
1026 | result.m[3][2] = m1.m[3][2] + m2.m[3][2];
1027 | result.m[3][3] = m1.m[3][3] + m2.m[3][3];
1028 |
1029 | #endif
1030 |
1031 | return result;
1032 | }
1033 |
1034 | //subtraction:
1035 |
1036 | inline mat3 operator-(const mat3& m1, const mat3& m2)
1037 | {
1038 | mat3 result;
1039 |
1040 | result.m[0][0] = m1.m[0][0] - m2.m[0][0];
1041 | result.m[0][1] = m1.m[0][1] - m2.m[0][1];
1042 | result.m[0][2] = m1.m[0][2] - m2.m[0][2];
1043 | result.m[1][0] = m1.m[1][0] - m2.m[1][0];
1044 | result.m[1][1] = m1.m[1][1] - m2.m[1][1];
1045 | result.m[1][2] = m1.m[1][2] - m2.m[1][2];
1046 | result.m[2][0] = m1.m[2][0] - m2.m[2][0];
1047 | result.m[2][1] = m1.m[2][1] - m2.m[2][1];
1048 | result.m[2][2] = m1.m[2][2] - m2.m[2][2];
1049 |
1050 | return result;
1051 | }
1052 |
1053 | inline mat4 operator-(const mat4& m1, const mat4& m2)
1054 | {
1055 | mat4 result;
1056 |
1057 | #if QM_USE_SSE
1058 |
1059 | result.packed[0] = _mm_sub_ps(m1.packed[0], m2.packed[0]);
1060 | result.packed[1] = _mm_sub_ps(m1.packed[1], m2.packed[1]);
1061 | result.packed[2] = _mm_sub_ps(m1.packed[2], m2.packed[2]);
1062 | result.packed[3] = _mm_sub_ps(m1.packed[3], m2.packed[3]);
1063 |
1064 | #else
1065 |
1066 | result.m[0][0] = m1.m[0][0] - m2.m[0][0];
1067 | result.m[0][1] = m1.m[0][1] - m2.m[0][1];
1068 | result.m[0][2] = m1.m[0][2] - m2.m[0][2];
1069 | result.m[0][3] = m1.m[0][3] - m2.m[0][3];
1070 | result.m[1][0] = m1.m[1][0] - m2.m[1][0];
1071 | result.m[1][1] = m1.m[1][1] - m2.m[1][1];
1072 | result.m[1][2] = m1.m[1][2] - m2.m[1][2];
1073 | result.m[1][3] = m1.m[1][3] - m2.m[1][3];
1074 | result.m[2][0] = m1.m[2][0] - m2.m[2][0];
1075 | result.m[2][1] = m1.m[2][1] - m2.m[2][1];
1076 | result.m[2][2] = m1.m[2][2] - m2.m[2][2];
1077 | result.m[2][3] = m1.m[2][3] - m2.m[2][3];
1078 | result.m[3][0] = m1.m[3][0] - m2.m[3][0];
1079 | result.m[3][1] = m1.m[3][1] - m2.m[3][1];
1080 | result.m[3][2] = m1.m[3][2] - m2.m[3][2];
1081 | result.m[3][3] = m1.m[3][3] - m2.m[3][3];
1082 |
1083 | #endif
1084 |
1085 | return result;
1086 | }
1087 |
1088 | //multiplication:
1089 |
1090 | inline mat3 operator*(const mat3& m1, const mat3& m2)
1091 | {
1092 | mat3 result;
1093 |
1094 | result.m[0][0] = m1.m[0][0] * m2.m[0][0] + m1.m[1][0] * m2.m[0][1] + m1.m[2][0] * m2.m[0][2];
1095 | result.m[0][1] = m1.m[0][1] * m2.m[0][0] + m1.m[1][1] * m2.m[0][1] + m1.m[2][1] * m2.m[0][2];
1096 | result.m[0][2] = m1.m[0][2] * m2.m[0][0] + m1.m[1][2] * m2.m[0][1] + m1.m[2][2] * m2.m[0][2];
1097 | result.m[1][0] = m1.m[0][0] * m2.m[1][0] + m1.m[1][0] * m2.m[1][1] + m1.m[2][0] * m2.m[1][2];
1098 | result.m[1][1] = m1.m[0][1] * m2.m[1][0] + m1.m[1][1] * m2.m[1][1] + m1.m[2][1] * m2.m[1][2];
1099 | result.m[1][2] = m1.m[0][2] * m2.m[1][0] + m1.m[1][2] * m2.m[1][1] + m1.m[2][2] * m2.m[1][2];
1100 | result.m[2][0] = m1.m[0][0] * m2.m[2][0] + m1.m[1][0] * m2.m[2][1] + m1.m[2][0] * m2.m[2][2];
1101 | result.m[2][1] = m1.m[0][1] * m2.m[2][0] + m1.m[1][1] * m2.m[2][1] + m1.m[2][1] * m2.m[2][2];
1102 | result.m[2][2] = m1.m[0][2] * m2.m[2][0] + m1.m[1][2] * m2.m[2][1] + m1.m[2][2] * m2.m[2][2];
1103 |
1104 | return result;
1105 | }
1106 |
1107 | inline mat4 operator*(const mat4& m1, const mat4& m2)
1108 | {
1109 | mat4 result;
1110 |
1111 | #if QM_USE_SSE
1112 |
1113 | result.packed[0] = mat4_mult_column_sse(m2.packed[0], m1);
1114 | result.packed[1] = mat4_mult_column_sse(m2.packed[1], m1);
1115 | result.packed[2] = mat4_mult_column_sse(m2.packed[2], m1);
1116 | result.packed[3] = mat4_mult_column_sse(m2.packed[3], m1);
1117 |
1118 | #else
1119 |
1120 | result.m[0][0] = m1.m[0][0] * m2.m[0][0] + m1.m[1][0] * m2.m[0][1] + m1.m[2][0] * m2.m[0][2] + m1.m[3][0] * m2.m[0][3];
1121 | result.m[0][1] = m1.m[0][1] * m2.m[0][0] + m1.m[1][1] * m2.m[0][1] + m1.m[2][1] * m2.m[0][2] + m1.m[3][1] * m2.m[0][3];
1122 | result.m[0][2] = m1.m[0][2] * m2.m[0][0] + m1.m[1][2] * m2.m[0][1] + m1.m[2][2] * m2.m[0][2] + m1.m[3][2] * m2.m[0][3];
1123 | result.m[0][3] = m1.m[0][3] * m2.m[0][0] + m1.m[1][3] * m2.m[0][1] + m1.m[2][3] * m2.m[0][2] + m1.m[3][3] * m2.m[0][3];
1124 | result.m[1][0] = m1.m[0][0] * m2.m[1][0] + m1.m[1][0] * m2.m[1][1] + m1.m[2][0] * m2.m[1][2] + m1.m[3][0] * m2.m[1][3];
1125 | result.m[1][1] = m1.m[0][1] * m2.m[1][0] + m1.m[1][1] * m2.m[1][1] + m1.m[2][1] * m2.m[1][2] + m1.m[3][1] * m2.m[1][3];
1126 | result.m[1][2] = m1.m[0][2] * m2.m[1][0] + m1.m[1][2] * m2.m[1][1] + m1.m[2][2] * m2.m[1][2] + m1.m[3][2] * m2.m[1][3];
1127 | result.m[1][3] = m1.m[0][3] * m2.m[1][0] + m1.m[1][3] * m2.m[1][1] + m1.m[2][3] * m2.m[1][2] + m1.m[3][3] * m2.m[1][3];
1128 | result.m[2][0] = m1.m[0][0] * m2.m[2][0] + m1.m[1][0] * m2.m[2][1] + m1.m[2][0] * m2.m[2][2] + m1.m[3][0] * m2.m[2][3];
1129 | result.m[2][1] = m1.m[0][1] * m2.m[2][0] + m1.m[1][1] * m2.m[2][1] + m1.m[2][1] * m2.m[2][2] + m1.m[3][1] * m2.m[2][3];
1130 | result.m[2][2] = m1.m[0][2] * m2.m[2][0] + m1.m[1][2] * m2.m[2][1] + m1.m[2][2] * m2.m[2][2] + m1.m[3][2] * m2.m[2][3];
1131 | result.m[2][3] = m1.m[0][3] * m2.m[2][0] + m1.m[1][3] * m2.m[2][1] + m1.m[2][3] * m2.m[2][2] + m1.m[3][3] * m2.m[2][3];
1132 | result.m[3][0] = m1.m[0][0] * m2.m[3][0] + m1.m[1][0] * m2.m[3][1] + m1.m[2][0] * m2.m[3][2] + m1.m[3][0] * m2.m[3][3];
1133 | result.m[3][1] = m1.m[0][1] * m2.m[3][0] + m1.m[1][1] * m2.m[3][1] + m1.m[2][1] * m2.m[3][2] + m1.m[3][1] * m2.m[3][3];
1134 | result.m[3][2] = m1.m[0][2] * m2.m[3][0] + m1.m[1][2] * m2.m[3][1] + m1.m[2][2] * m2.m[3][2] + m1.m[3][2] * m2.m[3][3];
1135 | result.m[3][3] = m1.m[0][3] * m2.m[3][0] + m1.m[1][3] * m2.m[3][1] + m1.m[2][3] * m2.m[3][2] + m1.m[3][3] * m2.m[3][3];
1136 |
1137 | #endif
1138 |
1139 | return result;
1140 | }
1141 |
1142 | inline vec3 operator*(const mat3& m, const vec3& v)
1143 | {
1144 | vec3 result;
1145 |
1146 | result.x = m.m[0][0] * v.x + m.m[1][0] * v.y + m.m[2][0] * v.z;
1147 | result.y = m.m[0][1] * v.x + m.m[1][1] * v.y + m.m[2][1] * v.z;
1148 | result.z = m.m[0][2] * v.x + m.m[1][2] * v.y + m.m[2][2] * v.z;
1149 |
1150 | return result;
1151 | }
1152 |
1153 | inline vec4 operator*(const mat4& m, const vec4& v)
1154 | {
1155 | vec4 result;
1156 |
1157 | #if QM_USE_SSE
1158 |
1159 | result.packed = mat4_mult_column_sse(v.packed, m);
1160 |
1161 | #else
1162 |
1163 | result.x = m.m[0][0] * v.x + m.m[1][0] * v.y + m.m[2][0] * v.z + m.m[3][0] * v.w;
1164 | result.y = m.m[0][1] * v.x + m.m[1][1] * v.y + m.m[2][1] * v.z + m.m[3][1] * v.w;
1165 | result.z = m.m[0][2] * v.x + m.m[1][2] * v.y + m.m[2][2] * v.z + m.m[3][2] * v.w;
1166 | result.w = m.m[0][3] * v.x + m.m[1][3] * v.y + m.m[2][3] * v.z + m.m[3][3] * v.w;
1167 |
1168 | #endif
1169 |
1170 | return result;
1171 | }
1172 |
1173 | //transpose:
1174 |
1175 | inline mat3 transpose(const mat3& m)
1176 | {
1177 | mat3 result;
1178 |
1179 | result.m[0][0] = m.m[0][0];
1180 | result.m[0][1] = m.m[1][0];
1181 | result.m[0][2] = m.m[2][0];
1182 | result.m[1][0] = m.m[0][1];
1183 | result.m[1][1] = m.m[1][1];
1184 | result.m[1][2] = m.m[2][1];
1185 | result.m[2][0] = m.m[0][2];
1186 | result.m[2][1] = m.m[1][2];
1187 | result.m[2][2] = m.m[2][2];
1188 |
1189 | return result;
1190 | }
1191 |
1192 | inline mat4 transpose(const mat4& m)
1193 | {
1194 | mat4 result = m;
1195 |
1196 | #if QM_USE_SSE
1197 |
1198 | _MM_TRANSPOSE4_PS(result.packed[0], result.packed[1], result.packed[2], result.packed[3]);
1199 |
1200 | #else
1201 |
1202 | result.m[0][0] = m.m[0][0];
1203 | result.m[0][1] = m.m[1][0];
1204 | result.m[0][2] = m.m[2][0];
1205 | result.m[0][3] = m.m[3][0];
1206 | result.m[1][0] = m.m[0][1];
1207 | result.m[1][1] = m.m[1][1];
1208 | result.m[1][2] = m.m[2][1];
1209 | result.m[1][3] = m.m[3][1];
1210 | result.m[2][0] = m.m[0][2];
1211 | result.m[2][1] = m.m[1][2];
1212 | result.m[2][2] = m.m[2][2];
1213 | result.m[2][3] = m.m[3][2];
1214 | result.m[3][0] = m.m[0][3];
1215 | result.m[3][1] = m.m[1][3];
1216 | result.m[3][2] = m.m[2][3];
1217 | result.m[3][3] = m.m[3][3];
1218 |
1219 | #endif
1220 |
1221 | return result;
1222 | }
1223 |
1224 | //inverse:
1225 |
1226 | inline mat3 inverse(const mat3& m)
1227 | {
1228 | mat3 result;
1229 |
1230 | float det;
1231 | float a = m.m[0][0], b = m.m[0][1], c = m.m[0][2],
1232 | d = m.m[1][0], e = m.m[1][1], f = m.m[1][2],
1233 | g = m.m[2][0], h = m.m[2][1], i = m.m[2][2];
1234 |
1235 | result.m[0][0] = e * i - f * h;
1236 | result.m[0][1] = -(b * i - h * c);
1237 | result.m[0][2] = b * f - e * c;
1238 | result.m[1][0] = -(d * i - g * f);
1239 | result.m[1][1] = a * i - c * g;
1240 | result.m[1][2] = -(a * f - d * c);
1241 | result.m[2][0] = d * h - g * e;
1242 | result.m[2][1] = -(a * h - g * b);
1243 | result.m[2][2] = a * e - b * d;
1244 |
1245 | det = 1.0f / (a * result.m[0][0] + b * result.m[1][0] + c * result.m[2][0]);
1246 |
1247 | result.m[0][0] *= det;
1248 | result.m[0][1] *= det;
1249 | result.m[0][2] *= det;
1250 | result.m[1][0] *= det;
1251 | result.m[1][1] *= det;
1252 | result.m[1][2] *= det;
1253 | result.m[2][0] *= det;
1254 | result.m[2][1] *= det;
1255 | result.m[2][2] *= det;
1256 |
1257 | return result;
1258 | }
1259 |
1260 | inline mat4 inverse(const mat4& mat)
1261 | {
1262 | //TODO: this function is not SIMD optimized, figure out how to do it
1263 |
1264 | mat4 result;
1265 |
1266 | float tmp[6];
1267 | float det;
1268 | float a = mat.m[0][0], b = mat.m[0][1], c = mat.m[0][2], d = mat.m[0][3],
1269 | e = mat.m[1][0], f = mat.m[1][1], g = mat.m[1][2], h = mat.m[1][3],
1270 | i = mat.m[2][0], j = mat.m[2][1], k = mat.m[2][2], l = mat.m[2][3],
1271 | m = mat.m[3][0], n = mat.m[3][1], o = mat.m[3][2], p = mat.m[3][3];
1272 |
1273 | tmp[0] = k * p - o * l;
1274 | tmp[1] = j * p - n * l;
1275 | tmp[2] = j * o - n * k;
1276 | tmp[3] = i * p - m * l;
1277 | tmp[4] = i * o - m * k;
1278 | tmp[5] = i * n - m * j;
1279 |
1280 | result.m[0][0] = f * tmp[0] - g * tmp[1] + h * tmp[2];
1281 | result.m[1][0] = -(e * tmp[0] - g * tmp[3] + h * tmp[4]);
1282 | result.m[2][0] = e * tmp[1] - f * tmp[3] + h * tmp[5];
1283 | result.m[3][0] = -(e * tmp[2] - f * tmp[4] + g * tmp[5]);
1284 |
1285 | result.m[0][1] = -(b * tmp[0] - c * tmp[1] + d * tmp[2]);
1286 | result.m[1][1] = a * tmp[0] - c * tmp[3] + d * tmp[4];
1287 | result.m[2][1] = -(a * tmp[1] - b * tmp[3] + d * tmp[5]);
1288 | result.m[3][1] = a * tmp[2] - b * tmp[4] + c * tmp[5];
1289 |
1290 | tmp[0] = g * p - o * h;
1291 | tmp[1] = f * p - n * h;
1292 | tmp[2] = f * o - n * g;
1293 | tmp[3] = e * p - m * h;
1294 | tmp[4] = e * o - m * g;
1295 | tmp[5] = e * n - m * f;
1296 |
1297 | result.m[0][2] = b * tmp[0] - c * tmp[1] + d * tmp[2];
1298 | result.m[1][2] = -(a * tmp[0] - c * tmp[3] + d * tmp[4]);
1299 | result.m[2][2] = a * tmp[1] - b * tmp[3] + d * tmp[5];
1300 | result.m[3][2] = -(a * tmp[2] - b * tmp[4] + c * tmp[5]);
1301 |
1302 | tmp[0] = g * l - k * h;
1303 | tmp[1] = f * l - j * h;
1304 | tmp[2] = f * k - j * g;
1305 | tmp[3] = e * l - i * h;
1306 | tmp[4] = e * k - i * g;
1307 | tmp[5] = e * j - i * f;
1308 |
1309 | result.m[0][3] = -(b * tmp[0] - c * tmp[1] + d * tmp[2]);
1310 | result.m[1][3] = a * tmp[0] - c * tmp[3] + d * tmp[4];
1311 | result.m[2][3] = -(a * tmp[1] - b * tmp[3] + d * tmp[5]);
1312 | result.m[3][3] = a * tmp[2] - b * tmp[4] + c * tmp[5];
1313 |
1314 | det = 1.0f / (a * result.m[0][0] + b * result.m[1][0]
1315 | + c * result.m[2][0] + d * result.m[3][0]);
1316 |
1317 | #if QM_USE_SSE
1318 |
1319 | __m128 scale = _mm_set1_ps(det);
1320 | result.packed[0] = _mm_mul_ps(result.packed[0], scale);
1321 | result.packed[1] = _mm_mul_ps(result.packed[1], scale);
1322 | result.packed[2] = _mm_mul_ps(result.packed[2], scale);
1323 | result.packed[3] = _mm_mul_ps(result.packed[3], scale);
1324 |
1325 | #else
1326 |
1327 | result.m[0][0] = result.m[0][0] * det;
1328 | result.m[0][1] = result.m[0][1] * det;
1329 | result.m[0][2] = result.m[0][2] * det;
1330 | result.m[0][3] = result.m[0][3] * det;
1331 | result.m[1][0] = result.m[1][0] * det;
1332 | result.m[1][1] = result.m[1][1] * det;
1333 | result.m[1][2] = result.m[1][2] * det;
1334 | result.m[1][3] = result.m[1][3] * det;
1335 | result.m[2][0] = result.m[2][0] * det;
1336 | result.m[2][1] = result.m[2][1] * det;
1337 | result.m[2][2] = result.m[2][2] * det;
1338 | result.m[2][3] = result.m[2][3] * det;
1339 | result.m[3][0] = result.m[3][0] * det;
1340 | result.m[3][1] = result.m[3][1] * det;
1341 | result.m[3][2] = result.m[3][2] * det;
1342 | result.m[3][3] = result.m[3][3] * det;
1343 |
1344 | #endif
1345 |
1346 | return result;
1347 | }
1348 |
1349 | //translation:
1350 |
1351 | inline mat3 translate(const vec2& t)
1352 | {
1353 | mat3 result = mat3_identity();
1354 |
1355 | result.m[2][0] = t.x;
1356 | result.m[2][1] = t.y;
1357 |
1358 | return result;
1359 | }
1360 |
1361 | inline mat4 translate(const vec3& t)
1362 | {
1363 | mat4 result = mat4_identity();
1364 |
1365 | result.m[3][0] = t.x;
1366 | result.m[3][1] = t.y;
1367 | result.m[3][2] = t.z;
1368 |
1369 | return result;
1370 | }
1371 |
1372 | //scaling:
1373 |
1374 | inline mat3 scale(const vec2& s)
1375 | {
1376 | mat3 result = mat3_identity();
1377 |
1378 | result.m[0][0] = s.x;
1379 | result.m[1][1] = s.y;
1380 |
1381 | return result;
1382 | }
1383 |
1384 | inline mat4 scale(const vec3& s)
1385 | {
1386 | mat4 result = mat4_identity();
1387 |
1388 | result.m[0][0] = s.x;
1389 | result.m[1][1] = s.y;
1390 | result.m[2][2] = s.z;
1391 |
1392 | return result;
1393 | }
1394 |
1395 | //rotation:
1396 |
1397 | inline mat3 rotate(float angle)
1398 | {
1399 | mat3 result = mat3_identity();
1400 |
1401 | float radians = deg_to_rad(angle);
1402 | float sine = QM_SINF(radians);
1403 | float cosine = QM_COSF(radians);
1404 |
1405 | result.m[0][0] = cosine;
1406 | result.m[1][0] = sine;
1407 | result.m[0][1] = -sine;
1408 | result.m[1][1] = cosine;
1409 |
1410 | return result;
1411 | }
1412 |
1413 | inline mat4 rotate(const vec3& axis, float angle)
1414 | {
1415 | mat4 result = mat4_identity();
1416 |
1417 | vec3 normalized = normalize(axis);
1418 |
1419 | float radians = deg_to_rad(angle);
1420 | float sine = QM_SINF(radians);
1421 | float cosine = QM_COSF(radians);
1422 | float cosine2 = 1.0f - cosine;
1423 |
1424 | result.m[0][0] = normalized.x * normalized.x * cosine2 + cosine;
1425 | result.m[0][1] = normalized.x * normalized.y * cosine2 + normalized.z * sine;
1426 | result.m[0][2] = normalized.x * normalized.z * cosine2 - normalized.y * sine;
1427 | result.m[1][0] = normalized.y * normalized.x * cosine2 - normalized.z * sine;
1428 | result.m[1][1] = normalized.y * normalized.y * cosine2 + cosine;
1429 | result.m[1][2] = normalized.y * normalized.z * cosine2 + normalized.x * sine;
1430 | result.m[2][0] = normalized.z * normalized.x * cosine2 + normalized.y * sine;
1431 | result.m[2][1] = normalized.z * normalized.y * cosine2 - normalized.x * sine;
1432 | result.m[2][2] = normalized.z * normalized.z * cosine2 + cosine;
1433 |
1434 | return result;
1435 | }
1436 |
1437 | inline mat4 rotate(const vec3& euler)
1438 | {
1439 | mat4 result = mat4_identity();
1440 |
1441 | vec3 radians;
1442 | radians.x = deg_to_rad(euler.x);
1443 | radians.y = deg_to_rad(euler.y);
1444 | radians.z = deg_to_rad(euler.z);
1445 |
1446 | float sinX = QM_SINF(radians.x);
1447 | float cosX = QM_COSF(radians.x);
1448 | float sinY = QM_SINF(radians.y);
1449 | float cosY = QM_COSF(radians.y);
1450 | float sinZ = QM_SINF(radians.z);
1451 | float cosZ = QM_COSF(radians.z);
1452 |
1453 | result.m[0][0] = cosY * cosZ;
1454 | result.m[0][1] = cosY * sinZ;
1455 | result.m[0][2] = -sinY;
1456 | result.m[1][0] = sinX * sinY * cosZ - cosX * sinZ;
1457 | result.m[1][1] = sinX * sinY * sinZ + cosX * cosZ;
1458 | result.m[1][2] = sinX * cosY;
1459 | result.m[2][0] = cosX * sinY * cosZ + sinX * sinZ;
1460 | result.m[2][1] = cosX * sinY * sinZ - sinX * cosZ;
1461 | result.m[2][2] = cosX * cosY;
1462 |
1463 | return result;
1464 | }
1465 |
1466 | //to mat3:
1467 |
1468 | inline mat3 top_left(const mat4& m)
1469 | {
1470 | mat3 result;
1471 |
1472 | result.m[0][0] = m.m[0][0];
1473 | result.m[0][1] = m.m[0][1];
1474 | result.m[0][2] = m.m[0][2];
1475 | result.m[1][0] = m.m[1][0];
1476 | result.m[1][1] = m.m[1][1];
1477 | result.m[1][2] = m.m[1][2];
1478 | result.m[2][0] = m.m[2][0];
1479 | result.m[2][1] = m.m[2][1];
1480 | result.m[2][2] = m.m[2][2];
1481 |
1482 | return result;
1483 | }
1484 |
1485 | //projection:
1486 |
1487 | inline mat4 perspective(float fov, float aspect, float near, float far)
1488 | {
1489 | mat4 result;
1490 |
1491 | float scale = QM_TANF(deg_to_rad(fov * 0.5f)) * near;
1492 |
1493 | float right = aspect * scale;
1494 | float left = -right;
1495 | float top = scale;
1496 | float bot = -top;
1497 |
1498 | result.m[0][0] = near / right;
1499 | result.m[1][1] = near / top;
1500 | result.m[2][2] = -(far + near) / (far - near);
1501 | result.m[3][2] = -2.0f * far * near / (far - near);
1502 | result.m[2][3] = -1.0f;
1503 |
1504 | return result;
1505 | }
1506 |
1507 | inline mat4 orthographic(float left, float right, float bot, float top, float near, float far)
1508 | {
1509 | mat4 result = mat4_identity();
1510 |
1511 | result.m[0][0] = 2.0f / (right - left);
1512 | result.m[1][1] = 2.0f / (top - bot);
1513 | result.m[2][2] = 2.0f / (near - far);
1514 |
1515 | result.m[3][0] = (left + right) / (left - right);
1516 | result.m[3][1] = (bot + top ) / (bot - top );
1517 | result.m[3][2] = (near + far ) / (near - far );
1518 |
1519 | return result;
1520 | }
1521 |
1522 | //view matrix:
1523 |
1524 | inline mat4 look(const vec3& pos, const vec3& dir, const vec3& up)
1525 | {
1526 | mat4 result;
1527 |
1528 | vec3 r = normalize(cross(up, dir));
1529 | vec3 u = cross(dir, r);
1530 |
1531 | mat4 RUD = mat4_identity();
1532 | RUD.m[0][0] = r.x;
1533 | RUD.m[1][0] = r.y;
1534 | RUD.m[2][0] = r.z;
1535 | RUD.m[0][1] = u.x;
1536 | RUD.m[1][1] = u.y;
1537 | RUD.m[2][1] = u.z;
1538 | RUD.m[0][2] = dir.x;
1539 | RUD.m[1][2] = dir.y;
1540 | RUD.m[2][2] = dir.z;
1541 |
1542 | vec3 oppPos = {-pos.x, -pos.y, -pos.z};
1543 | result = RUD * translate(oppPos);
1544 |
1545 | return result;
1546 | }
1547 |
1548 | inline mat4 lookat(const vec3& pos, const vec3& target, const vec3& up)
1549 | {
1550 | mat4 result;
1551 |
1552 | vec3 dir = normalize(pos - target);
1553 | result = look(pos, dir, up);
1554 |
1555 | return result;
1556 | }
1557 |
1558 | //----------------------------------------------------------------------//
1559 | //QUATERNION FUNCTIONS:
1560 |
1561 | #if QM_INCLUDE_IOSTREAM
1562 |
1563 | inline std::ostream& operator<<(std::ostream& os, const quaternion& q)
1564 | {
1565 | os << q.x << ", " << q.y << ", " << q.z << ", " << q.w;
1566 | return os;
1567 | }
1568 |
1569 | inline std::istream& operator>>(std::istream& is, quaternion& q)
1570 | {
1571 | is >> q.x >> q.y >> q.z >> q.w;
1572 | return is;
1573 | }
1574 |
1575 | #endif
1576 |
1577 | inline quaternion quaternion_identity()
1578 | {
1579 | quaternion result;
1580 |
1581 | result.x = 0.0f;
1582 | result.y = 0.0f;
1583 | result.z = 0.0f;
1584 | result.w = 1.0f;
1585 |
1586 | return result;
1587 | }
1588 |
1589 | inline quaternion operator+(const quaternion& q1, const quaternion& q2)
1590 | {
1591 | quaternion result;
1592 |
1593 | #if QM_USE_SSE
1594 |
1595 | result.packed = _mm_add_ps(q1.packed, q2.packed);
1596 |
1597 | #else
1598 |
1599 | result.x = q1.x + q2.x;
1600 | result.y = q1.y + q2.y;
1601 | result.z = q1.z + q2.z;
1602 | result.w = q1.w + q2.w;
1603 |
1604 | #endif
1605 |
1606 | return result;
1607 | }
1608 |
1609 | inline quaternion operator-(const quaternion& q1, const quaternion& q2)
1610 | {
1611 | quaternion result;
1612 |
1613 | #if QM_USE_SSE
1614 |
1615 | result.packed = _mm_sub_ps(q1.packed, q2.packed);
1616 |
1617 | #else
1618 |
1619 | result.x = q1.x - q2.x;
1620 | result.y = q1.y - q2.y;
1621 | result.z = q1.z - q2.z;
1622 | result.w = q1.w - q2.w;
1623 |
1624 | #endif
1625 |
1626 | return result;
1627 | }
1628 |
1629 | inline quaternion operator*(const quaternion& q1, const quaternion& q2)
1630 | {
1631 | quaternion result;
1632 |
1633 | #if QM_USE_SSE
1634 |
1635 | __m128 temp1;
1636 | __m128 temp2;
1637 |
1638 | temp1 = _mm_shuffle_ps(q1.packed, q1.packed, _MM_SHUFFLE(3, 3, 3, 3));
1639 | temp2 = q2.packed;
1640 | result.packed = _mm_mul_ps(temp1, temp2);
1641 |
1642 | temp1 = _mm_xor_ps(_mm_shuffle_ps(q1.packed, q1.packed, _MM_SHUFFLE(0, 0, 0, 0)), _mm_setr_ps(0.0f, -0.0f, 0.0f, -0.0f));
1643 | temp2 = _mm_shuffle_ps(q2.packed, q2.packed, _MM_SHUFFLE(0, 1, 2, 3));
1644 | result.packed = _mm_add_ps(result.packed, _mm_mul_ps(temp1, temp2));
1645 |
1646 | temp1 = _mm_xor_ps(_mm_shuffle_ps(q1.packed, q1.packed, _MM_SHUFFLE(1, 1, 1, 1)), _mm_setr_ps(0.0f, 0.0f, -0.0f, -0.0f));
1647 | temp2 = _mm_shuffle_ps(q2.packed, q2.packed, _MM_SHUFFLE(1, 0, 3, 2));
1648 | result.packed = _mm_add_ps(result.packed, _mm_mul_ps(temp1, temp2));
1649 |
1650 | temp1 = _mm_xor_ps(_mm_shuffle_ps(q1.packed, q1.packed, _MM_SHUFFLE(2, 2, 2, 2)), _mm_setr_ps(-0.0f, 0.0f, 0.0f, -0.0f));
1651 | temp2 = _mm_shuffle_ps(q2.packed, q2.packed, _MM_SHUFFLE(2, 3, 0, 1));
1652 | result.packed = _mm_add_ps(result.packed, _mm_mul_ps(temp1, temp2));
1653 |
1654 | #else
1655 |
1656 | result.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
1657 | result.y = q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x;
1658 | result.z = q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w;
1659 | result.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
1660 |
1661 | #endif
1662 |
1663 | return result;
1664 | }
1665 |
1666 | inline quaternion operator*(const quaternion& q, float s)
1667 | {
1668 | quaternion result;
1669 |
1670 | #if QM_USE_SSE
1671 |
1672 | __m128 scale = _mm_set1_ps(s);
1673 | result.packed = _mm_mul_ps(q.packed, scale);
1674 |
1675 | #else
1676 |
1677 | result.x = q.x * s;
1678 | result.y = q.y * s;
1679 | result.z = q.z * s;
1680 | result.w = q.w * s;
1681 |
1682 | #endif
1683 |
1684 | return result;
1685 | }
1686 |
1687 | inline quaternion operator*(float s, const quaternion& q)
1688 | {
1689 | return q * s;
1690 | }
1691 |
1692 | inline quaternion operator/(const quaternion& q, float s)
1693 | {
1694 | quaternion result;
1695 |
1696 | #if QM_USE_SSE
1697 |
1698 | __m128 scale = _mm_set1_ps(s);
1699 | result.packed = _mm_div_ps(q.packed, scale);
1700 |
1701 | #else
1702 |
1703 | result.x = q.x / s;
1704 | result.y = q.y / s;
1705 | result.z = q.z / s;
1706 | result.w = q.w / s;
1707 |
1708 | #endif
1709 |
1710 | return result;
1711 | }
1712 |
1713 | inline quaternion operator/(float s, const quaternion& q)
1714 | {
1715 | quaternion result;
1716 |
1717 | #if QM_USE_SSE
1718 |
1719 | __m128 scale = _mm_set1_ps(s);
1720 | result.packed = _mm_div_ps(scale, q.packed);
1721 |
1722 | #else
1723 |
1724 | result.x = s / q.x;
1725 | result.y = s / q.y;
1726 | result.z = s / q.z;
1727 | result.w = s / q.w;
1728 |
1729 | #endif
1730 |
1731 | return result;
1732 | }
1733 |
1734 | inline float dot(const quaternion& q1, const quaternion& q2)
1735 | {
1736 | float result;
1737 |
1738 | #if QM_USE_SSE
1739 |
1740 | __m128 r = _mm_mul_ps(q1.packed, q2.packed);
1741 | r = _mm_hadd_ps(r, r);
1742 | r = _mm_hadd_ps(r, r);
1743 | _mm_store_ss(&result, r);
1744 |
1745 | #else
1746 |
1747 | result = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
1748 |
1749 | #endif
1750 |
1751 | return result;
1752 | }
1753 |
1754 | inline float length(const quaternion& q)
1755 | {
1756 | float result;
1757 |
1758 | result = QM_SQRTF(dot(q, q));
1759 |
1760 | return result;
1761 | }
1762 |
1763 | inline quaternion normalize(const quaternion& q)
1764 | {
1765 | quaternion result;
1766 |
1767 | float len = length(q);
1768 | if(len != 0.0f)
1769 | result = q / len;
1770 |
1771 | return result;
1772 | }
1773 |
1774 | inline quaternion conjugate(const quaternion& q)
1775 | {
1776 | quaternion result;
1777 |
1778 | result.x = -q.x;
1779 | result.y = -q.y;
1780 | result.z = -q.z;
1781 | result.w = q.w;
1782 |
1783 | return result;
1784 | }
1785 |
1786 | inline quaternion inverse(const quaternion& q)
1787 | {
1788 | quaternion result;
1789 |
1790 | result.x = -q.x;
1791 | result.y = -q.y;
1792 | result.z = -q.z;
1793 | result.w = q.w;
1794 |
1795 | #if QM_USE_SSE
1796 |
1797 | __m128 scale = _mm_set1_ps(dot(q, q));
1798 | _mm_div_ps(result.packed, scale);
1799 |
1800 | #else
1801 |
1802 | float invLen2 = 1.0f / QM_PREFIX(quaternion_dot)(q, q);
1803 |
1804 | result.x *= invLen2;
1805 | result.y *= invLen2;
1806 | result.z *= invLen2;
1807 | result.w *= invLen2;
1808 |
1809 | #endif
1810 |
1811 | return result;
1812 | }
1813 |
1814 | inline quaternion slerp(const quaternion& q1, const quaternion& q2, float a)
1815 | {
1816 | quaternion result;
1817 |
1818 | float cosine = dot(q1, q2);
1819 | float angle = QM_ACOSF(cosine);
1820 |
1821 | float sine1 = QM_SINF((1.0f - a) * angle);
1822 | float sine2 = QM_SINF(a * angle);
1823 | float invSine = 1.0f / QM_SINF(angle);
1824 |
1825 | quaternion q1scaled = q1 * sine1;
1826 | quaternion q2scaled = q2 * sine2;
1827 |
1828 | result = q1scaled + q2scaled;
1829 | result = result * invSine;
1830 |
1831 | return result;
1832 | }
1833 |
1834 | inline bool operator==(const quaternion& q1, const quaternion& q2)
1835 | {
1836 | bool result;
1837 |
1838 | //TODO: there are SIMD instructions for floating point equality, find a way to get a single bool from them
1839 | result = (q1.x == q2.x) && (q1.y == q2.y) && (q1.z == q2.z) && (q1.w == q2.w);
1840 |
1841 | return result;
1842 | }
1843 |
1844 | inline bool operator!=(const quaternion& q1, const quaternion& q2)
1845 | {
1846 | bool result;
1847 |
1848 | result = (q1.x != q2.x) || (q1.y != q2.y) || (q1.z != q2.z) || (q1.w != q2.w);
1849 |
1850 | return result;
1851 | }
1852 |
1853 | inline quaternion quaternion_from_axis_angle(const vec3& axis, float angle)
1854 | {
1855 | quaternion result;
1856 |
1857 | float radians = deg_to_rad(angle * 0.5f);
1858 | vec3 normalized = normalize(axis);
1859 | float sine = QM_SINF(radians);
1860 |
1861 | result.x = normalized.x * sine;
1862 | result.y = normalized.y * sine;
1863 | result.z = normalized.z * sine;
1864 | result.w = QM_COSF(radians);
1865 |
1866 | return result;
1867 | }
1868 |
1869 | inline quaternion quaternion_from_euler(const vec3& angles)
1870 | {
1871 | quaternion result;
1872 |
1873 | vec3 radians;
1874 | radians.x = deg_to_rad(angles.x * 0.5f);
1875 | radians.y = deg_to_rad(angles.y * 0.5f);
1876 | radians.z = deg_to_rad(angles.z * 0.5f);
1877 |
1878 | float sinx = QM_SINF(radians.x);
1879 | float cosx = QM_COSF(radians.x);
1880 | float siny = QM_SINF(radians.y);
1881 | float cosy = QM_COSF(radians.y);
1882 | float sinz = QM_SINF(radians.z);
1883 | float cosz = QM_COSF(radians.z);
1884 |
1885 | #if QM_USE_SSE
1886 |
1887 | __m128 packedx = _mm_setr_ps(sinx, cosx, cosx, cosx);
1888 | __m128 packedy = _mm_setr_ps(cosy, siny, cosy, cosy);
1889 | __m128 packedz = _mm_setr_ps(cosz, cosz, sinz, cosz);
1890 |
1891 | result.packed = _mm_mul_ps(_mm_mul_ps(packedx, packedy), packedz);
1892 |
1893 | packedx = _mm_shuffle_ps(packedx, packedx, _MM_SHUFFLE(0, 0, 0, 1));
1894 | packedy = _mm_shuffle_ps(packedy, packedy, _MM_SHUFFLE(1, 1, 0, 1));
1895 | packedz = _mm_shuffle_ps(packedz, packedz, _MM_SHUFFLE(2, 0, 2, 2));
1896 |
1897 | result.packed = _mm_addsub_ps(result.packed, _mm_mul_ps(_mm_mul_ps(packedx, packedy), packedz));
1898 |
1899 | #else
1900 |
1901 | result.x = sinx * cosy * cosz - cosx * siny * sinz;
1902 | result.y = cosx * siny * cosz + sinx * cosy * sinz;
1903 | result.z = cosx * cosy * sinz - sinx * siny * cosz;
1904 | result.w = cosx * cosy * cosz + sinx * siny * sinz;
1905 |
1906 | #endif
1907 |
1908 | return result;
1909 | }
1910 |
1911 | inline mat4 quaternion_to_mat4(const quaternion& q)
1912 | {
1913 | mat4 result = mat4_identity();
1914 |
1915 | float x2 = q.x + q.x;
1916 | float y2 = q.y + q.y;
1917 | float z2 = q.z + q.z;
1918 | float xx2 = q.x * x2;
1919 | float xy2 = q.x * y2;
1920 | float xz2 = q.x * z2;
1921 | float yy2 = q.y * y2;
1922 | float yz2 = q.y * z2;
1923 | float zz2 = q.z * z2;
1924 | float sx2 = q.w * x2;
1925 | float sy2 = q.w * y2;
1926 | float sz2 = q.w * z2;
1927 |
1928 | result.m[0][0] = 1.0f - (yy2 + zz2);
1929 | result.m[0][1] = xy2 - sz2;
1930 | result.m[0][2] = xz2 + sy2;
1931 | result.m[1][0] = xy2 + sz2;
1932 | result.m[1][1] = 1.0f - (xx2 + zz2);
1933 | result.m[1][2] = yz2 - sx2;
1934 | result.m[2][0] = xz2 - sy2;
1935 | result.m[2][1] = yz2 + sx2;
1936 | result.m[2][2] = 1.0f - (xx2 + yy2);
1937 |
1938 | return result;
1939 | }
1940 |
1941 | }; //namespace qm
1942 |
1943 | #endif //QM_MATH_H
--------------------------------------------------------------------------------