├── .travis.yml ├── luna ├── LibIO.h ├── Upvalue.cpp ├── LibBase.h ├── LibMath.h ├── LibTable.h ├── SemanticAnalysis.h ├── LibString.h ├── Parser.h ├── CodeGenerate.h ├── UserData.cpp ├── Upvalue.h ├── Guard.h ├── Runtime.cpp ├── CMakeLists.txt ├── TextInStream.cpp ├── StringPool.cpp ├── Token.cpp ├── Runtime.h ├── ModuleManager.h ├── TextInStream.h ├── Token.h ├── UserData.h ├── StringPool.h ├── VM.h ├── Luna.cpp ├── String.cpp ├── Value.cpp ├── ModuleManager.cpp ├── Lex.h ├── SyntaxTree.cpp ├── Visitor.h ├── String.h ├── Table.h ├── State.h ├── Exception.h ├── Value.h ├── Function.cpp ├── LibTable.cpp ├── LibString.cpp ├── GC.h ├── OpCode.h ├── LibBase.cpp ├── Function.h ├── LibAPI.h ├── Table.cpp ├── LibAPI.cpp ├── LibMath.cpp ├── State.cpp └── LibIO.cpp ├── CMakeLists.txt ├── .gitignore ├── unittests ├── CMakeLists.txt ├── TestString.cpp ├── UnitTest.cpp ├── UnitTest.h ├── TestTable.cpp ├── TestLex.cpp ├── GCTest.cpp ├── TestCommon.h └── TestSemantic.cpp ├── examples ├── gctest.lua └── calculator.lua ├── LICENSE.md └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - clang 4 | script: cmake -G "Unix Makefiles" && make && ./bin/unittest 5 | notifications: 6 | email: 7 | - superking.airtrack@gmail.com 8 | os: 9 | - linux 10 | - osx 11 | -------------------------------------------------------------------------------- /luna/LibIO.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_IO_H 2 | #define LIB_IO_H 3 | 4 | #include "LibAPI.h" 5 | 6 | namespace lib { 7 | namespace io { 8 | 9 | void RegisterLibIO(luna::State *state); 10 | 11 | } // namespace io 12 | } // namespace lib 13 | 14 | #endif // LIB_IO_H 15 | -------------------------------------------------------------------------------- /luna/Upvalue.cpp: -------------------------------------------------------------------------------- 1 | #include "Upvalue.h" 2 | 3 | namespace luna 4 | { 5 | void Upvalue::Accept(GCObjectVisitor *v) 6 | { 7 | if (v->Visit(this)) 8 | { 9 | value_.Accept(v); 10 | } 11 | } 12 | } // namespace luna 13 | -------------------------------------------------------------------------------- /luna/LibBase.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_BASE_H 2 | #define LIB_BASE_H 3 | 4 | #include "LibAPI.h" 5 | 6 | namespace lib { 7 | namespace base { 8 | 9 | void RegisterLibBase(luna::State *state); 10 | 11 | } // namespace base 12 | } // namespace lib 13 | 14 | #endif // LIB_BASE_H 15 | -------------------------------------------------------------------------------- /luna/LibMath.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_MATH_H 2 | #define LIB_MATH_H 3 | 4 | #include "LibAPI.h" 5 | 6 | namespace lib { 7 | namespace math { 8 | 9 | void RegisterLibMath(luna::State *state); 10 | 11 | } // namespace math 12 | } // namespace lib 13 | 14 | #endif // LIB_MATH_H 15 | -------------------------------------------------------------------------------- /luna/LibTable.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_TABLE_H 2 | #define LIB_TABLE_H 3 | 4 | #include "LibAPI.h" 5 | 6 | namespace lib { 7 | namespace table { 8 | 9 | void RegisterLibTable(luna::State *state); 10 | 11 | } // namespace table 12 | } // namespace lib 13 | 14 | #endif // LIB_TABLE_H 15 | -------------------------------------------------------------------------------- /luna/SemanticAnalysis.h: -------------------------------------------------------------------------------- 1 | #ifndef SEMANTIC_ANALYSIS_H 2 | #define SEMANTIC_ANALYSIS_H 3 | 4 | #include "SyntaxTree.h" 5 | 6 | namespace luna 7 | { 8 | class State; 9 | 10 | void SemanticAnalysis(SyntaxTree *root, State *state); 11 | } 12 | 13 | #endif // SEMANTIC_ANALYSIS_H 14 | -------------------------------------------------------------------------------- /luna/LibString.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_STRING_H 2 | #define LIB_STRING_H 3 | 4 | #include "LibAPI.h" 5 | 6 | namespace lib { 7 | namespace string { 8 | 9 | void RegisterLibString(luna::State *state); 10 | 11 | } // namespace string 12 | } // namespace lib 13 | 14 | #endif // LIB_STRING_H 15 | -------------------------------------------------------------------------------- /luna/Parser.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_H 2 | #define PARSER_H 3 | 4 | #include "SyntaxTree.h" 5 | #include 6 | 7 | namespace luna 8 | { 9 | class Lexer; 10 | class State; 11 | 12 | std::unique_ptr Parse(Lexer *lexer); 13 | } // namespace luna 14 | 15 | #endif // PARSER_H 16 | -------------------------------------------------------------------------------- /luna/CodeGenerate.h: -------------------------------------------------------------------------------- 1 | #ifndef CODE_GENERATE_H 2 | #define CODE_GENERATE_H 3 | 4 | #include "Visitor.h" 5 | #include 6 | 7 | namespace luna 8 | { 9 | class State; 10 | 11 | void CodeGenerate(SyntaxTree *root, State *state); 12 | } // namespace luna 13 | 14 | #endif // CODE_GENERATE_H 15 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | project(luna) 4 | 5 | set(CMAKE_CXX_FLAGS 6 | "-std=c++11 -Wall -Wextra -Wno-unused-parameter -O2" 7 | ) 8 | 9 | set(EXECUTABLE_OUTPUT_PATH "${PROJECT_BINARY_DIR}/bin") 10 | set(LIBRARY_OUTPUT_PATH "${PROJECT_BINARY_DIR}/lib") 11 | 12 | add_subdirectory(luna) 13 | add_subdirectory(unittests) 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | *.sdf 3 | *.suo 4 | *.swp 5 | *.opensdf 6 | *.DS_Store 7 | *.sublime-project 8 | *.sublime-workspace 9 | *.xcodeproj 10 | .ycm_extra_conf.py 11 | .ycm_extra_conf.pyc 12 | bin/ 13 | lib/ 14 | ipch/ 15 | Debug/ 16 | Release/ 17 | DerivedData/ 18 | project.xcworkspace/ 19 | xcuserdata/ 20 | CMakeCache.txt 21 | CMakeFiles/ 22 | CMakeScripts/ 23 | cmake_install.cmake 24 | Makefile 25 | -------------------------------------------------------------------------------- /luna/UserData.cpp: -------------------------------------------------------------------------------- 1 | #include "UserData.h" 2 | 3 | namespace luna 4 | { 5 | UserData::~UserData() 6 | { 7 | if (!destroyed_ && destroyer_) 8 | destroyer_(user_data_); 9 | } 10 | 11 | void UserData::Accept(GCObjectVisitor *v) 12 | { 13 | if (v->Visit(this)) 14 | { 15 | metatable_->Accept(v); 16 | } 17 | } 18 | } // namespace luna 19 | -------------------------------------------------------------------------------- /unittests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories("${PROJECT_SOURCE_DIR}") 2 | 3 | add_executable(unittest 4 | TestLex.cpp 5 | TestParser.cpp 6 | TestSemantic.cpp 7 | TestString.cpp 8 | TestTable.cpp 9 | UnitTest.cpp 10 | ) 11 | target_link_libraries(unittest 12 | luna 13 | ) 14 | 15 | add_executable(gctest 16 | GCTest.cpp 17 | ) 18 | target_link_libraries(gctest 19 | luna 20 | ) 21 | -------------------------------------------------------------------------------- /luna/Upvalue.h: -------------------------------------------------------------------------------- 1 | #ifndef UPVALUE_H 2 | #define UPVALUE_H 3 | 4 | #include "GC.h" 5 | #include "Value.h" 6 | 7 | namespace luna 8 | { 9 | class Upvalue : public GCObject 10 | { 11 | public: 12 | virtual void Accept(GCObjectVisitor *v); 13 | 14 | void SetValue(const Value &value) 15 | { value_ = value; } 16 | 17 | Value * GetValue() 18 | { return &value_; } 19 | 20 | private: 21 | Value value_; 22 | }; 23 | } // namespace luna 24 | 25 | #endif // UPVALUE_H 26 | -------------------------------------------------------------------------------- /examples/gctest.lua: -------------------------------------------------------------------------------- 1 | local chars = {} 2 | 3 | local set_chars = function(first, last) 4 | for c = first, last do 5 | chars[#chars + 1] = c 6 | end 7 | end 8 | 9 | set_chars(string.byte("a"), string.byte("z")) 10 | set_chars(string.byte("A"), string.byte("Z")) 11 | set_chars(string.byte("0"), string.byte("9")) 12 | 13 | local chars_len = #chars 14 | 15 | while true do 16 | local str = "" 17 | local len = math.random(3, 200) 18 | for i = 1, len do 19 | str = str .. string.char(chars[math.random(chars_len)]) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /luna/Guard.h: -------------------------------------------------------------------------------- 1 | #ifndef GUARD_H 2 | #define GUARD_H 3 | 4 | #include 5 | 6 | // Guard class, using for RAII operations. 7 | // e.g. 8 | // { 9 | // Guard g(constructor, destructor); 10 | // ... 11 | // } 12 | class Guard 13 | { 14 | public: 15 | Guard(const std::function &enter, 16 | const std::function &leave) 17 | : leave_(leave) 18 | { 19 | enter(); 20 | } 21 | 22 | ~Guard() 23 | { 24 | leave_(); 25 | } 26 | 27 | Guard(const Guard &) = delete; 28 | void operator = (const Guard &) = delete; 29 | 30 | private: 31 | std::function leave_; 32 | }; 33 | 34 | #endif // GUARD_H 35 | -------------------------------------------------------------------------------- /luna/Runtime.cpp: -------------------------------------------------------------------------------- 1 | #include "Runtime.h" 2 | 3 | namespace luna 4 | { 5 | Stack::Stack() 6 | : stack_(kBaseStackSize), 7 | top_(nullptr) 8 | { 9 | top_ = &stack_[0]; 10 | } 11 | 12 | void Stack::SetNewTop(Value *top) 13 | { 14 | Value *old = top_; 15 | top_ = top; 16 | 17 | // Clear values between new top to old 18 | for (; top <= old; ++top) 19 | top->SetNil(); 20 | } 21 | 22 | CallInfo::CallInfo() 23 | : register_(nullptr), 24 | func_(nullptr), 25 | instruction_(nullptr), 26 | end_(nullptr), 27 | expect_result_(0) 28 | { 29 | } 30 | } // namespace luna 31 | -------------------------------------------------------------------------------- /luna/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(luna 2 | CodeGenerate.cpp 3 | Function.cpp 4 | GC.cpp 5 | Lex.cpp 6 | LibAPI.cpp 7 | LibBase.cpp 8 | LibIO.cpp 9 | LibMath.cpp 10 | LibString.cpp 11 | LibTable.cpp 12 | ModuleManager.cpp 13 | Parser.cpp 14 | Runtime.cpp 15 | SemanticAnalysis.cpp 16 | State.cpp 17 | String.cpp 18 | StringPool.cpp 19 | SyntaxTree.cpp 20 | Table.cpp 21 | TextInStream.cpp 22 | Token.cpp 23 | Upvalue.cpp 24 | UserData.cpp 25 | Value.cpp 26 | VM.cpp 27 | ) 28 | 29 | add_executable(lunac 30 | Luna.cpp 31 | ) 32 | 33 | target_link_libraries(lunac 34 | luna 35 | ) 36 | 37 | set_target_properties(lunac 38 | PROPERTIES OUTPUT_NAME luna 39 | ) 40 | -------------------------------------------------------------------------------- /luna/TextInStream.cpp: -------------------------------------------------------------------------------- 1 | #include "TextInStream.h" 2 | 3 | namespace io { 4 | namespace text { 5 | InStream::InStream(const std::string &path) 6 | : stream_(nullptr) 7 | { 8 | #ifdef _MSC_VER 9 | fopen_s(&stream_, path.c_str(), "rb"); 10 | #else 11 | stream_ = fopen(path.c_str(), "rb"); 12 | #endif // _MSC_VER 13 | } 14 | 15 | InStream::~InStream() 16 | { 17 | if (stream_) 18 | fclose(stream_); 19 | } 20 | 21 | InStringStream::InStringStream(const std::string &str) 22 | : str_(str), 23 | pos_(0) 24 | { 25 | } 26 | 27 | void InStringStream::SetInputString(const std::string &input) 28 | { 29 | str_ = input; 30 | pos_ = 0; 31 | } 32 | } // namespace text 33 | } // namespace io 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 airtrack 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 | -------------------------------------------------------------------------------- /luna/StringPool.cpp: -------------------------------------------------------------------------------- 1 | #include "StringPool.h" 2 | #include 3 | 4 | namespace luna 5 | { 6 | StringPool::StringPool() 7 | { 8 | } 9 | 10 | String * StringPool::GetString(const std::string &str) 11 | { 12 | temp_.SetValue(str); 13 | return GetString(); 14 | } 15 | 16 | String * StringPool::GetString(const char *str, std::size_t len) 17 | { 18 | temp_.SetValue(str, len); 19 | return GetString(); 20 | } 21 | 22 | String * StringPool::GetString(const char *str) 23 | { 24 | temp_.SetValue(str); 25 | return GetString(); 26 | } 27 | 28 | void StringPool::AddString(String *str) 29 | { 30 | auto it = strings_.insert(str); 31 | assert(it.second); 32 | (void)it; 33 | } 34 | 35 | void StringPool::DeleteString(String *str) 36 | { 37 | strings_.erase(str); 38 | } 39 | 40 | String * StringPool::GetString() 41 | { 42 | auto it = strings_.find(&temp_); 43 | if (it == strings_.end()) 44 | return nullptr; 45 | else 46 | return *it; 47 | } 48 | } // namespace luna 49 | -------------------------------------------------------------------------------- /luna/Token.cpp: -------------------------------------------------------------------------------- 1 | #include "Token.h" 2 | #include "String.h" 3 | #include 4 | 5 | namespace luna 6 | { 7 | const char *token_str[] = { 8 | "and", "break", "do", "else", "elseif", "end", 9 | "false", "for", "function", "if", "in", 10 | "local", "nil", "not", "or", "repeat", 11 | "return", "then", "true", "until", "while", 12 | "", "", "", 13 | "==", "~=", "<=", ">=", "..", "...", "" 14 | }; 15 | 16 | std::string GetTokenStr(const TokenDetail &t) 17 | { 18 | std::string str; 19 | 20 | int token = t.token_; 21 | if (token == Token_Number) 22 | { 23 | std::ostringstream oss; 24 | oss << t.number_; 25 | str = oss.str(); 26 | } 27 | else if (token == Token_Id || token == Token_String) 28 | { 29 | str = t.str_->GetStdString(); 30 | } 31 | else if (token >= Token_And && token <= Token_EOF) 32 | { 33 | str = token_str[token - Token_And]; 34 | } 35 | else 36 | { 37 | str.push_back(token); 38 | } 39 | 40 | return str; 41 | } 42 | } // namespace luna 43 | -------------------------------------------------------------------------------- /luna/Runtime.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNTIME_H 2 | #define RUNTIME_H 3 | 4 | #include "Value.h" 5 | #include 6 | 7 | namespace luna 8 | { 9 | class Closure; 10 | struct Instruction; 11 | 12 | // Runtime stack, registers of each function is one part of stack. 13 | struct Stack 14 | { 15 | static const int kBaseStackSize = 10000; 16 | 17 | std::vector stack_; 18 | Value *top_; 19 | 20 | Stack(); 21 | Stack(const Stack&) = delete; 22 | void operator = (const Stack&) = delete; 23 | 24 | // Set new top pointer, and [new top, old top) will be set nil 25 | void SetNewTop(Value *top); 26 | }; 27 | 28 | // Function call stack info 29 | struct CallInfo 30 | { 31 | // register base pointer which points to Stack 32 | Value *register_; 33 | // current closure, pointer to stack Value 34 | Value *func_; 35 | // current Instruction 36 | const Instruction *instruction_; 37 | // Instruction end 38 | const Instruction *end_; 39 | // expect result of this function call 40 | int expect_result_; 41 | 42 | CallInfo(); 43 | }; 44 | } // namespace luna 45 | 46 | #endif // RUNTIME_H 47 | -------------------------------------------------------------------------------- /luna/ModuleManager.h: -------------------------------------------------------------------------------- 1 | #ifndef MODULE_MANAGER_H 2 | #define MODULE_MANAGER_H 3 | 4 | #include "Value.h" 5 | #include 6 | 7 | namespace luna 8 | { 9 | class State; 10 | class Lexer; 11 | 12 | // Load and manage all modules or load string 13 | class ModuleManager 14 | { 15 | public: 16 | ModuleManager(State *state, Table *modules); 17 | 18 | ModuleManager(const ModuleManager&) = delete; 19 | void operator = (const ModuleManager&) = delete; 20 | 21 | // Check module loaded or not 22 | bool IsLoaded(const std::string &module_name) const; 23 | 24 | // Get module closure when module loaded, 25 | // if the module is not loaded, return nil value 26 | Value GetModuleClosure(const std::string &module_name) const; 27 | 28 | // Load module, when loaded success, push the closure of the module 29 | // onto stack 30 | void LoadModule(const std::string &module_name); 31 | 32 | // Load string, when loaded success, push the closure of the string 33 | // onto stack 34 | void LoadString(const std::string &str, const std::string &name); 35 | 36 | private: 37 | // Load and push the closure onto stack 38 | void Load(Lexer &lexer); 39 | 40 | State *state_; 41 | Table *modules_; 42 | }; 43 | } // namespace luna 44 | 45 | #endif // MODULE_MANAGER_H 46 | -------------------------------------------------------------------------------- /luna/TextInStream.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXT_IN_STREAM_H 2 | #define TEXT_IN_STREAM_H 3 | 4 | #include 5 | #include 6 | 7 | namespace io { 8 | namespace text { 9 | 10 | class InStream 11 | { 12 | public: 13 | explicit InStream(const std::string &path); 14 | ~InStream(); 15 | 16 | InStream(const InStream&) = delete; 17 | void operator = (const InStream&) = delete; 18 | 19 | bool IsOpen() const 20 | { 21 | return stream_ != nullptr; 22 | } 23 | 24 | int GetChar() 25 | { 26 | return fgetc(stream_); 27 | } 28 | 29 | private: 30 | FILE *stream_; 31 | }; 32 | 33 | class InStringStream 34 | { 35 | public: 36 | explicit InStringStream(const std::string &str); 37 | 38 | InStringStream(const InStringStream&) = delete; 39 | void operator = (const InStringStream&) = delete; 40 | 41 | void SetInputString(const std::string &input); 42 | 43 | int GetChar() 44 | { 45 | if (pos_ < str_.size()) 46 | return static_cast(str_[pos_++]); 47 | else 48 | return EOF; 49 | } 50 | 51 | private: 52 | std::string str_; 53 | std::size_t pos_; 54 | }; 55 | 56 | } // namespace text 57 | } // namespace io 58 | 59 | #endif // TEXT_IN_STREAM_H 60 | -------------------------------------------------------------------------------- /luna/Token.h: -------------------------------------------------------------------------------- 1 | #ifndef TOKEN_H 2 | #define TOKEN_H 3 | 4 | #include 5 | 6 | namespace luna 7 | { 8 | class String; 9 | 10 | enum Token 11 | { 12 | Token_And = 256, Token_Break, Token_Do, Token_Else, Token_Elseif, Token_End, 13 | Token_False, Token_For, Token_Function, Token_If, Token_In, 14 | Token_Local, Token_Nil, Token_Not, Token_Or, Token_Repeat, 15 | Token_Return, Token_Then, Token_True, Token_Until, Token_While, 16 | Token_Id, Token_String, Token_Number, 17 | Token_Equal, Token_NotEqual, Token_LessEqual, Token_GreaterEqual, 18 | Token_Concat, Token_VarArg, Token_EOF, 19 | }; 20 | 21 | struct TokenDetail 22 | { 23 | union 24 | { 25 | double number_; // number for Token_Number 26 | String *str_; // string for Token_Id, Token_KeyWord and Token_String 27 | }; 28 | 29 | String *module_; // module name of this token belongs to 30 | int line_; // token line number in module 31 | int column_; // token column number at 'line_' 32 | int token_; // token value 33 | 34 | TokenDetail() : str_(nullptr), module_(nullptr), line_(0), column_(0), token_(Token_EOF) { } 35 | }; 36 | 37 | std::string GetTokenStr(const TokenDetail &t); 38 | } // namespace luna 39 | 40 | #endif // TOKEN_H 41 | -------------------------------------------------------------------------------- /luna/UserData.h: -------------------------------------------------------------------------------- 1 | #ifndef USER_DATA_H 2 | #define USER_DATA_H 3 | 4 | #include "GC.h" 5 | #include "Table.h" 6 | 7 | namespace luna 8 | { 9 | class UserData : public GCObject 10 | { 11 | public: 12 | typedef void (*Destroyer)(void *); 13 | 14 | UserData() = default; 15 | virtual ~UserData(); 16 | 17 | virtual void Accept(GCObjectVisitor *v) final; 18 | 19 | void Set(void *user_data, Table *metatable) 20 | { 21 | user_data_ = user_data; 22 | metatable_ = metatable; 23 | } 24 | 25 | void SetDestroyer(Destroyer destroyer) 26 | { 27 | destroyer_ = destroyer; 28 | } 29 | 30 | void MarkDestroyed() 31 | { 32 | destroyed_ = true; 33 | } 34 | 35 | void * GetData() const 36 | { 37 | return user_data_; 38 | } 39 | 40 | Table * GetMetatable() const 41 | { 42 | return metatable_; 43 | } 44 | 45 | private: 46 | // Point to user data 47 | void *user_data_ = nullptr; 48 | // Metatable of user data 49 | Table *metatable_ = nullptr; 50 | // User data destroyer, call it when user data destroy 51 | Destroyer destroyer_ = nullptr; 52 | // Whether user data destroyed 53 | bool destroyed_ = false; 54 | }; 55 | } // namespace luna 56 | 57 | #endif // USER_DATA_H 58 | -------------------------------------------------------------------------------- /luna/StringPool.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_POOL_H 2 | #define STRING_POOL_H 3 | 4 | #include "String.h" 5 | #include 6 | #include 7 | 8 | namespace luna 9 | { 10 | class StringPool 11 | { 12 | public: 13 | StringPool(); 14 | 15 | StringPool(const StringPool&) = delete; 16 | void operator = (const StringPool&) = delete; 17 | 18 | // Get string from pool when string is existed, 19 | // otherwise return nullptr 20 | String * GetString(const std::string &str); 21 | String * GetString(const char *str, std::size_t len); 22 | String * GetString(const char *str); 23 | 24 | // Add string to pool 25 | void AddString(String *str); 26 | 27 | // Delete string from pool 28 | void DeleteString(String *str); 29 | 30 | private: 31 | struct StringHash 32 | { 33 | std::size_t operator () (const String *s) const 34 | { 35 | return s->GetHash(); 36 | } 37 | }; 38 | 39 | struct StringEqual 40 | { 41 | bool operator () (const String *l, const String *r) const 42 | { 43 | return l == r || *l == *r; 44 | } 45 | }; 46 | 47 | String * GetString(); 48 | 49 | String temp_; 50 | std::unordered_set strings_; 51 | }; 52 | } // namespace luna 53 | 54 | #endif // STRING_POOL_H 55 | -------------------------------------------------------------------------------- /luna/VM.h: -------------------------------------------------------------------------------- 1 | #ifndef VM_H 2 | #define VM_H 3 | 4 | #include "Value.h" 5 | #include "OpCode.h" 6 | #include 7 | 8 | namespace luna 9 | { 10 | class State; 11 | 12 | class VM 13 | { 14 | public: 15 | explicit VM(State *state); 16 | 17 | void Execute(); 18 | 19 | private: 20 | void ExecuteFrame(); 21 | 22 | // Execute next frame if return true 23 | bool Call(Value *a, Instruction i); 24 | 25 | void GenerateClosure(Value *a, Instruction i); 26 | void CopyVarArg(Value *a, Instruction i); 27 | void Return(Value *a, Instruction i); 28 | 29 | void Concat(Value *dst, Value *op1, Value *op2); 30 | void ForInit(Value *var, Value *limit, Value *step); 31 | 32 | // Debug help functions 33 | std::pair 34 | GetOperandNameAndScope(const Value *a) const; 35 | 36 | std::pair GetCurrentInstructionPos() const; 37 | 38 | void CheckType(const Value *v, ValueT type, const char *op) const; 39 | 40 | void CheckArithType(const Value *v1, const Value *v2, 41 | const char *op) const; 42 | 43 | void CheckInequalityType(const Value *v1, const Value *v2, 44 | const char *op) const; 45 | 46 | void CheckTableType(const Value *t, const Value *k, 47 | const char *op, const char *desc) const; 48 | 49 | void ReportTypeError(const Value *v, const char *op) const; 50 | 51 | State *state_; 52 | }; 53 | } // namespace luna 54 | 55 | #endif // VM_H 56 | -------------------------------------------------------------------------------- /luna/Luna.cpp: -------------------------------------------------------------------------------- 1 | #include "State.h" 2 | #include "Exception.h" 3 | #include "LibBase.h" 4 | #include "LibIO.h" 5 | #include "LibMath.h" 6 | #include "LibString.h" 7 | #include "LibTable.h" 8 | #include 9 | 10 | void Repl(luna::State &state) 11 | { 12 | printf("Luna 2.0 Copyright (C) 2014\n"); 13 | 14 | for (;;) 15 | { 16 | try 17 | { 18 | printf("> "); 19 | 20 | char s[1024] = { 0 }; 21 | auto input = fgets(s, sizeof(s), stdin); 22 | if (!input) 23 | break; 24 | state.DoString(input, "stdin"); 25 | } 26 | catch (const luna::Exception &exp) 27 | { 28 | printf("%s\n", exp.What().c_str()); 29 | } 30 | } 31 | } 32 | 33 | void ExecuteFile(const char **argv, luna::State &state) 34 | { 35 | try 36 | { 37 | state.DoModule(argv[1]); 38 | } 39 | catch (const luna::OpenFileFail &exp) 40 | { 41 | printf("%s: can not open file %s\n", argv[0], exp.What().c_str()); 42 | } 43 | catch (const luna::Exception &exp) 44 | { 45 | printf("%s\n", exp.What().c_str()); 46 | } 47 | } 48 | 49 | int main(int argc, const char **argv) 50 | { 51 | luna::State state; 52 | 53 | lib::base::RegisterLibBase(&state); 54 | lib::io::RegisterLibIO(&state); 55 | lib::math::RegisterLibMath(&state); 56 | lib::string::RegisterLibString(&state); 57 | lib::table::RegisterLibTable(&state); 58 | 59 | if (argc < 2) 60 | { 61 | Repl(state); 62 | } 63 | else 64 | { 65 | ExecuteFile(argv, state); 66 | } 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /luna/String.cpp: -------------------------------------------------------------------------------- 1 | #include "String.h" 2 | 3 | namespace luna 4 | { 5 | String::String() 6 | : in_heap_(0), str_(nullptr), length_(0), hash_(0) 7 | { 8 | } 9 | 10 | String::String(const char *str) 11 | : String() 12 | { 13 | SetValue(str); 14 | } 15 | 16 | String::~String() 17 | { 18 | if (in_heap_) 19 | delete [] str_; 20 | } 21 | 22 | std::string String::GetStdString() const 23 | { 24 | if (in_heap_) 25 | return std::string(str_, length_); 26 | else 27 | return std::string(str_buffer_, length_); 28 | } 29 | 30 | void String::SetValue(const std::string &str) 31 | { 32 | SetValue(str.c_str(), str.size()); 33 | } 34 | 35 | void String::SetValue(const char *str) 36 | { 37 | SetValue(str, strlen(str)); 38 | } 39 | 40 | void String::SetValue(const char *str, std::size_t len) 41 | { 42 | if (in_heap_) 43 | delete [] str_; 44 | 45 | length_ = len; 46 | if (len < sizeof(str_buffer_)) 47 | { 48 | memcpy(str_buffer_, str, len); 49 | str_buffer_[len] = 0; 50 | in_heap_ = 0; 51 | Hash(str_buffer_); 52 | } 53 | else 54 | { 55 | str_ = new char[len + 1]; 56 | memcpy(str_, str, len); 57 | str_[len] = 0; 58 | in_heap_ = 1; 59 | Hash(str_); 60 | } 61 | } 62 | 63 | void String::Hash(const char *s) 64 | { 65 | hash_ = 5381; 66 | int c = 0; 67 | 68 | while ((c = *s++)) 69 | hash_ = ((hash_ << 5) + hash_) + c; 70 | } 71 | } // namespace luna 72 | -------------------------------------------------------------------------------- /luna/Value.cpp: -------------------------------------------------------------------------------- 1 | #include "Value.h" 2 | #include "Function.h" 3 | #include "Table.h" 4 | #include "String.h" 5 | #include "Upvalue.h" 6 | #include "UserData.h" 7 | 8 | namespace luna 9 | { 10 | void Value::Accept(GCObjectVisitor *v) const 11 | { 12 | switch (type_) 13 | { 14 | case ValueT_Nil: 15 | case ValueT_Bool: 16 | case ValueT_Number: 17 | case ValueT_CFunction: 18 | break; 19 | case ValueT_Obj: 20 | obj_->Accept(v); 21 | break; 22 | case ValueT_String: 23 | str_->Accept(v); 24 | break; 25 | case ValueT_Closure: 26 | closure_->Accept(v); 27 | break; 28 | case ValueT_Upvalue: 29 | upvalue_->Accept(v); 30 | break; 31 | case ValueT_Table: 32 | table_->Accept(v); 33 | break; 34 | case ValueT_UserData: 35 | user_data_->Accept(v); 36 | break; 37 | } 38 | } 39 | 40 | const char * Value::TypeName() const 41 | { 42 | return TypeName(type_); 43 | } 44 | 45 | const char * Value::TypeName(ValueT type) 46 | { 47 | switch (type) 48 | { 49 | case ValueT_Nil: return "nil"; 50 | case ValueT_Bool: return "bool"; 51 | case ValueT_Number: return "number"; 52 | case ValueT_CFunction: return "C-Function"; 53 | case ValueT_String: return "string"; 54 | case ValueT_Closure: return "function"; 55 | case ValueT_Upvalue: return "upvalue"; 56 | case ValueT_Table: return "table"; 57 | case ValueT_UserData: return "userdata"; 58 | default: return "unknown type"; 59 | } 60 | } 61 | } // namespace luna 62 | -------------------------------------------------------------------------------- /luna/ModuleManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ModuleManager.h" 2 | #include "Lex.h" 3 | #include "Parser.h" 4 | #include "State.h" 5 | #include "Table.h" 6 | #include "Exception.h" 7 | #include "SemanticAnalysis.h" 8 | #include "CodeGenerate.h" 9 | #include "TextInStream.h" 10 | #include 11 | 12 | namespace luna 13 | { 14 | ModuleManager::ModuleManager(State *state, Table *modules) 15 | : state_(state), modules_(modules) 16 | { 17 | } 18 | 19 | bool ModuleManager::IsLoaded(const std::string &module_name) const 20 | { 21 | auto value = GetModuleClosure(module_name); 22 | return !value.IsNil(); 23 | } 24 | 25 | Value ModuleManager::GetModuleClosure(const std::string &module_name) const 26 | { 27 | Value key(state_->GetString(module_name)); 28 | return modules_->GetValue(key); 29 | } 30 | 31 | void ModuleManager::LoadModule(const std::string &module_name) 32 | { 33 | io::text::InStream is(module_name); 34 | if (!is.IsOpen()) 35 | throw OpenFileFail(module_name); 36 | 37 | Lexer lexer(state_, state_->GetString(module_name), 38 | [&is] () { return is.GetChar(); }); 39 | Load(lexer); 40 | 41 | // Add to modules' table 42 | Value key(state_->GetString(module_name)); 43 | Value value = *(state_->stack_.top_ - 1); 44 | modules_->SetValue(key, value); 45 | } 46 | 47 | void ModuleManager::LoadString(const std::string &str, const std::string &name) 48 | { 49 | io::text::InStringStream is(str); 50 | Lexer lexer(state_, state_->GetString(name), 51 | [&is] () { return is.GetChar(); }); 52 | Load(lexer); 53 | } 54 | 55 | void ModuleManager::Load(Lexer &lexer) 56 | { 57 | // Parse to AST 58 | auto ast = Parse(&lexer); 59 | 60 | // Semantic analysis 61 | SemanticAnalysis(ast.get(), state_); 62 | 63 | // Generate code 64 | CodeGenerate(ast.get(), state_); 65 | } 66 | } // namespace luna 67 | -------------------------------------------------------------------------------- /luna/Lex.h: -------------------------------------------------------------------------------- 1 | #ifndef LEX_H 2 | #define LEX_H 3 | 4 | #include "Token.h" 5 | #include 6 | #include 7 | 8 | namespace luna 9 | { 10 | class String; 11 | class State; 12 | 13 | class Lexer 14 | { 15 | public: 16 | typedef std::function CharInStream; 17 | 18 | Lexer(State *state, String *module, CharInStream in); 19 | 20 | Lexer(const Lexer&) = delete; 21 | void operator = (const Lexer&) = delete; 22 | 23 | // Get next token, 'detail' store next token detail information, 24 | // return value is next token type. 25 | int GetToken(TokenDetail *detail); 26 | 27 | // Get current lex module name. 28 | String * GetLexModule() const 29 | { 30 | return module_; 31 | } 32 | 33 | private: 34 | int Next() 35 | { 36 | auto c = in_stream_(); 37 | if (c != EOF) ++column_; 38 | return c; 39 | } 40 | 41 | void LexNewLine(); 42 | void LexComment(); 43 | void LexMultiLineComment(); 44 | void LexSingleLineComment(); 45 | 46 | int LexNumber(TokenDetail *detail); 47 | int LexNumberX(TokenDetail *detail, bool integer_part, 48 | const std::function &is_number_char, 49 | const std::function &is_exponent); 50 | int LexNumberXFractional(TokenDetail *detail, 51 | bool integer_part, bool point, 52 | const std::function &is_number_char, 53 | const std::function &is_exponent); 54 | 55 | int LexXEqual(TokenDetail *detail, int equal_token); 56 | 57 | int LexMultiLineString(TokenDetail *detail); 58 | int LexSingleLineString(TokenDetail *detail); 59 | void LexStringChar(); 60 | 61 | int LexId(TokenDetail *detail); 62 | 63 | State *state_; 64 | String *module_; 65 | CharInStream in_stream_; 66 | 67 | int current_; 68 | int line_; 69 | int column_; 70 | 71 | std::string token_buffer_; 72 | }; 73 | } // namespace luna 74 | 75 | #endif // LEX_H 76 | -------------------------------------------------------------------------------- /luna/SyntaxTree.cpp: -------------------------------------------------------------------------------- 1 | #include "SyntaxTree.h" 2 | #include "Visitor.h" 3 | 4 | namespace luna 5 | { 6 | #define SYNTAX_TREE_ACCEPT_VISITOR_IMPL(class_name) \ 7 | void class_name::Accept(Visitor *v, void *data) \ 8 | { \ 9 | v->Visit(this, data); \ 10 | } 11 | 12 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(Chunk) 13 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(Block) 14 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(ReturnStatement) 15 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(BreakStatement) 16 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(DoStatement) 17 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(WhileStatement) 18 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(RepeatStatement) 19 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(IfStatement) 20 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(ElseIfStatement) 21 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(ElseStatement) 22 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(NumericForStatement) 23 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(GenericForStatement) 24 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(FunctionStatement) 25 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(FunctionName) 26 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(LocalFunctionStatement) 27 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(LocalNameListStatement) 28 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(AssignmentStatement) 29 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(VarList) 30 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(Terminator) 31 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(BinaryExpression) 32 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(UnaryExpression) 33 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(FunctionBody) 34 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(ParamList) 35 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(NameList) 36 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(TableDefine) 37 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(TableIndexField) 38 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(TableNameField) 39 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(TableArrayField) 40 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(IndexAccessor) 41 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(MemberAccessor) 42 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(NormalFuncCall) 43 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(MemberFuncCall) 44 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(FuncCallArgs) 45 | SYNTAX_TREE_ACCEPT_VISITOR_IMPL(ExpressionList) 46 | 47 | } // namespace luna 48 | -------------------------------------------------------------------------------- /unittests/TestString.cpp: -------------------------------------------------------------------------------- 1 | #include "UnitTest.h" 2 | #include "luna/String.h" 3 | #include "luna/StringPool.h" 4 | 5 | TEST_CASE(string1) 6 | { 7 | luna::String str1("abc"); 8 | luna::String str2("abc"); 9 | 10 | EXPECT_TRUE(str1 == str2); 11 | EXPECT_TRUE(str1 <= str2); 12 | EXPECT_TRUE(str1 >= str2); 13 | EXPECT_TRUE(str1.GetHash() == str2.GetHash()); 14 | EXPECT_TRUE(str1.GetLength() == str2.GetLength()); 15 | EXPECT_TRUE(str1.GetStdString() == str2.GetStdString()); 16 | 17 | str1.SetValue("abcdefghijklmn"); 18 | str2.SetValue("abcdefghijklmnopqrst"); 19 | EXPECT_TRUE(str1 != str2); 20 | EXPECT_TRUE(str1 < str2); 21 | EXPECT_TRUE(str2 > str1); 22 | EXPECT_TRUE(str1.GetLength() < str2.GetLength()); 23 | EXPECT_TRUE(str1.GetStdString() != str2.GetStdString()); 24 | 25 | str1.SetValue("abc"); 26 | str2.SetValue("def"); 27 | EXPECT_TRUE(str1 != str2); 28 | EXPECT_TRUE(str1 < str2); 29 | EXPECT_TRUE(str2 > str1); 30 | EXPECT_TRUE(str1.GetLength() == str2.GetLength()); 31 | EXPECT_TRUE(str1.GetStdString() != str2.GetStdString()); 32 | } 33 | 34 | TEST_CASE(string2) 35 | { 36 | luna::String str1("abc"); 37 | luna::String str2("def"); 38 | luna::String str3("abcdefghijklmn"); 39 | luna::String str4("abcdefghijklmnopqrst"); 40 | luna::StringPool pool; 41 | 42 | pool.AddString(&str1); 43 | pool.AddString(&str2); 44 | pool.AddString(&str3); 45 | pool.AddString(&str4); 46 | 47 | auto s1 = pool.GetString("abc"); 48 | auto s2 = pool.GetString("def"); 49 | auto s3 = pool.GetString("abcdefghijklmn"); 50 | auto s4 = pool.GetString("abcdefghijklmnopqrst"); 51 | EXPECT_TRUE(s1 == &str1); 52 | EXPECT_TRUE(s2 == &str2); 53 | EXPECT_TRUE(s3 == &str3); 54 | EXPECT_TRUE(s4 == &str4); 55 | 56 | auto s5 = pool.GetString("abcdef"); 57 | EXPECT_TRUE(!s5); 58 | 59 | pool.DeleteString(&str1); 60 | pool.DeleteString(&str2); 61 | pool.DeleteString(&str3); 62 | pool.DeleteString(&str4); 63 | 64 | s1 = pool.GetString("abc"); 65 | s2 = pool.GetString("def"); 66 | s3 = pool.GetString("abcdefghijklmn"); 67 | s4 = pool.GetString("abcdefghijklmnopqrst"); 68 | EXPECT_TRUE(!s1); 69 | EXPECT_TRUE(!s2); 70 | EXPECT_TRUE(!s3); 71 | EXPECT_TRUE(!s4); 72 | } 73 | -------------------------------------------------------------------------------- /luna/Visitor.h: -------------------------------------------------------------------------------- 1 | #ifndef VISITOR_H 2 | #define VISITOR_H 3 | 4 | #include "SyntaxTree.h" 5 | 6 | namespace luna 7 | { 8 | class Visitor 9 | { 10 | public: 11 | virtual ~Visitor() { } 12 | virtual void Visit(Chunk *, void *) = 0; 13 | virtual void Visit(Block *, void *) = 0; 14 | virtual void Visit(ReturnStatement *, void *) = 0; 15 | virtual void Visit(BreakStatement *, void *) = 0; 16 | virtual void Visit(DoStatement *, void *) = 0; 17 | virtual void Visit(WhileStatement *, void *) = 0; 18 | virtual void Visit(RepeatStatement *, void *) = 0; 19 | virtual void Visit(IfStatement *, void *) = 0; 20 | virtual void Visit(ElseIfStatement *, void *) = 0; 21 | virtual void Visit(ElseStatement *, void *) = 0; 22 | virtual void Visit(NumericForStatement *, void *) = 0; 23 | virtual void Visit(GenericForStatement *, void *) = 0; 24 | virtual void Visit(FunctionStatement *, void *) = 0; 25 | virtual void Visit(FunctionName *, void *) = 0; 26 | virtual void Visit(LocalFunctionStatement *, void *) = 0; 27 | virtual void Visit(LocalNameListStatement *, void *) = 0; 28 | virtual void Visit(AssignmentStatement *, void *) = 0; 29 | virtual void Visit(VarList *, void *) = 0; 30 | virtual void Visit(Terminator *, void *) = 0; 31 | virtual void Visit(BinaryExpression *, void *) = 0; 32 | virtual void Visit(UnaryExpression *, void *) = 0; 33 | virtual void Visit(FunctionBody *, void *) = 0; 34 | virtual void Visit(ParamList *, void *) = 0; 35 | virtual void Visit(NameList *, void *) = 0; 36 | virtual void Visit(TableDefine *, void *) = 0; 37 | virtual void Visit(TableIndexField *, void *) = 0; 38 | virtual void Visit(TableNameField *, void *) = 0; 39 | virtual void Visit(TableArrayField *, void *) = 0; 40 | virtual void Visit(IndexAccessor *, void *) = 0; 41 | virtual void Visit(MemberAccessor *, void *) = 0; 42 | virtual void Visit(NormalFuncCall *, void *) = 0; 43 | virtual void Visit(MemberFuncCall *, void *) = 0; 44 | virtual void Visit(FuncCallArgs *, void *) = 0; 45 | virtual void Visit(ExpressionList *, void *) = 0; 46 | }; 47 | } // namespace luna 48 | 49 | #endif // VISITOR_H 50 | -------------------------------------------------------------------------------- /unittests/UnitTest.cpp: -------------------------------------------------------------------------------- 1 | #include "UnitTest.h" 2 | #include 3 | 4 | class UnitTestManager 5 | { 6 | public: 7 | UnitTestManager(const UnitTestManager&) = delete; 8 | void operator = (const UnitTestManager&) = delete; 9 | 10 | static UnitTestManager& GetInstance() 11 | { 12 | static UnitTestManager instance; 13 | return instance; 14 | } 15 | 16 | void AddUnitTest(UnitTestBase *test) 17 | { 18 | all_.push_back(test); 19 | } 20 | 21 | int RunAllTestCase() 22 | { 23 | int pass = 0; 24 | int failed = 0; 25 | 26 | for (auto test : all_) 27 | { 28 | try 29 | { 30 | test->Run(); 31 | if (test->IsTestOK()) 32 | { 33 | ++pass; 34 | printf("\033[32m[%s] pass\033[0m\n", test->GetTestName().c_str()); 35 | } 36 | else 37 | { 38 | ++failed; 39 | printf("\033[31m[%s] failed:\n", test->GetTestName().c_str()); 40 | 41 | auto errors = test->GetErrors(); 42 | for (auto &error : errors) 43 | { 44 | printf("\t%s\n", error.c_str()); 45 | } 46 | 47 | printf("\033[0m"); 48 | } 49 | } 50 | catch (...) 51 | { 52 | ++failed; 53 | printf("\033[31m[%s] catch exception\033[0m\n", test->GetTestName().c_str()); 54 | } 55 | } 56 | 57 | printf("%d cases: %d passed, %d failed\n", pass + failed, pass, failed); 58 | return failed; 59 | } 60 | 61 | private: 62 | UnitTestManager() { } 63 | 64 | std::vector all_; 65 | }; 66 | 67 | UnitTestBase::UnitTestBase() 68 | { 69 | UnitTestManager::GetInstance().AddUnitTest(this); 70 | } 71 | 72 | std::string UnitTestBase::GetTestName() const 73 | { 74 | return test_name_; 75 | } 76 | 77 | std::vector UnitTestBase::GetErrors() const 78 | { 79 | return errors_; 80 | } 81 | 82 | bool UnitTestBase::IsTestOK() const 83 | { 84 | return errors_.empty(); 85 | } 86 | 87 | int main() 88 | { 89 | return UnitTestManager::GetInstance().RunAllTestCase(); 90 | } 91 | -------------------------------------------------------------------------------- /unittests/UnitTest.h: -------------------------------------------------------------------------------- 1 | #ifndef UNIT_TEST_H 2 | #define UNIT_TEST_H 3 | 4 | #include 5 | #include 6 | 7 | class UnitTestBase 8 | { 9 | public: 10 | UnitTestBase(); 11 | 12 | UnitTestBase(const UnitTestBase&) = delete; 13 | void operator = (const UnitTestBase&) = delete; 14 | 15 | std::string GetTestName() const; 16 | std::vector GetErrors() const; 17 | bool IsTestOK() const; 18 | virtual void Run() = 0; 19 | 20 | protected: 21 | void Error(const std::string& error) 22 | { 23 | errors_.push_back(error + " failed!"); 24 | } 25 | 26 | std::string test_name_; 27 | std::vector errors_; 28 | }; 29 | 30 | #define TEST_CASE(case_name) \ 31 | class UnitTest_##case_name : public UnitTestBase \ 32 | { \ 33 | public: \ 34 | UnitTest_##case_name(); \ 35 | virtual void Run(); \ 36 | } test_##case_name##obj; \ 37 | \ 38 | UnitTest_##case_name::UnitTest_##case_name() \ 39 | { \ 40 | test_name_ = #case_name; \ 41 | } \ 42 | \ 43 | void UnitTest_##case_name::Run() 44 | 45 | #define EXPECT_TRUE(expr) \ 46 | do \ 47 | { \ 48 | if (!(expr)) \ 49 | Error("'" #expr "'"); \ 50 | } while (0) 51 | 52 | #define EXPECT_EXCEPTION(exception, stmt) \ 53 | do \ 54 | { \ 55 | try \ 56 | { \ 57 | {stmt} \ 58 | Error("'" #stmt "' has no " #exception); \ 59 | } \ 60 | catch (const exception&) \ 61 | { \ 62 | } \ 63 | } while (0) 64 | 65 | #endif // UNIT_TEST_H 66 | -------------------------------------------------------------------------------- /luna/String.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | 4 | #include "GC.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace luna 10 | { 11 | class String : public GCObject 12 | { 13 | public: 14 | String(); 15 | explicit String(const char *str); 16 | ~String(); 17 | 18 | String(const String &) = delete; 19 | void operator = (const String &) = delete; 20 | 21 | virtual void Accept(GCObjectVisitor *v) 22 | { v->Visit(this); } 23 | 24 | std::size_t GetHash() const 25 | { return hash_; } 26 | 27 | std::size_t GetLength() const 28 | { return length_; } 29 | 30 | const char * GetCStr() const 31 | { return in_heap_ ? str_ : str_buffer_; } 32 | 33 | // Convert to std::string 34 | std::string GetStdString() const; 35 | 36 | // Change context of string 37 | void SetValue(const std::string &str); 38 | void SetValue(const char *str); 39 | void SetValue(const char *str, std::size_t len); 40 | 41 | friend bool operator == (const String &l, const String &r) 42 | { 43 | return l.hash_ == r.hash_ && 44 | l.length_ == r.length_ && 45 | (l.in_heap_ ? memcmp(l.str_, r.str_, l.length_) : 46 | memcmp(l.str_buffer_, r.str_buffer_, l.length_)) == 0; 47 | } 48 | 49 | friend bool operator != (const String &l, const String &r) 50 | { 51 | return !(l == r); 52 | } 53 | 54 | friend bool operator < (const String &l, const String &r) 55 | { 56 | auto *l_s = l.in_heap_ ? l.str_ : l.str_buffer_; 57 | auto *r_s = r.in_heap_ ? r.str_ : r.str_buffer_; 58 | auto len = std::min(l.length_, r.length_); 59 | auto cmp = memcmp(l_s, r_s, len); 60 | if (cmp == 0) 61 | return l.length_ < r.length_; 62 | else 63 | return cmp < 0; 64 | } 65 | 66 | friend bool operator >= (const String &l, const String &r) 67 | { 68 | return !(l < r); 69 | } 70 | 71 | friend bool operator > (const String &l, const String &r) 72 | { 73 | return r < l; 74 | } 75 | 76 | friend bool operator <= (const String &l, const String &r) 77 | { 78 | return !(l > r); 79 | } 80 | 81 | private: 82 | // Calculate hash of string 83 | void Hash(const char *s); 84 | 85 | // String in heap or not 86 | char in_heap_; 87 | union 88 | { 89 | // Buffer for short string 90 | char str_buffer_[11]; 91 | // Pointer to heap which stored long string 92 | char *str_; 93 | }; 94 | 95 | // Length of string 96 | unsigned int length_; 97 | // Hash value of string 98 | std::size_t hash_; 99 | }; 100 | } // namespace luna 101 | 102 | #endif // STRING_H 103 | -------------------------------------------------------------------------------- /luna/Table.h: -------------------------------------------------------------------------------- 1 | #ifndef TABLE_H 2 | #define TABLE_H 3 | 4 | #include "GC.h" 5 | #include "Value.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace luna 11 | { 12 | // Table has array part and hash table part. 13 | class Table : public GCObject 14 | { 15 | public: 16 | Table(); 17 | 18 | virtual void Accept(GCObjectVisitor *v); 19 | 20 | // Set array value by index, return true if success. 21 | // 'index' start from 1, if 'index' == ArraySize() + 1, 22 | // then append value to array. 23 | bool SetArrayValue(std::size_t index, const Value &value); 24 | 25 | // If 'index' == ArraySize() + 1, then append value to array, 26 | // otherwise shifting up all values which start from 'index', 27 | // and insert value to 'index' of array. 28 | // Return true when insert success. 29 | bool InsertArrayValue(std::size_t index, const Value &value); 30 | 31 | // Erase the value by 'index' in array if 'index' is legal, 32 | // shifting down all values which start from 'index' + 1. 33 | // Return true when erase success. 34 | bool EraseArrayValue(std::size_t index); 35 | 36 | // Add key-value into table. 37 | // If key is number and key fit with array, then insert into array, 38 | // otherwise insert into hash table. 39 | void SetValue(const Value &key, const Value &value); 40 | 41 | // Get Value of key from array first, 42 | // if key is number, then get the value from array when key number 43 | // is fit with array as index, otherwise try search in hash table. 44 | // Return value is 'nil' if 'key' is not existed. 45 | Value GetValue(const Value &key) const; 46 | 47 | // Get first key-value pair of table, return true if table is not empty. 48 | bool FirstKeyValue(Value &key, Value &value); 49 | 50 | // Get the next key-value pair by current 'key', return false if there 51 | // is no key-value pair any more. 52 | bool NextKeyValue(const Value &key, Value &next_key, Value &next_value); 53 | 54 | // Return the number of array part elements. 55 | std::size_t ArraySize() const; 56 | 57 | private: 58 | typedef std::vector Array; 59 | typedef std::unordered_map Hash; 60 | 61 | // Combine AppendToArray and MergeFromHashToArray 62 | void AppendAndMergeFromHashToArray(const Value &value); 63 | 64 | // Append value to array. 65 | void AppendToArray(const Value &value); 66 | 67 | // Try to move values from hash to array which keys start from 68 | // ArraySize() + 1 69 | void MergeFromHashToArray(); 70 | 71 | // Move hash table key-value pair to array which key is number and key 72 | // fit with array, return true if move success. 73 | bool MoveHashToArray(const Value &key); 74 | 75 | std::unique_ptr array_; // array part of table 76 | std::unique_ptr hash_; // hash table part of table 77 | }; 78 | } // namespace luna 79 | 80 | #endif // TABLE_H 81 | -------------------------------------------------------------------------------- /luna/State.h: -------------------------------------------------------------------------------- 1 | #ifndef STATE_H 2 | #define STATE_H 3 | 4 | #include "GC.h" 5 | #include "Runtime.h" 6 | #include "ModuleManager.h" 7 | #include "StringPool.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace luna 14 | { 15 | class VM; 16 | 17 | // Error type reported by called c function 18 | enum CFuntionErrorType 19 | { 20 | CFuntionErrorType_NoError, 21 | CFuntionErrorType_ArgCount, 22 | CFuntionErrorType_ArgType, 23 | }; 24 | 25 | // Error reported by called c function 26 | struct CFunctionError 27 | { 28 | CFuntionErrorType type_; 29 | union 30 | { 31 | int expect_arg_count_; 32 | struct 33 | { 34 | int arg_index_; 35 | ValueT expect_type_; 36 | }; 37 | }; 38 | 39 | CFunctionError() : type_(CFuntionErrorType_NoError) { } 40 | }; 41 | 42 | class State 43 | { 44 | friend class VM; 45 | friend class StackAPI; 46 | friend class Library; 47 | friend class ModuleManager; 48 | friend class CodeGenerateVisitor; 49 | public: 50 | State(); 51 | ~State(); 52 | 53 | State(const State&) = delete; 54 | void operator = (const State&) = delete; 55 | 56 | // Check module loaded or not 57 | bool IsModuleLoaded(const std::string &module_name) const; 58 | 59 | // Load module, if load success, then push a module closure on stack, 60 | // otherwise throw Exception 61 | void LoadModule(const std::string &module_name); 62 | 63 | // Load module and call the module function when the module 64 | // loaded success. 65 | void DoModule(const std::string &module_name); 66 | 67 | // Load string and call the string function when the string 68 | // loaded success. 69 | void DoString(const std::string &str, const std::string &name = ""); 70 | 71 | // Call an in stack function 72 | // If f is a closure, then create a stack frame and return true, 73 | // call VM::Execute() to execute the closure instructions. 74 | // Return false when f is a c function. 75 | bool CallFunction(Value *f, int arg_count, int expect_result); 76 | 77 | // New GCObjects 78 | String * GetString(const std::string &str); 79 | String * GetString(const char *str, std::size_t len); 80 | String * GetString(const char *str); 81 | Function * NewFunction(); 82 | Closure * NewClosure(); 83 | Upvalue * NewUpvalue(); 84 | Table * NewTable(); 85 | UserData * NewUserData(); 86 | 87 | // Get current CallInfo 88 | CallInfo * GetCurrentCall(); 89 | 90 | // Get the global table value 91 | Value * GetGlobal(); 92 | 93 | // Return metatable, create when metatable not existed 94 | Table * GetMetatable(const char *metatable_name); 95 | 96 | // Erase metatable 97 | void EraseMetatable(const char *metatable_name); 98 | 99 | // For call c function 100 | void ClearCFunctionError() 101 | { cfunc_error_.type_ = CFuntionErrorType_NoError; } 102 | 103 | // Error data for call c function 104 | CFunctionError * GetCFunctionErrorData() 105 | { return &cfunc_error_; } 106 | 107 | // Get the GC 108 | GC& GetGC() { return *gc_; } 109 | 110 | // Check and run GC 111 | void CheckRunGC() { gc_->CheckGC(); } 112 | 113 | private: 114 | // Full GC root 115 | void FullGCRoot(GCObjectVisitor *v); 116 | 117 | // For CallFunction 118 | void CallClosure(Value *f, int expect_result); 119 | void CallCFunction(Value *f, int expect_result); 120 | void CheckCFunctionError(); 121 | 122 | // Get the table which stores all metatables 123 | Table * GetMetatables(); 124 | 125 | // Manage all modules 126 | std::unique_ptr module_manager_; 127 | // All strings in the pool 128 | std::unique_ptr string_pool_; 129 | // The GC 130 | std::unique_ptr gc_; 131 | 132 | // Error of call c function 133 | CFunctionError cfunc_error_; 134 | 135 | // Stack data 136 | Stack stack_; 137 | // Stack frames 138 | std::list calls_; 139 | // Global table 140 | Value global_; 141 | }; 142 | } // namespace luna 143 | 144 | #endif // STATE_H 145 | -------------------------------------------------------------------------------- /unittests/TestTable.cpp: -------------------------------------------------------------------------------- 1 | #include "UnitTest.h" 2 | #include "luna/Table.h" 3 | #include "luna/String.h" 4 | 5 | TEST_CASE(table1) 6 | { 7 | luna::Table t; 8 | 9 | for (int i = 0; i < 3; ++i) 10 | { 11 | luna::Value value; 12 | value.num_ = i + 1; 13 | value.type_ = luna::ValueT_Number; 14 | EXPECT_TRUE(t.SetArrayValue(i + 1, value)); 15 | } 16 | 17 | luna::Value key; 18 | luna::Value value; 19 | EXPECT_TRUE(t.FirstKeyValue(key, value)); 20 | EXPECT_TRUE(key.type_ == luna::ValueT_Number); 21 | EXPECT_TRUE(key.num_ == static_cast(1)); 22 | EXPECT_TRUE(value.type_ == luna::ValueT_Number); 23 | EXPECT_TRUE(value.num_ == static_cast(1)); 24 | 25 | for (int i = 1; i < 3; ++i) 26 | { 27 | luna::Value next_key; 28 | luna::Value next_value; 29 | EXPECT_TRUE(t.NextKeyValue(key, next_key, next_value)); 30 | EXPECT_TRUE(next_key.type_ == luna::ValueT_Number); 31 | EXPECT_TRUE(next_key.num_ == static_cast(i + 1)); 32 | EXPECT_TRUE(next_value.type_ == luna::ValueT_Number); 33 | EXPECT_TRUE(next_value.num_ == static_cast(i + 1)); 34 | key = next_key; 35 | } 36 | 37 | EXPECT_TRUE(!t.NextKeyValue(key, key, value)); 38 | 39 | value = t.GetValue(key); 40 | EXPECT_TRUE(value.type_ == luna::ValueT_Number); 41 | EXPECT_TRUE(value.num_ == static_cast(3)); 42 | } 43 | 44 | TEST_CASE(table2) 45 | { 46 | luna::Table t; 47 | luna::String key_str("key"); 48 | luna::String value_str("value"); 49 | 50 | luna::Value key; 51 | luna::Value value; 52 | 53 | key.type_ = luna::ValueT_Obj; 54 | key.obj_ = &key_str; 55 | value.type_ = luna::ValueT_Obj; 56 | value.obj_ = &value_str; 57 | 58 | t.SetValue(key, value); 59 | value = t.GetValue(key); 60 | 61 | EXPECT_TRUE(value.type_ == luna::ValueT_Obj); 62 | EXPECT_TRUE(value.obj_ == &value_str); 63 | 64 | luna::Value key_not_existed; 65 | key_not_existed.type_ = luna::ValueT_Obj; 66 | key_not_existed.obj_ = &value_str; 67 | 68 | value = t.GetValue(key_not_existed); 69 | EXPECT_TRUE(value.type_ == luna::ValueT_Nil); 70 | 71 | EXPECT_TRUE(t.FirstKeyValue(key, value)); 72 | EXPECT_TRUE(key.obj_ == &key_str); 73 | EXPECT_TRUE(value.obj_ == &value_str); 74 | 75 | EXPECT_TRUE(!t.NextKeyValue(key, key, value)); 76 | } 77 | 78 | TEST_CASE(table3) 79 | { 80 | luna::Table t; 81 | luna::Value key; 82 | luna::Value value; 83 | 84 | key.type_ = luna::ValueT_Bool; 85 | key.bvalue_ = true; 86 | 87 | value.type_ = luna::ValueT_Bool; 88 | value.bvalue_ = false; 89 | 90 | t.SetValue(key, value); 91 | value = t.GetValue(key); 92 | EXPECT_TRUE(value.type_ == luna::ValueT_Bool); 93 | EXPECT_TRUE(value.bvalue_ == false); 94 | 95 | t.SetValue(value, key); 96 | key = t.GetValue(value); 97 | EXPECT_TRUE(key.type_ == luna::ValueT_Bool); 98 | EXPECT_TRUE(key.bvalue_ == true); 99 | 100 | luna::Value nil; 101 | value = t.GetValue(nil); 102 | EXPECT_TRUE(value.type_ == luna::ValueT_Nil); 103 | } 104 | 105 | TEST_CASE(table4) 106 | { 107 | luna::Table t; 108 | luna::Value key; 109 | luna::Value value; 110 | 111 | value.type_ = luna::ValueT_Number; 112 | 113 | for (int i = 0; i < 3; ++i) 114 | { 115 | value.num_ = i + 1; 116 | t.InsertArrayValue(i + 1, value); 117 | } 118 | 119 | value.num_ = 0; 120 | t.InsertArrayValue(1, value); 121 | 122 | EXPECT_TRUE(t.ArraySize() == 4); 123 | 124 | key.type_ = luna::ValueT_Number; 125 | for (int i = 0; i < 4; ++i) 126 | { 127 | key.num_ = i + 1; 128 | value = t.GetValue(key); 129 | EXPECT_TRUE(value.type_ == luna::ValueT_Number); 130 | EXPECT_TRUE(value.num_ == i); 131 | } 132 | } 133 | 134 | TEST_CASE(table5) 135 | { 136 | luna::Table t; 137 | luna::Value key; 138 | luna::Value value; 139 | 140 | value.type_ = luna::ValueT_Number; 141 | 142 | for (int i = 0; i < 4; ++i) 143 | { 144 | value.num_ = i + 1; 145 | t.InsertArrayValue(i + 1, value); 146 | } 147 | 148 | EXPECT_TRUE(t.EraseArrayValue(1)); 149 | EXPECT_TRUE(t.EraseArrayValue(1)); 150 | 151 | EXPECT_TRUE(t.ArraySize() == 2); 152 | 153 | key.type_ = luna::ValueT_Number; 154 | 155 | key.num_ = 1; 156 | value = t.GetValue(key); 157 | EXPECT_TRUE(value.type_ == luna::ValueT_Number); 158 | EXPECT_TRUE(value.num_ == 3); 159 | 160 | key.num_ = 2; 161 | value = t.GetValue(key); 162 | EXPECT_TRUE(value.type_ == luna::ValueT_Number); 163 | EXPECT_TRUE(value.num_ == 4); 164 | } 165 | -------------------------------------------------------------------------------- /luna/Exception.h: -------------------------------------------------------------------------------- 1 | #ifndef EXCEPTION_H 2 | #define EXCEPTION_H 3 | 4 | #include "Token.h" 5 | #include "Value.h" 6 | #include "String.h" 7 | #include 8 | #include 9 | #include 10 | 11 | namespace luna 12 | { 13 | // Base exception for luna, all exceptions throwed by luna 14 | // are derived from this class 15 | class Exception 16 | { 17 | public: 18 | const std::string& What() const { return what_; } 19 | 20 | protected: 21 | // Helper functions for format string of exception 22 | void SetWhat(std::ostringstream &) { } 23 | 24 | template 25 | void SetWhat(std::ostringstream &oss, Arg&& arg, Args&&... args) 26 | { 27 | oss << std::forward(arg); 28 | SetWhat(oss, std::forward(args)...); 29 | } 30 | 31 | template 32 | void SetWhat(Args&&... args) 33 | { 34 | std::ostringstream oss; 35 | SetWhat(oss, std::forward(args)...); 36 | what_ = oss.str(); 37 | } 38 | 39 | private: 40 | std::string what_; 41 | }; 42 | 43 | // Module file open failed, this exception will be throwed 44 | class OpenFileFail : public Exception 45 | { 46 | public: 47 | explicit OpenFileFail(const std::string &file) 48 | { 49 | SetWhat(file); 50 | } 51 | }; 52 | 53 | // For lexer report error of token 54 | class LexException : public Exception 55 | { 56 | public: 57 | template 58 | LexException(const char *module, int line, int column, Args&&... args) 59 | { 60 | SetWhat(module, ':', line, ':', column, ' ', 61 | std::forward(args)...); 62 | } 63 | }; 64 | 65 | // For parser report grammer error 66 | class ParseException : public Exception 67 | { 68 | public: 69 | ParseException(const char *str, const TokenDetail &t) 70 | { 71 | SetWhat(t.module_->GetCStr(), ':', t.line_, ':', t.column_, " '", 72 | GetTokenStr(t), "' ", str); 73 | } 74 | }; 75 | 76 | // For semantic analysor report semantic error 77 | class SemanticException : public Exception 78 | { 79 | public: 80 | SemanticException(const char *str, const TokenDetail &t) 81 | { 82 | SetWhat(t.module_->GetCStr(), ':', t.line_, ':', t.column_, " '", 83 | GetTokenStr(t), "' ", str); 84 | } 85 | }; 86 | 87 | // For code generator report error 88 | class CodeGenerateException : public Exception 89 | { 90 | public: 91 | template 92 | CodeGenerateException(const char *module, int line, Args&&... args) 93 | { 94 | SetWhat(module, ':', line, ' ', std::forward(args)...); 95 | } 96 | }; 97 | 98 | // Report error of call c function 99 | class CallCFuncException : public Exception 100 | { 101 | public: 102 | CallCFuncException() = default; 103 | 104 | template 105 | CallCFuncException(Args&&... args) 106 | { 107 | SetWhat(std::forward(args)...); 108 | } 109 | }; 110 | 111 | // For VM report runtime error 112 | class RuntimeException : public Exception 113 | { 114 | public: 115 | RuntimeException(const char *module, int line, const char *desc) 116 | { 117 | SetWhat(module, ':', line, ' ', desc); 118 | } 119 | 120 | RuntimeException(const char *module, int line, 121 | const Value *v, const char *v_name, 122 | const char *expect_type) 123 | { 124 | SetWhat(module, ':', line, ' ', v_name, " is a ", 125 | v->TypeName(), " value, expect a ", expect_type, " value"); 126 | } 127 | 128 | RuntimeException(const char *module, int line, 129 | const Value *v, const char *v_name, 130 | const char *v_scope, const char *op) 131 | { 132 | SetWhat(module, ':', line, " attempt to ", op, ' ', 133 | v_scope, " '", v_name, "' (a ", v->TypeName(), " value)"); 134 | } 135 | 136 | RuntimeException(const char *module, int line, 137 | const Value *v1, const Value *v2, 138 | const char *op) 139 | { 140 | SetWhat(module, ':', line, " attempt to ", op, ' ', 141 | v1->TypeName(), " with ", v2->TypeName()); 142 | } 143 | }; 144 | } // namespace luna 145 | 146 | #endif // EXCEPTION_H 147 | -------------------------------------------------------------------------------- /luna/Value.h: -------------------------------------------------------------------------------- 1 | #ifndef VALUE_H 2 | #define VALUE_H 3 | 4 | #include "GC.h" 5 | #include 6 | 7 | namespace luna 8 | { 9 | #define EXP_VALUE_COUNT_ANY -1 10 | 11 | class String; 12 | class Closure; 13 | class Upvalue; 14 | class Table; 15 | class UserData; 16 | class State; 17 | 18 | typedef int (*CFunctionType)(State *); 19 | 20 | enum ValueT 21 | { 22 | ValueT_Nil, 23 | ValueT_Bool, 24 | ValueT_Number, 25 | ValueT_Obj, 26 | ValueT_String, 27 | ValueT_Closure, 28 | ValueT_Upvalue, 29 | ValueT_Table, 30 | ValueT_UserData, 31 | ValueT_CFunction, 32 | }; 33 | 34 | // Value type of luna 35 | struct Value 36 | { 37 | union 38 | { 39 | GCObject *obj_; 40 | String *str_; 41 | Closure *closure_; 42 | Upvalue *upvalue_; 43 | Table *table_; 44 | UserData *user_data_; 45 | CFunctionType cfunc_; 46 | double num_; 47 | bool bvalue_; 48 | }; 49 | 50 | ValueT type_; 51 | 52 | Value() : obj_(nullptr), type_(ValueT_Nil) { } 53 | explicit Value(bool bvalue) : bvalue_(bvalue), type_(ValueT_Bool) { } 54 | explicit Value(double num) : num_(num), type_(ValueT_Number) { } 55 | explicit Value(String *str) : str_(str), type_(ValueT_String) { } 56 | explicit Value(Closure *closure) : closure_(closure), type_(ValueT_Closure) { } 57 | explicit Value(Upvalue *upvalue) : upvalue_(upvalue), type_(ValueT_Upvalue) { } 58 | explicit Value(Table *table) : table_(table), type_(ValueT_Table) { } 59 | explicit Value(UserData *user_data) : user_data_(user_data), type_(ValueT_UserData) { } 60 | explicit Value(CFunctionType cfunc) : cfunc_(cfunc), type_(ValueT_CFunction) { } 61 | 62 | void SetNil() 63 | { obj_ = nullptr; type_ = ValueT_Nil; } 64 | 65 | void SetBool(bool bvalue) 66 | { bvalue_ = bvalue; type_ = ValueT_Bool; } 67 | 68 | bool IsNil() const 69 | { return type_ == ValueT_Nil; } 70 | 71 | bool IsFalse() const 72 | { return type_ == ValueT_Nil || (type_ == ValueT_Bool && !bvalue_); } 73 | 74 | void Accept(GCObjectVisitor *v) const; 75 | const char * TypeName() const; 76 | 77 | static const char * TypeName(ValueT type); 78 | }; 79 | 80 | inline bool operator == (const Value &left, const Value &right) 81 | { 82 | if (left.type_ != right.type_) 83 | return false; 84 | 85 | switch (left.type_) 86 | { 87 | case ValueT_Nil: return true; 88 | case ValueT_Bool: return left.bvalue_ == right.bvalue_; 89 | case ValueT_Number: return left.num_ == right.num_; 90 | case ValueT_Obj: return left.obj_ == right.obj_; 91 | case ValueT_String: return left.str_ == right.str_; 92 | case ValueT_Closure: return left.closure_ == right.closure_; 93 | case ValueT_Upvalue: return left.upvalue_ == right.upvalue_; 94 | case ValueT_Table: return left.table_ == right.table_; 95 | case ValueT_UserData: return left.user_data_ == right.user_data_; 96 | case ValueT_CFunction: return left.cfunc_ == right.cfunc_; 97 | } 98 | 99 | return false; 100 | } 101 | 102 | inline bool operator != (const Value &left, const Value &right) 103 | { 104 | return !(left == right); 105 | } 106 | } // namespace luna 107 | 108 | namespace std 109 | { 110 | template<> 111 | struct hash : public unary_function 112 | { 113 | size_t operator () (const luna::Value &t) const 114 | { 115 | switch (t.type_) 116 | { 117 | case luna::ValueT_Nil: 118 | return hash()(0); 119 | case luna::ValueT_Bool: 120 | return hash()(t.bvalue_); 121 | case luna::ValueT_Number: 122 | return hash()(t.num_); 123 | case luna::ValueT_String: 124 | return hash()(t.str_); 125 | case luna::ValueT_Closure: 126 | return hash()(t.closure_); 127 | case luna::ValueT_Upvalue: 128 | return hash()(t.upvalue_); 129 | case luna::ValueT_Table: 130 | return hash()(t.table_); 131 | case luna::ValueT_UserData: 132 | return hash()(t.user_data_); 133 | case luna::ValueT_CFunction: 134 | return hash()(reinterpret_cast(t.cfunc_)); 135 | default: 136 | return hash()(t.obj_); 137 | } 138 | } 139 | }; 140 | } // namespace std 141 | 142 | #endif // VALUE_H 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Linux & OSX: ![Build with clang](https://travis-ci.org/airtrack/luna.svg) 2 | 3 | Luna 4 | ==== 5 | An interpreter of lua-like language written in C++ 11. 6 | 7 | Build 8 | ----- 9 | 10 | cmake -G "Unix Makefiles" 11 | 12 | or 13 | 14 | cmake -G Xcode 15 | 16 | API 17 | --- 18 | 19 | Global function|Description 20 | ---------------|----------- 21 | print(...)|Print values to stdout 22 | puts(string)|Print a *string* to stdout 23 | ipairs(table)|Returns a iterator to iterate array part of a *table* 24 | pairs(table)|Returns a iterator to iterate a *table*(array and hash) 25 | type(value)|Returns type of a *value* 26 | getline()|Returns a line string which gets from stdin 27 | require(path)|Load the *path* module 28 | 29 | IO table|Description 30 | --------|----------- 31 | io.open(path [, mode])|Returns a file of *path* by *mode* when open success, otherwise returns nil and error description, *mode* is same with c function *fopen*, default is "r". 32 | io.stdin()|Returns a file of stdin 33 | io.stdout()|Returns a file of stdout 34 | io.stderr()|Returns a file of stderr 35 | 36 | File table|Description 37 | ----------|----------- 38 | file:close()|Close file 39 | file:flush()|Flush write buffer 40 | file:read(...)|Read data from file, arguments could be number(read number bytes, returns as a string), "\*n"(read a number and returns the number), "\*a"(read whole file, returns as a string. Returns a empty string when on the end of file), "\*l"(read a line, returns as a string without '\\n'), "*L"(read a line, returns as a string with '\\n'). Returns nil when on end of file. 41 | file:seek([whence [, offset]])|Sets and gets the file position. *whence* could be "set", "cur", "end", *offset* is a number. If seek success, then returns the file position, otherwise returns nil and error description. Called with no argument, returns current position. 42 | file:setvbuf(mode [, size])|Set the buffering mode for the output file. *mode* could be "no"(no buffering), "full"(full buffering), "line"(line buffering), *size* is a number specifies the size of the buffer, in bytes. 43 | file:write(...)|Write the value of each argument to file, arguments could be string and number. If success, returns the file, otherwise returns nil and error description. 44 | 45 | Math table|Description 46 | ----------|----------- 47 | math.abs(x)|Same with c function *abs* 48 | math.acos(x)|Same with c function *acos* 49 | math.asin(x)|Same with c function *asin* 50 | math.atan(x)|Same with c function *atan* 51 | math.atan2(y, x)|Same with c function *atan2* 52 | math.ceil(x)|Same with c function ceil 53 | math.cos(x)|Same with c function *cos* 54 | math.cosh(x)|Same with c function *cosh* 55 | math.deg(x)|Returns the angle *x*(given in radians) in degrees. 56 | math.exp(x)|Same with c function *exp* 57 | math.floor(x)|Same with c function *floor* 58 | math.frexp(x)|Same with c function *frexp*, returns significand of the given number in range of [0.5, 1) and exponent. 59 | math.huge|The c macro HUGE_VAL 60 | math.ldexp(m, e)|Same with c function *ldexp* 61 | math.log(x [, base])|Same with c function *log*, the default for *base* is *e*. 62 | math.max(x, ...)|Returns the maximum value 63 | math.min(x, ...)|Returns the minimum value 64 | math.modf(x)|Returns the integral part of *x* and the fractional part of *x*. 65 | math.pi|The value of PI 66 | math.pow(x, y)|Same with c function *pow* 67 | math.rad(x)|Returns the angle(given in degrees) in radians. 68 | math.random([m [, n]])|Returns a uniform pseudo-random number. When called with no arguments, returns a real number in the range [0, 1). When called with number *m*, returns a integer in the range [1, m]. When called with number *m* and *n*, returns a integer in the range [m, n]. 69 | math.randomseed(x)|Set *x* as the seed for the pseudo-random generator. 70 | math.sin(x)|Same with c function *sin* 71 | math.sinh(x)|Same with c function *sinh* 72 | math.sqrt(x)|Same with c function *sqrt* 73 | math.tan(x)|Same with c function *tan* 74 | math.tanh(x)|Same with c function *tanh* 75 | 76 | String table|Description 77 | ------------|----------- 78 | string.byte(s [, i [, j]])|Returns the numerical codes of the characters s[*i*] to s[*j*]. The default value for *i* is 1. 79 | string.char(...)|Returns a string with length equal to the number of arguments, in which each character has the numerical code equal to its corresponding argument. 80 | string.len(s)|Returns the length of the string *s*. 81 | string.lower(s)|Returns a string in which each character is lowercase. 82 | string.upper(s)|Returns a string in which each character is uppercase. 83 | string.reverse(s)|Returns a reverse string of the string *s*. 84 | string.sub(s, i [, j])|Returns the substring of *s*[*i*..*j*]. 85 | 86 | Table table|Description 87 | -----------|----------- 88 | table.concat(t [, sep [, i [, j]]])|Concatenate *t*[*i*] .. *t*[*j*] to a string, insert *sep* between two elements, the default values for *i* is 1, *j* is #*t*, *sep* is an empty string. 89 | table.insert(t, [pos ,] value)|Insert the *value* at position *pos*, by default, the *value* append to the table *t*. Returns true when insert success. 90 | table.pack(...)|Pack all arguments into a table and returns it. 91 | table.remove(t [, pos])|Remove the element at position *pos*, by default, remove the last element. Returns true when remove success. 92 | table.unpack(t [, i [, j]])|Returns *t*[*i*] .. *t*[*j*] elements of table *t*, the default for *i* is 1, the default for *j* is #*t*. 93 | -------------------------------------------------------------------------------- /unittests/TestLex.cpp: -------------------------------------------------------------------------------- 1 | #include "UnitTest.h" 2 | #include "luna/Lex.h" 3 | #include "luna/State.h" 4 | #include "luna/String.h" 5 | #include "luna/TextInStream.h" 6 | #include "luna/Exception.h" 7 | #include 8 | 9 | namespace 10 | { 11 | class LexerWrapper 12 | { 13 | public: 14 | explicit LexerWrapper(const std::string &str) 15 | : iss_(str), 16 | state_(), 17 | name_("lex"), 18 | lexer_(&state_, &name_, std::bind(&io::text::InStringStream::GetChar, &iss_)) 19 | { 20 | } 21 | 22 | int GetToken() 23 | { 24 | luna::TokenDetail token; 25 | return lexer_.GetToken(&token); 26 | } 27 | 28 | private: 29 | io::text::InStringStream iss_; 30 | luna::State state_; 31 | luna::String name_; 32 | luna::Lexer lexer_; 33 | }; 34 | } // namespace 35 | 36 | TEST_CASE(lex1) 37 | { 38 | LexerWrapper lexer("\r\n\t\v\f "); 39 | EXPECT_TRUE(lexer.GetToken() == luna::Token_EOF); 40 | } 41 | 42 | TEST_CASE(lex2) 43 | { 44 | LexerWrapper lexer("-- this is comment\n" 45 | "--[[this is long\n comment]]" 46 | "--[[this is long\n comment too--]]" 47 | "--[[incomplete comment]"); 48 | 49 | EXPECT_EXCEPTION(luna::LexException, { 50 | lexer.GetToken(); 51 | }); 52 | } 53 | 54 | TEST_CASE(lex3) 55 | { 56 | LexerWrapper lexer("[==[long\nlong\nstring]==]'string'\"string\"" 57 | "[=[incomplete string]="); 58 | for (int i = 0; i < 3; ++i) 59 | EXPECT_TRUE(lexer.GetToken() == luna::Token_String); 60 | 61 | EXPECT_EXCEPTION(luna::LexException, { 62 | lexer.GetToken(); 63 | }); 64 | } 65 | 66 | TEST_CASE(lex4) 67 | { 68 | LexerWrapper lexer("3 3.0 3.1416 314.16e-2 0.31416E1 0xff 0x0.1E 0xA23p-4 0X1.921FB54442D18P+1" 69 | " 0x"); 70 | for (int i = 0; i < 9; ++i) 71 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Number); 72 | 73 | EXPECT_EXCEPTION(luna::LexException, { 74 | lexer.GetToken(); 75 | }); 76 | } 77 | 78 | TEST_CASE(lex5) 79 | { 80 | LexerWrapper lexer("+ - * / % ^ # == ~= <= >= < > = ( ) { } [ ] ; : , . .. ..."); 81 | EXPECT_TRUE(lexer.GetToken() == '+'); 82 | EXPECT_TRUE(lexer.GetToken() == '-'); 83 | EXPECT_TRUE(lexer.GetToken() == '*'); 84 | EXPECT_TRUE(lexer.GetToken() == '/'); 85 | EXPECT_TRUE(lexer.GetToken() == '%'); 86 | EXPECT_TRUE(lexer.GetToken() == '^'); 87 | EXPECT_TRUE(lexer.GetToken() == '#'); 88 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Equal); 89 | EXPECT_TRUE(lexer.GetToken() == luna::Token_NotEqual); 90 | EXPECT_TRUE(lexer.GetToken() == luna::Token_LessEqual); 91 | EXPECT_TRUE(lexer.GetToken() == luna::Token_GreaterEqual); 92 | EXPECT_TRUE(lexer.GetToken() == '<'); 93 | EXPECT_TRUE(lexer.GetToken() == '>'); 94 | EXPECT_TRUE(lexer.GetToken() == '='); 95 | EXPECT_TRUE(lexer.GetToken() == '('); 96 | EXPECT_TRUE(lexer.GetToken() == ')'); 97 | EXPECT_TRUE(lexer.GetToken() == '{'); 98 | EXPECT_TRUE(lexer.GetToken() == '}'); 99 | EXPECT_TRUE(lexer.GetToken() == '['); 100 | EXPECT_TRUE(lexer.GetToken() == ']'); 101 | EXPECT_TRUE(lexer.GetToken() == ';'); 102 | EXPECT_TRUE(lexer.GetToken() == ':'); 103 | EXPECT_TRUE(lexer.GetToken() == ','); 104 | EXPECT_TRUE(lexer.GetToken() == '.'); 105 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Concat); 106 | EXPECT_TRUE(lexer.GetToken() == luna::Token_VarArg); 107 | EXPECT_TRUE(lexer.GetToken() == luna::Token_EOF); 108 | } 109 | 110 | TEST_CASE(lex6) 111 | { 112 | LexerWrapper lexer("and do else elseif end false for function if in local " 113 | "nil not or repeat return then true until while"); 114 | EXPECT_TRUE(lexer.GetToken() == luna::Token_And); 115 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Do); 116 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Else); 117 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Elseif); 118 | EXPECT_TRUE(lexer.GetToken() == luna::Token_End); 119 | EXPECT_TRUE(lexer.GetToken() == luna::Token_False); 120 | EXPECT_TRUE(lexer.GetToken() == luna::Token_For); 121 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Function); 122 | EXPECT_TRUE(lexer.GetToken() == luna::Token_If); 123 | EXPECT_TRUE(lexer.GetToken() == luna::Token_In); 124 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Local); 125 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Nil); 126 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Not); 127 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Or); 128 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Repeat); 129 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Return); 130 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Then); 131 | EXPECT_TRUE(lexer.GetToken() == luna::Token_True); 132 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Until); 133 | EXPECT_TRUE(lexer.GetToken() == luna::Token_While); 134 | EXPECT_TRUE(lexer.GetToken() == luna::Token_EOF); 135 | } 136 | 137 | TEST_CASE(lex7) 138 | { 139 | LexerWrapper lexer("_ __ ___ _1 _a _a1 a1 a_ a_1 name"); 140 | for (int i = 0; i < 10; ++i) 141 | EXPECT_TRUE(lexer.GetToken() == luna::Token_Id); 142 | EXPECT_TRUE(lexer.GetToken() == luna::Token_EOF); 143 | } 144 | -------------------------------------------------------------------------------- /luna/Function.cpp: -------------------------------------------------------------------------------- 1 | #include "Function.h" 2 | #include 3 | 4 | namespace luna 5 | { 6 | Function::Function() 7 | : module_(nullptr), line_(0), args_(0), 8 | is_vararg_(false), superior_(nullptr) 9 | { 10 | } 11 | 12 | void Function::Accept(GCObjectVisitor *v) 13 | { 14 | if (v->Visit(this)) 15 | { 16 | if (module_) 17 | module_->Accept(v); 18 | if (superior_) 19 | superior_->Accept(v); 20 | 21 | for (const auto &value : const_values_) 22 | value.Accept(v); 23 | 24 | for (const auto &var : local_vars_) 25 | var.name_->Accept(v); 26 | 27 | for (auto child : child_funcs_) 28 | child->Accept(v); 29 | 30 | for (const auto &upvalue : upvalues_) 31 | upvalue.name_->Accept(v); 32 | } 33 | } 34 | 35 | const Instruction * Function::GetOpCodes() const 36 | { 37 | return opcodes_.empty() ? nullptr : &opcodes_[0]; 38 | } 39 | 40 | std::size_t Function::OpCodeSize() const 41 | { 42 | return opcodes_.size(); 43 | } 44 | 45 | Instruction * Function::GetMutableInstruction(std::size_t index) 46 | { 47 | return &opcodes_[index]; 48 | } 49 | 50 | std::size_t Function::AddInstruction(Instruction i, int line) 51 | { 52 | opcodes_.push_back(i); 53 | opcode_lines_.push_back(line); 54 | return opcodes_.size() - 1; 55 | } 56 | 57 | void Function::SetHasVararg() 58 | { 59 | is_vararg_ = true; 60 | } 61 | 62 | bool Function::HasVararg() const 63 | { 64 | return is_vararg_; 65 | } 66 | 67 | void Function::AddFixedArgCount(int count) 68 | { 69 | args_ += count; 70 | } 71 | 72 | int Function::FixedArgCount() const 73 | { 74 | return args_; 75 | } 76 | 77 | void Function::SetModuleName(String *module) 78 | { 79 | module_ = module; 80 | } 81 | 82 | void Function::SetLine(int line) 83 | { 84 | line_ = line; 85 | } 86 | 87 | void Function::SetSuperior(Function *superior) 88 | { 89 | superior_ = superior; 90 | } 91 | 92 | int Function::AddConstNumber(double num) 93 | { 94 | Value v; 95 | v.type_ = ValueT_Number; 96 | v.num_ = num; 97 | return AddConstValue(v); 98 | } 99 | 100 | int Function::AddConstString(String *str) 101 | { 102 | Value v; 103 | v.type_ = ValueT_String; 104 | v.str_ = str; 105 | return AddConstValue(v); 106 | } 107 | 108 | int Function::AddConstValue(const Value &v) 109 | { 110 | const_values_.push_back(v); 111 | return const_values_.size() - 1; 112 | } 113 | 114 | void Function::AddLocalVar(String *name, int register_id, 115 | int begin_pc, int end_pc) 116 | { 117 | local_vars_.push_back(LocalVarInfo(name, register_id, begin_pc, end_pc)); 118 | } 119 | 120 | int Function::AddChildFunction(Function *child) 121 | { 122 | child_funcs_.push_back(child); 123 | return child_funcs_.size() - 1; 124 | } 125 | 126 | int Function::AddUpvalue(String *name, bool parent_local, int register_index) 127 | { 128 | upvalues_.push_back(UpvalueInfo(name, parent_local, register_index)); 129 | return upvalues_.size() - 1; 130 | } 131 | 132 | int Function::SearchUpvalue(String *name) const 133 | { 134 | int size = upvalues_.size(); 135 | for (int i = 0; i < size; ++i) 136 | { 137 | if (upvalues_[i].name_ == name) 138 | return i; 139 | } 140 | 141 | return -1; 142 | } 143 | 144 | Function * Function::GetChildFunction(int index) const 145 | { 146 | return child_funcs_[index]; 147 | } 148 | 149 | String * Function::SearchLocalVar(int register_id, int pc) const 150 | { 151 | String *name = nullptr; 152 | int begin_pc = std::numeric_limits::min(); 153 | int end_pc = std::numeric_limits::max(); 154 | 155 | for (const auto &var : local_vars_) 156 | { 157 | if (var.register_id_ == register_id && 158 | var.begin_pc_ <= pc && pc < var.end_pc_) 159 | { 160 | if (var.begin_pc_ >= begin_pc && var.end_pc_ <= end_pc) 161 | { 162 | name = var.name_; 163 | begin_pc = var.begin_pc_; 164 | end_pc = var.end_pc_; 165 | } 166 | } 167 | } 168 | 169 | return name; 170 | } 171 | 172 | Value * Function::GetConstValue(int i) 173 | { 174 | return &const_values_[i]; 175 | } 176 | 177 | int Function::GetInstructionLine(int i) const 178 | { 179 | return opcode_lines_[i]; 180 | } 181 | 182 | Closure::Closure() 183 | : prototype_(nullptr) 184 | { 185 | } 186 | 187 | void Closure::Accept(GCObjectVisitor *v) 188 | { 189 | if (v->Visit(this)) 190 | { 191 | prototype_->Accept(v); 192 | 193 | for (const auto &upvalue : upvalues_) 194 | upvalue->Accept(v); 195 | } 196 | } 197 | 198 | Function * Closure::GetPrototype() const 199 | { 200 | return prototype_; 201 | } 202 | 203 | void Closure::SetPrototype(Function *prototype) 204 | { 205 | prototype_ = prototype; 206 | } 207 | } // namespace luna 208 | -------------------------------------------------------------------------------- /luna/LibTable.cpp: -------------------------------------------------------------------------------- 1 | #include "LibTable.h" 2 | #include "State.h" 3 | #include "Table.h" 4 | #include 5 | 6 | namespace lib { 7 | namespace table { 8 | 9 | // If the index value is number, then get the number value, 10 | // else report type error. 11 | template 12 | bool GetNumber(luna::StackAPI &api, int index, NumType &num) 13 | { 14 | if (!api.IsNumber(index)) 15 | { 16 | api.ArgTypeError(index, luna::ValueT_Number); 17 | return false; 18 | } 19 | 20 | num = static_cast(api.GetNumber(index)); 21 | return true; 22 | } 23 | 24 | int Concat(luna::State *state) 25 | { 26 | luna::StackAPI api(state); 27 | if (!api.CheckArgs(1, luna::ValueT_Table)) 28 | return 0; 29 | 30 | auto table = api.GetTable(0); 31 | const char *sep = ""; 32 | std::size_t i = 1; 33 | std::size_t j = table->ArraySize(); 34 | 35 | auto params = api.GetStackSize(); 36 | if (params > 1) 37 | { 38 | // If the value of index 1 is string, then get the string as sep 39 | if (api.IsString(1)) 40 | { 41 | sep = api.GetString(1)->GetCStr(); 42 | 43 | // Try to get the range of table 44 | if (params > 2 && !GetNumber(api, 2, i)) 45 | return 0; 46 | 47 | if (params > 3 && !GetNumber(api, 3, j)) 48 | return 0; 49 | } 50 | else 51 | { 52 | // Try to get the range of table 53 | if (!GetNumber(api, 1, i)) 54 | return 0; 55 | 56 | if (params > 2 && !GetNumber(api, 2, j)) 57 | return 0; 58 | } 59 | } 60 | 61 | luna::Value key; 62 | key.type_ = luna::ValueT_Number; 63 | 64 | // Concat values(number or string) of the range [i, j] 65 | std::ostringstream oss; 66 | for (; i <= j; ++i) 67 | { 68 | key.num_ = i; 69 | auto value = table->GetValue(key); 70 | 71 | if (value.type_ == luna::ValueT_Number) 72 | oss << value.num_; 73 | else if (value.type_ == luna::ValueT_String) 74 | oss << value.str_->GetCStr(); 75 | 76 | if (i != j) oss << sep; 77 | } 78 | 79 | api.PushString(oss.str()); 80 | return 1; 81 | } 82 | 83 | int Insert(luna::State *state) 84 | { 85 | luna::StackAPI api(state); 86 | if (!api.CheckArgs(2, luna::ValueT_Table)) 87 | return 0; 88 | 89 | auto params = api.GetStackSize(); 90 | auto table = api.GetTable(0); 91 | auto index = table->ArraySize() + 1; 92 | int value = 1; 93 | 94 | if (params > 2) 95 | { 96 | if (!GetNumber(api, 1, index)) 97 | return 0; 98 | 99 | value = 2; 100 | } 101 | 102 | api.PushBool(table->InsertArrayValue(index, *api.GetValue(value))); 103 | return 1; 104 | } 105 | 106 | int Pack(luna::State *state) 107 | { 108 | luna::StackAPI api(state); 109 | 110 | auto table = state->NewTable(); 111 | auto params = api.GetStackSize(); 112 | for (int i = 0; i < params; ++i) 113 | table->SetArrayValue(i + 1, *api.GetValue(i)); 114 | 115 | api.PushTable(table); 116 | return 1; 117 | } 118 | 119 | int Remove(luna::State *state) 120 | { 121 | luna::StackAPI api(state); 122 | if (!api.CheckArgs(1, luna::ValueT_Table, luna::ValueT_Number)) 123 | return 0; 124 | 125 | auto table = api.GetTable(0); 126 | auto index = table->ArraySize(); 127 | 128 | // There is no elements in array of table. 129 | if (index == 0) 130 | { 131 | api.PushBool(false); 132 | return 1; 133 | } 134 | 135 | auto params = api.GetStackSize(); 136 | if (params > 1) 137 | index = static_cast(api.GetNumber(1)); 138 | 139 | api.PushBool(table->EraseArrayValue(index)); 140 | return 1; 141 | } 142 | 143 | int Unpack(luna::State *state) 144 | { 145 | luna::StackAPI api(state); 146 | if (!api.CheckArgs(1, luna::ValueT_Table, 147 | luna::ValueT_Number, luna::ValueT_Number)) 148 | return 0; 149 | 150 | auto params = api.GetStackSize(); 151 | auto table = api.GetTable(0); 152 | 153 | int begin = 1; 154 | int end = table->ArraySize(); 155 | if (params > 1) 156 | begin = static_cast(api.GetNumber(1)); 157 | if (params > 2) 158 | end = static_cast(api.GetNumber(2)); 159 | 160 | int count = 0; 161 | luna::Value key; 162 | key.type_ = luna::ValueT_Number; 163 | for (int i = begin; i <= end; ++i) 164 | { 165 | key.num_ = i; 166 | api.PushValue(table->GetValue(key)); 167 | ++count; 168 | } 169 | 170 | return count; 171 | } 172 | 173 | void RegisterLibTable(luna::State *state) 174 | { 175 | luna::Library lib(state); 176 | luna::TableMemberReg table[] = { 177 | { "concat", Concat }, 178 | { "insert", Insert }, 179 | { "pack", Pack }, 180 | { "remove", Remove }, 181 | { "unpack", Unpack } 182 | }; 183 | 184 | lib.RegisterTableFunction("table", table); 185 | } 186 | 187 | } // namespace table 188 | } // namespace lib 189 | -------------------------------------------------------------------------------- /luna/LibString.cpp: -------------------------------------------------------------------------------- 1 | #include "LibString.h" 2 | #include "String.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace lib { 9 | namespace string { 10 | 11 | int Byte(luna::State *state) 12 | { 13 | luna::StackAPI api(state); 14 | if (!api.CheckArgs(1, luna::ValueT_String, 15 | luna::ValueT_Number, luna::ValueT_Number)) 16 | return 0; 17 | 18 | int params = api.GetStackSize(); 19 | 20 | int i = 1; 21 | if (params >= 2) 22 | i = static_cast(api.GetNumber(1)); 23 | 24 | int j = i; 25 | if (params >= 3) 26 | j = static_cast(api.GetNumber(2)); 27 | 28 | if (i <= 0) 29 | return 0; 30 | 31 | const luna::String *str = api.GetString(0); 32 | const char *s = str->GetCStr(); 33 | int len = str->GetLength(); 34 | int count = 0; 35 | 36 | for (int index = i - 1; index < j; ++index) 37 | { 38 | if (index >= 0 && index < len) 39 | { 40 | api.PushNumber(s[index]); 41 | ++count; 42 | } 43 | } 44 | return count; 45 | } 46 | 47 | int Char(luna::State *state) 48 | { 49 | luna::StackAPI api(state); 50 | int params = api.GetStackSize(); 51 | 52 | std::string str; 53 | for (int i = 0; i < params; ++i) 54 | { 55 | if (!api.IsNumber(i)) 56 | { 57 | api.ArgTypeError(i, luna::ValueT_Number); 58 | return 0; 59 | } 60 | else 61 | { 62 | str.push_back(static_cast(api.GetNumber(i))); 63 | } 64 | } 65 | 66 | api.PushString(str); 67 | return 1; 68 | } 69 | 70 | int Len(luna::State *state) 71 | { 72 | luna::StackAPI api(state); 73 | if (!api.CheckArgs(1, luna::ValueT_String)) 74 | return 0; 75 | 76 | api.PushNumber(api.GetString(0)->GetLength()); 77 | return 1; 78 | } 79 | 80 | int Lower(luna::State *state) 81 | { 82 | luna::StackAPI api(state); 83 | if (!api.CheckArgs(1, luna::ValueT_String)) 84 | return 0; 85 | 86 | auto str = api.GetString(0); 87 | auto size = str->GetLength(); 88 | auto c_str = reinterpret_cast(str->GetCStr()); 89 | 90 | std::string lower; 91 | lower.reserve(size); 92 | for (std::size_t i = 0; i < size; ++i) 93 | lower.push_back(std::tolower(c_str[i])); 94 | 95 | api.PushString(lower); 96 | return 1; 97 | } 98 | 99 | int Reverse(luna::State *state) 100 | { 101 | luna::StackAPI api(state); 102 | if (!api.CheckArgs(1, luna::ValueT_String)) 103 | return 0; 104 | 105 | auto str = api.GetString(0); 106 | auto size = str->GetLength(); 107 | auto c_str = str->GetCStr(); 108 | 109 | std::string reverse; 110 | reverse.reserve(size); 111 | for (; size > 0; --size) 112 | reverse.push_back(c_str[size - 1]); 113 | 114 | api.PushString(reverse); 115 | return 1; 116 | } 117 | 118 | int Sub(luna::State *state) 119 | { 120 | luna::StackAPI api(state); 121 | if (!api.CheckArgs(2, luna::ValueT_String, 122 | luna::ValueT_Number, luna::ValueT_Number)) 123 | return 0; 124 | 125 | auto str = api.GetString(0); 126 | int size = str->GetLength(); 127 | auto c_str = str->GetCStr(); 128 | auto start = static_cast(api.GetNumber(1)); 129 | auto end = size; 130 | 131 | auto params = api.GetStackSize(); 132 | if (params <= 2) 133 | { 134 | if (start == 0) 135 | { 136 | start = 1; 137 | } 138 | else if (start < 0) 139 | { 140 | start += size; 141 | start = start < 0 ? 1 : start + 1; 142 | } 143 | } 144 | else 145 | { 146 | start = start == 0 ? 1 : std::abs(start); 147 | end = std::abs(static_cast(api.GetNumber(2))); 148 | end = std::min(end, size); 149 | } 150 | 151 | std::string sub; 152 | for (int i = start; i <= end; ++i) 153 | sub.push_back(c_str[i - 1]); 154 | 155 | api.PushString(sub); 156 | return 1; 157 | } 158 | 159 | int Upper(luna::State *state) 160 | { 161 | luna::StackAPI api(state); 162 | if (!api.CheckArgs(1, luna::ValueT_String)) 163 | return 0; 164 | 165 | auto str = api.GetString(0); 166 | auto size = str->GetLength(); 167 | auto c_str = reinterpret_cast(str->GetCStr()); 168 | 169 | std::string upper; 170 | upper.reserve(size); 171 | for (std::size_t i = 0; i < size; ++i) 172 | upper.push_back(std::toupper(c_str[i])); 173 | 174 | api.PushString(upper); 175 | return 1; 176 | } 177 | 178 | void RegisterLibString(luna::State *state) 179 | { 180 | luna::Library lib(state); 181 | luna::TableMemberReg string[] = { 182 | { "byte", Byte }, 183 | { "char", Char }, 184 | { "len", Len }, 185 | { "lower", Lower }, 186 | { "reverse", Reverse }, 187 | { "sub", Sub }, 188 | { "upper", Upper } 189 | }; 190 | lib.RegisterTableFunction("string", string); 191 | } 192 | 193 | } // namespace string 194 | } // namespace lib 195 | -------------------------------------------------------------------------------- /luna/GC.h: -------------------------------------------------------------------------------- 1 | #ifndef GC_OBJECT_H 2 | #define GC_OBJECT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace luna 9 | { 10 | // Generations of GC object 11 | enum GCGeneration 12 | { 13 | GCGen0, // Youngest generation 14 | GCGen1, // Mesozoic generation 15 | GCGen2, // Oldest generation 16 | }; 17 | 18 | // GC flag for mark GC object 19 | enum GCFlag 20 | { 21 | GCFlag_White, 22 | GCFlag_Black, 23 | }; 24 | 25 | // GC object type allocated by GC 26 | enum GCObjectType 27 | { 28 | GCObjectType_Table = 1, 29 | GCObjectType_Function, 30 | GCObjectType_Closure, 31 | GCObjectType_Upvalue, 32 | GCObjectType_String, 33 | GCObjectType_UserData, 34 | }; 35 | 36 | class Table; 37 | class Function; 38 | class Closure; 39 | class Upvalue; 40 | class String; 41 | class UserData; 42 | 43 | // Visitor for visit all GC objects 44 | class GCObjectVisitor 45 | { 46 | public: 47 | // Need visit all GC object members when return true 48 | virtual bool Visit(Table *) = 0; 49 | virtual bool Visit(Function *) = 0; 50 | virtual bool Visit(Closure *) = 0; 51 | virtual bool Visit(Upvalue *) = 0; 52 | virtual bool Visit(String *) = 0; 53 | virtual bool Visit(UserData *) = 0; 54 | }; 55 | 56 | // Base class of GC objects, GC use this class to manipulate 57 | // all GC objects 58 | class GCObject 59 | { 60 | friend class GC; 61 | friend class MinorMarkVisitor; 62 | friend class BarrieredMarkVisitor; 63 | friend class MajorMarkVisitor; 64 | friend bool CheckBarrier(GCObject *); 65 | public: 66 | GCObject(); 67 | virtual ~GCObject() = 0; 68 | 69 | virtual void Accept(GCObjectVisitor *) = 0; 70 | 71 | private: 72 | // Pointing next GCObject in current generation 73 | GCObject *next_; 74 | // Generation flag 75 | unsigned int generation_ : 2; 76 | // GCFlag 77 | unsigned int gc_ : 2; 78 | // GCObjectType 79 | unsigned int gc_obj_type_ : 4; 80 | }; 81 | 82 | // GC object barrier checker 83 | inline bool CheckBarrier(GCObject *obj) { return obj->generation_ != GCGen0; } 84 | #define CHECK_BARRIER(gc, obj) \ 85 | do { if (luna::CheckBarrier(obj)) gc.SetBarrier(obj); } while (0) 86 | 87 | class GC 88 | { 89 | public: 90 | typedef std::function RootTravelType; 91 | typedef std::function GCObjectDeleter; 92 | 93 | struct DefaultDeleter 94 | { 95 | inline void operator () (GCObject *obj, unsigned int) const 96 | { delete obj; } 97 | }; 98 | 99 | explicit GC(const GCObjectDeleter &obj_deleter = DefaultDeleter(), bool log = false); 100 | ~GC(); 101 | 102 | GC(const GC&) = delete; 103 | void operator = (const GC&) = delete; 104 | 105 | void ResetDeleter(const GCObjectDeleter &obj_deleter = DefaultDeleter()) 106 | { obj_deleter_ = obj_deleter; } 107 | 108 | // Set minor and major root travel functions 109 | void SetRootTraveller(const RootTravelType &minor, const RootTravelType &major); 110 | 111 | // Alloc GC objects 112 | Table * NewTable(GCGeneration gen = GCGen0); 113 | Function * NewFunction(GCGeneration gen = GCGen2); 114 | Closure * NewClosure(GCGeneration gen = GCGen0); 115 | Upvalue * NewUpvalue(GCGeneration gen = GCGen0); 116 | String * NewString(GCGeneration gen = GCGen0); 117 | UserData * NewUserData(GCGeneration gen = GCGen0); 118 | 119 | // Set GC object barrier 120 | void SetBarrier(GCObject *obj); 121 | 122 | // Check run GC 123 | void CheckGC(); 124 | 125 | private: 126 | struct GenInfo 127 | { 128 | // Pointing to GC object list 129 | GCObject *gen_; 130 | // Count of GC objects 131 | unsigned int count_; 132 | // Current threshold count of GC objects 133 | unsigned int threshold_count_; 134 | 135 | GenInfo() : gen_(nullptr), count_(0), threshold_count_(0) { } 136 | }; 137 | 138 | void SetObjectGen(GCObject *obj, GCGeneration gen); 139 | 140 | // Run minor and major GC 141 | void MinorGC(); 142 | void MajorGC(); 143 | 144 | void MinorGCMark(); 145 | void MinorGCSweep(); 146 | 147 | void MajorGCMark(); 148 | void MajorGCSweep(); 149 | 150 | void SweepGeneration(GenInfo &gen); 151 | 152 | // Adjust GenInfo's threshold_count_ by alived_count 153 | void AdjustThreshold(unsigned int alived_count, GenInfo &gen, 154 | unsigned int min_threshold, 155 | unsigned int max_threshold); 156 | 157 | // Delete generation all objects 158 | void DestroyGeneration(GenInfo &gen); 159 | 160 | static const unsigned int kGen0InitThresholdCount = 512; 161 | static const unsigned int kGen1InitThresholdCount = 512; 162 | static const unsigned int kGen0MaxThresholdCount = 2048; 163 | static const unsigned int kGen1MaxThresholdCount = 102400; 164 | 165 | // Youngest generation 166 | GenInfo gen0_; 167 | // Mesozoic generation 168 | GenInfo gen1_; 169 | // Oldest generation 170 | GenInfo gen2_; 171 | 172 | // Minor root traveller 173 | RootTravelType minor_traveller_; 174 | // Major root traveller 175 | RootTravelType major_traveller_; 176 | 177 | // Barriered GC objects 178 | std::deque barriered_; 179 | 180 | // GC object Deleter 181 | GCObjectDeleter obj_deleter_; 182 | // Log file 183 | std::ofstream log_stream_; 184 | }; 185 | } // namespace luna 186 | 187 | #endif // GC_OBJECT_H 188 | -------------------------------------------------------------------------------- /luna/OpCode.h: -------------------------------------------------------------------------------- 1 | #ifndef OP_CODE_H 2 | #define OP_CODE_H 3 | 4 | namespace luna 5 | { 6 | enum OpType 7 | { 8 | OpType_LoadNil = 1, // A A: register 9 | OpType_FillNil, // AB A: start reg B: end reg [A,B) 10 | OpType_LoadBool, // AB A: register B: 1 true 0 false 11 | OpType_LoadInt, // A A: register Next instruction opcode is const unsigned int 12 | OpType_LoadConst, // ABx A: register Bx: const index 13 | OpType_Move, // AB A: dst register B: src register 14 | OpType_GetUpvalue, // AB A: register B: upvalue index 15 | OpType_SetUpvalue, // AB A: register B: upvalue index 16 | OpType_GetGlobal, // ABx A: value register Bx: const index 17 | OpType_SetGlobal, // ABx A: value register Bx: const index 18 | OpType_Closure, // ABx A: register Bx: proto index 19 | OpType_Call, // ABC A: register B: arg value count + 1 C: expected result count + 1 20 | OpType_VarArg, // AsBx A: register sBx: expected result count 21 | OpType_Ret, // AsBx A: return value start register sBx: return value count 22 | OpType_JmpFalse, // AsBx A: register sBx: diff of instruction index 23 | OpType_JmpTrue, // AsBx A: register sBx: diff of instruction index 24 | OpType_JmpNil, // AsBx A: register sBx: diff of instruction index 25 | OpType_Jmp, // sBx sBx: diff of instruction index 26 | OpType_Neg, // A A: operand register and dst register 27 | OpType_Not, // A A: operand register and dst register 28 | OpType_Len, // A A: operand register and dst register 29 | OpType_Add, // ABC A: dst register B: operand1 register C: operand2 register 30 | OpType_Sub, // ABC A: dst register B: operand1 register C: operand2 register 31 | OpType_Mul, // ABC A: dst register B: operand1 register C: operand2 register 32 | OpType_Div, // ABC A: dst register B: operand1 register C: operand2 register 33 | OpType_Pow, // ABC A: dst register B: operand1 register C: operand2 register 34 | OpType_Mod, // ABC A: dst register B: operand1 register C: operand2 register 35 | OpType_Concat, // ABC A: dst register B: operand1 register C: operand2 register 36 | OpType_Less, // ABC A: dst register B: operand1 register C: operand2 register 37 | OpType_Greater, // ABC A: dst register B: operand1 register C: operand2 register 38 | OpType_Equal, // ABC A: dst register B: operand1 register C: operand2 register 39 | OpType_UnEqual, // ABC A: dst register B: operand1 register C: operand2 register 40 | OpType_LessEqual, // ABC A: dst register B: operand1 register C: operand2 register 41 | OpType_GreaterEqual, // ABC A: dst register B: operand1 register C: operand2 register 42 | OpType_NewTable, // A A: register of table 43 | OpType_SetTable, // ABC A: register of table B: key register C: value register 44 | OpType_GetTable, // ABC A: register of table B: key register C: value register 45 | OpType_ForInit, // ABC A: var register B: limit register C: step register 46 | OpType_ForStep, // ABC ABC same with OpType_ForInit, next instruction sBx: diff of instruction index 47 | }; 48 | 49 | struct Instruction 50 | { 51 | unsigned int opcode_; 52 | 53 | Instruction() : opcode_(0) { } 54 | 55 | Instruction(OpType op, int a, int b, int c) : opcode_(op) 56 | { 57 | opcode_ = (opcode_ << 24) | ((a & 0xFF) << 16) | ((b & 0xFF) << 8) | (c & 0xFF); 58 | } 59 | 60 | Instruction(OpType op, int a, short b) : opcode_(op) 61 | { 62 | opcode_ = (opcode_ << 24) | ((a & 0xFF) << 16) | (static_cast(b) & 0xFFFF); 63 | } 64 | 65 | Instruction(OpType op, int a, unsigned short b) : opcode_(op) 66 | { 67 | opcode_ = (opcode_ << 24) | ((a & 0xFF) << 16) | (static_cast(b) & 0xFFFF); 68 | } 69 | 70 | void RefillsBx(short b) 71 | { 72 | opcode_ = (opcode_ & 0xFFFF0000) | (static_cast(b) & 0xFFFF); 73 | } 74 | 75 | static int GetOpCode(Instruction i) 76 | { 77 | return (i.opcode_ >> 24) & 0xFF; 78 | } 79 | 80 | static int GetParamA(Instruction i) 81 | { 82 | return (i.opcode_ >> 16) & 0xFF; 83 | } 84 | 85 | static int GetParamB(Instruction i) 86 | { 87 | return (i.opcode_ >> 8) & 0xFF; 88 | } 89 | 90 | static int GetParamC(Instruction i) 91 | { 92 | return i.opcode_ & 0xFF; 93 | } 94 | 95 | static int GetParamsBx(Instruction i) 96 | { 97 | return static_cast(i.opcode_ & 0xFFFF); 98 | } 99 | 100 | static int GetParamBx(Instruction i) 101 | { 102 | return static_cast(i.opcode_ & 0xFFFF); 103 | } 104 | 105 | static Instruction ABCCode(OpType op, int a, int b, int c) 106 | { 107 | return Instruction(op, a, b, c); 108 | } 109 | 110 | static Instruction ABCode(OpType op, int a, int b) 111 | { 112 | return Instruction(op, a, b, 0); 113 | } 114 | 115 | static Instruction ACode(OpType op, int a) 116 | { 117 | return Instruction(op, a, 0, 0); 118 | } 119 | 120 | static Instruction AsBxCode(OpType op, int a, int b) 121 | { 122 | return Instruction(op, a, static_cast(b)); 123 | } 124 | 125 | static Instruction ABxCode(OpType op, int a, int b) 126 | { 127 | return Instruction(op, a, static_cast(b)); 128 | } 129 | }; 130 | } // namespace luna 131 | 132 | #endif // OP_CODE_H 133 | -------------------------------------------------------------------------------- /luna/LibBase.cpp: -------------------------------------------------------------------------------- 1 | #include "LibBase.h" 2 | #include "Table.h" 3 | #include "Upvalue.h" 4 | #include "State.h" 5 | #include "String.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace lib { 12 | namespace base { 13 | 14 | int Print(luna::State *state) 15 | { 16 | luna::StackAPI api(state); 17 | int params = api.GetStackSize(); 18 | 19 | for (int i = 0; i < params; ++i) 20 | { 21 | luna::ValueT type = api.GetValueType(i); 22 | 23 | switch (type) { 24 | case luna::ValueT_Nil: 25 | printf("nil"); 26 | break; 27 | case luna::ValueT_Bool: 28 | printf("%s", api.GetBool(i) ? "true" : "false"); 29 | break; 30 | case luna::ValueT_Number: 31 | printf("%.14g", api.GetNumber(i)); 32 | break; 33 | case luna::ValueT_String: 34 | printf("%s", api.GetCString(i)); 35 | break; 36 | case luna::ValueT_Closure: 37 | printf("function:\t%p", api.GetClosure(i)); 38 | break; 39 | case luna::ValueT_Table: 40 | printf("table:\t%p", api.GetTable(i)); 41 | break; 42 | case luna::ValueT_UserData: 43 | printf("userdata:\t%p", api.GetUserData(i)); 44 | break; 45 | case luna::ValueT_CFunction: 46 | printf("function:\t%p", api.GetCFunction(i)); 47 | break; 48 | default: 49 | break; 50 | } 51 | 52 | if (i != params - 1) 53 | printf("\t"); 54 | } 55 | 56 | printf("\n"); 57 | return 0; 58 | } 59 | 60 | int Puts(luna::State *state) 61 | { 62 | luna::StackAPI api(state); 63 | if (!api.CheckArgs(1, luna::ValueT_String)) 64 | return 0; 65 | 66 | printf("%s", api.GetCString(0)); 67 | return 0; 68 | } 69 | 70 | int Type(luna::State *state) 71 | { 72 | luna::StackAPI api(state); 73 | if (!api.CheckArgs(1)) 74 | return 0; 75 | 76 | const luna::Value *v = api.GetValue(0); 77 | luna::ValueT type = v->type_ == luna::ValueT_Upvalue ? 78 | v->upvalue_->GetValue()->type_ : v->type_; 79 | 80 | switch (type) { 81 | case luna::ValueT_Nil: 82 | api.PushString("nil"); 83 | break; 84 | case luna::ValueT_Bool: 85 | api.PushString("boolean"); 86 | break; 87 | case luna::ValueT_Number: 88 | api.PushString("number"); 89 | break; 90 | case luna::ValueT_String: 91 | api.PushString("string"); 92 | break; 93 | case luna::ValueT_Table: 94 | api.PushString("table"); 95 | break; 96 | case luna::ValueT_UserData: 97 | api.PushString("userdata"); 98 | break; 99 | case luna::ValueT_Closure: 100 | case luna::ValueT_CFunction: 101 | api.PushString("function"); 102 | break; 103 | default: 104 | assert(0); 105 | return 0; 106 | } 107 | return 1; 108 | } 109 | 110 | int DoIPairs(luna::State *state) 111 | { 112 | luna::StackAPI api(state); 113 | if (!api.CheckArgs(2, luna::ValueT_Table, luna::ValueT_Number)) 114 | return 0; 115 | 116 | luna::Table *t = api.GetTable(0); 117 | double num = api.GetNumber(1) + 1; 118 | 119 | luna::Value k; 120 | k.type_ = luna::ValueT_Number; 121 | k.num_ = num; 122 | luna::Value v = t->GetValue(k); 123 | 124 | if (v.type_ == luna::ValueT_Nil) 125 | return 0; 126 | 127 | api.PushValue(k); 128 | api.PushValue(v); 129 | return 2; 130 | } 131 | 132 | int IPairs(luna::State *state) 133 | { 134 | luna::StackAPI api(state); 135 | if (!api.CheckArgs(1, luna::ValueT_Table)) 136 | return 0; 137 | 138 | luna::Table *t = api.GetTable(0); 139 | api.PushCFunction(DoIPairs); 140 | api.PushTable(t); 141 | api.PushNumber(0); 142 | return 3; 143 | } 144 | 145 | int DoPairs(luna::State *state) 146 | { 147 | luna::StackAPI api(state); 148 | if (!api.CheckArgs(2, luna::ValueT_Table)) 149 | return 0; 150 | 151 | luna::Table *t = api.GetTable(0); 152 | luna::Value *last_key = api.GetValue(1); 153 | 154 | luna::Value key; 155 | luna::Value value; 156 | if (last_key->type_ == luna::ValueT_Nil) 157 | t->FirstKeyValue(key, value); 158 | else 159 | t->NextKeyValue(*last_key, key, value); 160 | 161 | api.PushValue(key); 162 | api.PushValue(value); 163 | return 2; 164 | } 165 | 166 | int Pairs(luna::State *state) 167 | { 168 | luna::StackAPI api(state); 169 | if (!api.CheckArgs(1, luna::ValueT_Table)) 170 | return 0; 171 | 172 | luna::Table *t = api.GetTable(0); 173 | api.PushCFunction(DoPairs); 174 | api.PushTable(t); 175 | api.PushNil(); 176 | return 3; 177 | } 178 | 179 | int GetLine(luna::State *state) 180 | { 181 | luna::StackAPI api(state); 182 | std::string line; 183 | std::getline(std::cin, line); 184 | api.PushString(line.c_str()); 185 | return 1; 186 | } 187 | 188 | int Require(luna::State *state) 189 | { 190 | luna::StackAPI api(state); 191 | if (!api.CheckArgs(1, luna::ValueT_String)) 192 | return 0; 193 | 194 | auto module = api.GetString(0)->GetStdString(); 195 | if (!state->IsModuleLoaded(module)) 196 | state->DoModule(module); 197 | 198 | return 0; 199 | } 200 | 201 | void RegisterLibBase(luna::State *state) 202 | { 203 | luna::Library lib(state); 204 | lib.RegisterFunc("print", Print); 205 | lib.RegisterFunc("puts", Puts); 206 | lib.RegisterFunc("ipairs", IPairs); 207 | lib.RegisterFunc("pairs", Pairs); 208 | lib.RegisterFunc("type", Type); 209 | lib.RegisterFunc("getline", GetLine); 210 | lib.RegisterFunc("require", Require); 211 | } 212 | 213 | } // namespace base 214 | } // namespace lib 215 | -------------------------------------------------------------------------------- /luna/Function.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTION_H 2 | #define FUNCTION_H 3 | 4 | #include "GC.h" 5 | #include "Value.h" 6 | #include "OpCode.h" 7 | #include "String.h" 8 | #include "Upvalue.h" 9 | #include 10 | 11 | namespace luna 12 | { 13 | // Function prototype class, all runtime functions(closures) reference this 14 | // class object. This class contains some static information generated after 15 | // parse. 16 | class Function : public GCObject 17 | { 18 | public: 19 | struct UpvalueInfo 20 | { 21 | // Upvalue name 22 | String *name_; 23 | 24 | // This upvalue is parent function's local variable 25 | // when value is true, otherwise it is parent parent 26 | // (... and so on) function's local variable 27 | bool parent_local_; 28 | 29 | // Register id when this upvalue is parent function's 30 | // local variable, otherwise it is index of upvalue list 31 | // of parent function 32 | int register_index_; 33 | 34 | UpvalueInfo(String *name, bool parent_local, 35 | int register_index) 36 | : name_(name), parent_local_(parent_local), 37 | register_index_(register_index) { } 38 | }; 39 | 40 | Function(); 41 | 42 | virtual void Accept(GCObjectVisitor *v); 43 | 44 | // Get function instructions and size 45 | const Instruction * GetOpCodes() const; 46 | std::size_t OpCodeSize() const; 47 | // Get instruction pointer, then it can be changed 48 | Instruction * GetMutableInstruction(std::size_t index); 49 | 50 | // Add instruction, 'line' is line number of the instruction 'i', 51 | // return index of the new instruction 52 | std::size_t AddInstruction(Instruction i, int line); 53 | 54 | // Set and get this function has vararg 55 | void SetHasVararg(); 56 | bool HasVararg() const; 57 | 58 | // Add and get fixed arg count 59 | void AddFixedArgCount(int count); 60 | int FixedArgCount() const; 61 | 62 | // Set module and function define start line 63 | void SetModuleName(String *module); 64 | void SetLine(int line); 65 | 66 | // Set superior function 67 | void SetSuperior(Function *superior); 68 | 69 | // Add const number and return index of the const value 70 | int AddConstNumber(double num); 71 | 72 | // Add const String and return index of the const value 73 | int AddConstString(String *str); 74 | 75 | // Add const Value and return index of the const value 76 | int AddConstValue(const Value &v); 77 | 78 | // Add local variable debug info 79 | void AddLocalVar(String *name, int register_id, 80 | int begin_pc, int end_pc); 81 | 82 | // Add child function, return index of the function 83 | int AddChildFunction(Function *child); 84 | 85 | // Add a upvalue, return index of the upvalue 86 | int AddUpvalue(String *name, bool parent_local, int register_index); 87 | 88 | // Get upvalue index when the name upvalue existed, otherwise return -1 89 | int SearchUpvalue(String *name) const; 90 | 91 | // Get child function by index 92 | Function * GetChildFunction(int index) const; 93 | 94 | // Search local variable name from local variable list 95 | String * SearchLocalVar(int register_id, int pc) const; 96 | 97 | // Get const Value by index 98 | Value * GetConstValue(int i); 99 | 100 | // Get instruction line by instruction index 101 | int GetInstructionLine(int i) const; 102 | 103 | // Get upvalue count 104 | std::size_t GetUpvalueCount() const 105 | { return upvalues_.size(); } 106 | 107 | // Get upvalue info by index 108 | const UpvalueInfo * GetUpvalue(std::size_t index) const 109 | { return &upvalues_[index]; } 110 | 111 | // Get module name 112 | String * GetModule() const 113 | { return module_; } 114 | 115 | // Get line of function define 116 | int GetLine() const 117 | { return line_; } 118 | 119 | private: 120 | // For debug 121 | struct LocalVarInfo 122 | { 123 | // Local variable name 124 | String *name_; 125 | // Register id in function 126 | int register_id_; 127 | // Begin instruction index of variable 128 | int begin_pc_; 129 | // The past-the-end instruction index 130 | int end_pc_; 131 | 132 | LocalVarInfo(String *name, int register_id, 133 | int begin_pc, int end_pc) 134 | : name_(name), register_id_(register_id), 135 | begin_pc_(begin_pc), end_pc_(end_pc) { } 136 | }; 137 | 138 | // function instruction opcodes 139 | std::vector opcodes_; 140 | // opcodes' line number 141 | std::vector opcode_lines_; 142 | // const values in function 143 | std::vector const_values_; 144 | // debug info 145 | std::vector local_vars_; 146 | // child functions 147 | std::vector child_funcs_; 148 | // upvalues 149 | std::vector upvalues_; 150 | // function define module name 151 | String *module_; 152 | // function define line at module 153 | int line_; 154 | // count of args 155 | int args_; 156 | // has '...' param or not 157 | bool is_vararg_; 158 | // superior function pointer 159 | Function *superior_; 160 | }; 161 | 162 | // All runtime function are closures, this class object pointer to a 163 | // prototype Function object and its upvalues. 164 | class Closure : public GCObject 165 | { 166 | public: 167 | Closure(); 168 | 169 | virtual void Accept(GCObjectVisitor *v); 170 | 171 | // Get and set closure prototype Function 172 | Function * GetPrototype() const; 173 | void SetPrototype(Function *prototype); 174 | 175 | // Add upvalue 176 | void AddUpvalue(Upvalue *upvalue) 177 | { upvalues_.push_back(upvalue); } 178 | 179 | // Get upvalue by index 180 | Upvalue * GetUpvalue(std::size_t index) const 181 | { return upvalues_[index]; } 182 | 183 | private: 184 | // prototype Function 185 | Function *prototype_; 186 | // upvalues 187 | std::vector upvalues_; 188 | }; 189 | } // namespace luna 190 | 191 | #endif // FUNCTION_H 192 | -------------------------------------------------------------------------------- /luna/LibAPI.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_API_H 2 | #define LIB_API_H 3 | 4 | #include "Value.h" 5 | #include 6 | 7 | namespace luna 8 | { 9 | struct Stack; 10 | class State; 11 | class Table; 12 | class Closure; 13 | 14 | // This class is API for library to manipulate stack, 15 | // stack index value is: 16 | // -1 ~ -n is top to bottom, 17 | // 0 ~ n is bottom to top. 18 | class StackAPI 19 | { 20 | public: 21 | explicit StackAPI(State *state); 22 | 23 | StackAPI(const StackAPI&) = delete; 24 | void operator = (const StackAPI&) = delete; 25 | 26 | // Helper functions for check API arguments 27 | // e.g. 28 | // bool result = CheckArgs(2, ValueT_String, ValueT_Number); 29 | // 2: min arguments to call API 30 | // ValueT_String: type of the first argument 31 | // ValueT_Number: type of the second argument 32 | // all arguments are valid when result == true 33 | bool CheckArgs(int index, int params) 34 | { 35 | // No more expect argument to check, success 36 | return true; 37 | } 38 | 39 | template 40 | bool CheckArgs(int index, int params, ValueT type, ValueTypes... types) 41 | { 42 | // All arguments check success 43 | if (index == params) 44 | return true; 45 | 46 | // Check type of the index + 1 argument 47 | if (GetValueType(index) != type) 48 | { 49 | ArgTypeError(index, type); 50 | return false; 51 | } 52 | 53 | // Check remain arguments 54 | return CheckArgs(++index, params, types...); 55 | } 56 | 57 | template 58 | bool CheckArgs(int minCount, ValueTypes... types) 59 | { 60 | // Check count of arguments 61 | auto params = GetStackSize(); 62 | if (params < minCount) 63 | { 64 | ArgCountError(minCount); 65 | return false; 66 | } 67 | 68 | return CheckArgs(0, params, types...); 69 | } 70 | 71 | // Get count of value in this function stack 72 | int GetStackSize() const; 73 | 74 | // Get value type by index of stack 75 | ValueT GetValueType(int index); 76 | 77 | // Check value type by index of stack 78 | bool IsNumber(int index) { return GetValueType(index) == ValueT_Number; } 79 | bool IsString(int index) { return GetValueType(index) == ValueT_String; } 80 | bool IsBool(int index) { return GetValueType(index) == ValueT_Bool; } 81 | bool IsClosure(int index) { return GetValueType(index) == ValueT_Closure; } 82 | bool IsTable(int index) { return GetValueType(index) == ValueT_Table; } 83 | bool IsUserData(int index) { return GetValueType(index) == ValueT_UserData; } 84 | bool IsCFunction(int index) { return GetValueType(index) == ValueT_CFunction; } 85 | 86 | // Get value from stack by index 87 | double GetNumber(int index); 88 | const char * GetCString(int index); 89 | const String * GetString(int index); 90 | bool GetBool(int index); 91 | Closure * GetClosure(int index); 92 | Table * GetTable(int index); 93 | UserData * GetUserData(int index); 94 | CFunctionType GetCFunction(int index); 95 | Value * GetValue(int index); 96 | 97 | // Push value to stack 98 | void PushNil(); 99 | void PushNumber(double num); 100 | void PushString(const char *string); 101 | void PushString(const char *str, std::size_t len); 102 | void PushString(const std::string &str); 103 | void PushBool(bool value); 104 | void PushTable(Table *table); 105 | void PushUserData(UserData *user_data); 106 | void PushCFunction(CFunctionType function); 107 | void PushValue(const Value &value); 108 | 109 | // For report argument error 110 | void ArgCountError(int expect_count); 111 | void ArgTypeError(int arg_index, ValueT expect_type); 112 | 113 | private: 114 | // Push value to stack, and return the value 115 | Value * PushValue(); 116 | 117 | State *state_; 118 | Stack *stack_; 119 | }; 120 | 121 | // For register table member 122 | struct TableMemberReg 123 | { 124 | // Member name 125 | const char *name_; 126 | 127 | // Member value 128 | union 129 | { 130 | CFunctionType func_; 131 | double number_; 132 | const char *str_; 133 | }; 134 | 135 | // Member value type 136 | ValueT type_; 137 | 138 | TableMemberReg(const char *name, CFunctionType func) 139 | : name_(name), func_(func), type_(ValueT_CFunction) 140 | { 141 | } 142 | 143 | TableMemberReg(const char *name, double number) 144 | : name_(name), number_(number), type_(ValueT_Number) 145 | { 146 | } 147 | 148 | TableMemberReg(const char *name, const char *str) 149 | : name_(name), str_(str), type_(ValueT_String) 150 | { 151 | } 152 | }; 153 | 154 | // This class provide register C function/data to luna 155 | class Library 156 | { 157 | public: 158 | explicit Library(State *state); 159 | 160 | Library(const Library&) = delete; 161 | void operator = (const Library&) = delete; 162 | 163 | // Register global function 'func' as 'name' 164 | void RegisterFunc(const char *name, CFunctionType func); 165 | 166 | // Register a table of functions 167 | void RegisterTableFunction(const char *name, const TableMemberReg *table, 168 | std::size_t size); 169 | 170 | template 171 | void RegisterTableFunction(const char *name, const TableMemberReg (&table)[N]) 172 | { 173 | RegisterTableFunction(name, table, N); 174 | } 175 | 176 | // Register a metatable 177 | void RegisterMetatable(const char *name, const TableMemberReg *table, 178 | std::size_t size); 179 | 180 | template 181 | void RegisterMetatable(const char *name, const TableMemberReg (&table)[N]) 182 | { 183 | RegisterMetatable(name, table, N); 184 | } 185 | 186 | private: 187 | void RegisterToTable(Table *table, const TableMemberReg *table_reg, std::size_t size); 188 | void RegisterFunc(Table *table, const char *name, CFunctionType func); 189 | void RegisterNumber(Table *table, const char *name, double number); 190 | void RegisterString(Table *table, const char *name, const char *str); 191 | 192 | State *state_; 193 | Table *global_; 194 | }; 195 | } // namespace luna 196 | 197 | #endif // LIB_API_H 198 | -------------------------------------------------------------------------------- /examples/calculator.lua: -------------------------------------------------------------------------------- 1 | local function char(s) 2 | return string.byte(s) 3 | end 4 | 5 | local _0 = char('0') 6 | local _9 = char('9') 7 | local space = char(' ') 8 | local tab = char('\t') 9 | local add = char('+') 10 | local minus = char('-') 11 | local mul = char('*') 12 | local div = char('/') 13 | local left_par = char('(') 14 | local right_par = char(')') 15 | local eof = -1 16 | 17 | local token_type_eof = -1 18 | local token_type_error = 0 19 | local token_type_op = 1 20 | local token_type_num = 2 21 | local token_eof = { type = token_type_eof } 22 | local error_exp = "error expression" 23 | 24 | local function get_lexer(str) 25 | local i = 1 26 | local len = #str 27 | 28 | local function next_char() 29 | if i <= len then 30 | local c = string.byte(str, i) 31 | i = i + 1 32 | return c 33 | else 34 | return eof 35 | end 36 | end 37 | 38 | local function look_ahead() 39 | if i <= len then 40 | return string.byte(str, i) 41 | else 42 | return eof 43 | end 44 | end 45 | 46 | local function is_digit(c) 47 | return c >= _0 and c <= _9 48 | end 49 | 50 | local function is_space(c) 51 | return c == space or c == tab 52 | end 53 | 54 | local function is_operator(c) 55 | return c == add or c == minus or c == mul or 56 | c == div or c == left_par or c == right_par 57 | end 58 | 59 | local function lex_number(c) 60 | local num = c - _0 61 | while true do 62 | if is_digit(look_ahead()) then 63 | num = num * 10 + next_char() - _0 64 | else 65 | return num 66 | end 67 | end 68 | end 69 | 70 | return function() 71 | while true do 72 | local c = next_char() 73 | if c == eof then 74 | return token_eof 75 | end 76 | 77 | if not is_space(c) then 78 | if is_digit(c) then 79 | return { type = token_type_num, value = lex_number(c) } 80 | elseif is_operator(c) then 81 | return { type = token_type_op, value = c } 82 | else 83 | return { type = token_type_error, error = i .. ": unknown char" } 84 | end 85 | end 86 | end 87 | end 88 | end 89 | 90 | local function get_parser(lexer) 91 | local look_ahead = token_eof 92 | 93 | local function next() 94 | if look_ahead.type == token_type_eof then 95 | return lexer() 96 | else 97 | local token = look_ahead 98 | look_ahead = token_eof 99 | return token 100 | end 101 | end 102 | 103 | local function peek() 104 | if look_ahead.type == token_type_eof then 105 | look_ahead = lexer() 106 | end 107 | return look_ahead 108 | end 109 | 110 | local parser = { next = next, look_ahead = peek } 111 | 112 | function parser:factor() 113 | local neg = false 114 | if self.look_ahead().type == token_type_op and 115 | self.look_ahead().value == minus then 116 | self.next() 117 | neg = true 118 | end 119 | 120 | local token = self.next() 121 | if token.type == token_type_num then 122 | return neg and -token.value or token.value 123 | elseif token.type == token_type_op and token.value == left_par then 124 | local temp = self:parse_exp() 125 | if type(temp) == "string" then 126 | return temp 127 | end 128 | 129 | token = self.next() 130 | if not (token.type == token_type_op and token.value == right_par) then 131 | return error_exp 132 | end 133 | return neg and -temp or temp 134 | else 135 | return error_exp 136 | end 137 | end 138 | 139 | function parser:mul_div() 140 | local result = self:factor() 141 | if type(result) == "string" then 142 | return result 143 | end 144 | 145 | while true do 146 | if self.look_ahead().type == token_type_op then 147 | if self.look_ahead().value == mul then 148 | self.next() 149 | local temp = self:factor() 150 | if type(temp) == "string" then return temp end 151 | result = result * temp 152 | elseif self.look_ahead().value == div then 153 | self.next() 154 | local temp = self:factor() 155 | if type(temp) == "string" then return temp end 156 | result = result / temp 157 | else 158 | return result 159 | end 160 | else 161 | return result 162 | end 163 | end 164 | end 165 | 166 | function parser:add_minus() 167 | local result = self:mul_div() 168 | if type(result) == "string" then 169 | return result 170 | end 171 | 172 | while true do 173 | if self.look_ahead().type == token_type_eof then 174 | return result 175 | elseif self.look_ahead().type == token_type_op then 176 | if self.look_ahead().value == add then 177 | self.next() 178 | local temp = self:mul_div() 179 | if type(temp) == "string" then return temp end 180 | result = result + temp 181 | elseif self.look_ahead().value == minus then 182 | self.next() 183 | local temp = self:mul_div() 184 | if type(temp) == "string" then return temp end 185 | result = result - temp 186 | elseif self.look_ahead().value == right_par then 187 | return result 188 | else 189 | return error_exp 190 | end 191 | elseif self.look_ahead().type == token_type_error then 192 | return self.look_ahead().error 193 | else 194 | return error_exp 195 | end 196 | end 197 | end 198 | 199 | function parser:parse_exp() 200 | return self:add_minus() 201 | end 202 | 203 | function parser:parse() 204 | local result = self:parse_exp() 205 | if type(result) == "string" then 206 | return result 207 | end 208 | 209 | if self.look_ahead().type ~= token_type_eof then 210 | return error_exp 211 | end 212 | return result 213 | end 214 | 215 | return parser 216 | end 217 | 218 | while true do 219 | puts("calculator > ") 220 | local line = getline() 221 | local parser = get_parser(get_lexer(line)) 222 | if parser.look_ahead().type ~= token_type_eof then 223 | print(parser:parse()) 224 | end 225 | end 226 | -------------------------------------------------------------------------------- /luna/Table.cpp: -------------------------------------------------------------------------------- 1 | #include "Table.h" 2 | #include 3 | 4 | namespace 5 | { 6 | inline bool IsInt(double d) 7 | { 8 | return floor(d) == d; 9 | } 10 | } // namespace 11 | 12 | namespace luna 13 | { 14 | Table::Table() 15 | { 16 | } 17 | 18 | void Table::Accept(GCObjectVisitor *v) 19 | { 20 | if (v->Visit(this)) 21 | { 22 | // Visit all array members 23 | if (array_) 24 | { 25 | for (const auto &value : *array_) 26 | value.Accept(v); 27 | } 28 | 29 | // Visit all keys and values in hash table. 30 | if (hash_) 31 | { 32 | for (auto it = hash_->begin(); it != hash_->end(); ++it) 33 | { 34 | it->first.Accept(v); 35 | it->second.Accept(v); 36 | } 37 | } 38 | } 39 | } 40 | 41 | bool Table::SetArrayValue(std::size_t index, const Value &value) 42 | { 43 | if (index < 1) 44 | return false; 45 | 46 | std::size_t array_size = ArraySize(); 47 | if (index > array_size + 1) 48 | return false; 49 | 50 | if (index == array_size + 1) 51 | AppendAndMergeFromHashToArray(value); 52 | else 53 | (*array_)[index - 1] = value; 54 | 55 | return true; 56 | } 57 | 58 | bool Table::InsertArrayValue(std::size_t index, const Value &value) 59 | { 60 | if (index < 1) 61 | return false; 62 | 63 | std::size_t array_size = ArraySize(); 64 | if (index > array_size + 1) 65 | return false; 66 | 67 | if (index == array_size + 1) 68 | AppendAndMergeFromHashToArray(value); 69 | else 70 | { 71 | // Insert value 72 | auto it = array_->begin(); 73 | std::advance(it, index - 1); 74 | array_->insert(it, value); 75 | // Try to merge from hash to array 76 | MergeFromHashToArray(); 77 | } 78 | 79 | return true; 80 | } 81 | 82 | bool Table::EraseArrayValue(std::size_t index) 83 | { 84 | if (index < 1 || index > ArraySize()) 85 | return false; 86 | 87 | auto it = array_->begin(); 88 | std::advance(it, index - 1); 89 | array_->erase(it); 90 | return true; 91 | } 92 | 93 | void Table::SetValue(const Value &key, const Value &value) 94 | { 95 | // Try array part 96 | if (key.type_ == ValueT_Number && IsInt(key.num_)) 97 | { 98 | if (SetArrayValue(static_cast(key.num_), value)) 99 | return ; 100 | } 101 | 102 | // Hash part 103 | if (!hash_) 104 | { 105 | // If value is nil and hash part is not existed, then do nothing 106 | if (value.IsNil()) 107 | return ; 108 | hash_.reset(new Hash); 109 | } 110 | 111 | auto it = hash_->find(key); 112 | if (it != hash_->end()) 113 | { 114 | // If value is nil, then just erase the element 115 | if (value.IsNil()) 116 | hash_->erase(it); 117 | else 118 | it->second = value; 119 | } 120 | else 121 | { 122 | // If key is not existed and value is not nil, then insert it 123 | if (!value.IsNil()) 124 | hash_->insert(std::make_pair(key, value)); 125 | } 126 | } 127 | 128 | Value Table::GetValue(const Value &key) const 129 | { 130 | // Get from array first 131 | if (key.type_ == ValueT_Number && IsInt(key.num_)) 132 | { 133 | std::size_t index = static_cast(key.num_); 134 | if (index >= 1 && index <= ArraySize()) 135 | return (*array_)[index - 1]; 136 | } 137 | 138 | // Get from hash table 139 | if (hash_) 140 | { 141 | auto it = hash_->find(key); 142 | if (it != hash_->end()) 143 | return it->second; 144 | } 145 | 146 | // key not exist 147 | return Value(); 148 | } 149 | 150 | bool Table::FirstKeyValue(Value &key, Value &value) 151 | { 152 | // array part 153 | if (ArraySize() > 0) 154 | { 155 | key.num_ = 1; // first element index 156 | key.type_ = ValueT_Number; 157 | value = (*array_)[0]; 158 | return true; 159 | } 160 | 161 | // hash part 162 | if (hash_ && !hash_->empty()) 163 | { 164 | auto first = hash_->begin(); 165 | key = first->first; 166 | value = first->second; 167 | return true; 168 | } 169 | 170 | return false; 171 | } 172 | 173 | bool Table::NextKeyValue(const Value &key, Value &next_key, Value &next_value) 174 | { 175 | // array part 176 | if (key.type_ == ValueT_Number && IsInt(key.num_)) 177 | { 178 | std::size_t index = static_cast(key.num_) + 1; 179 | if (index >= 1 && index <= ArraySize()) 180 | { 181 | next_key.num_ = index; 182 | next_key.type_ = ValueT_Number; 183 | next_value = (*array_)[index - 1]; 184 | return true; 185 | } 186 | } 187 | 188 | // hash part 189 | if (hash_) 190 | { 191 | auto it = hash_->find(key); 192 | if (it == hash_->end() && !hash_->empty()) 193 | { 194 | it = hash_->begin(); 195 | next_key = it->first; 196 | next_value = it->second; 197 | return true; 198 | } 199 | 200 | if (it != hash_->end() && ++it != hash_->end()) 201 | { 202 | next_key = it->first; 203 | next_value = it->second; 204 | return true; 205 | } 206 | } 207 | 208 | return false; 209 | } 210 | 211 | std::size_t Table::ArraySize() const 212 | { 213 | return array_ ? array_->size() : 0; 214 | } 215 | 216 | void Table::AppendAndMergeFromHashToArray(const Value &value) 217 | { 218 | AppendToArray(value); 219 | MergeFromHashToArray(); 220 | } 221 | 222 | void Table::AppendToArray(const Value &value) 223 | { 224 | if (!array_) 225 | array_.reset(new Array); 226 | array_->push_back(value); 227 | } 228 | 229 | void Table::MergeFromHashToArray() 230 | { 231 | auto index = ArraySize(); 232 | Value key; 233 | key.num_ = ++index; 234 | key.type_ = ValueT_Number; 235 | 236 | while (MoveHashToArray(key)) 237 | key.num_ = ++index; 238 | } 239 | 240 | bool Table::MoveHashToArray(const Value &key) 241 | { 242 | if (!hash_) 243 | return false; 244 | 245 | auto it = hash_->find(key); 246 | if (it == hash_->end()) 247 | return false; 248 | 249 | AppendToArray(it->second); 250 | hash_->erase(it); 251 | return true; 252 | } 253 | } // namespace luna 254 | -------------------------------------------------------------------------------- /luna/LibAPI.cpp: -------------------------------------------------------------------------------- 1 | #include "LibAPI.h" 2 | #include "State.h" 3 | #include "Runtime.h" 4 | #include "Table.h" 5 | #include 6 | 7 | namespace luna 8 | { 9 | StackAPI::StackAPI(State *state) 10 | : state_(state), 11 | stack_(&state->stack_) 12 | { 13 | } 14 | 15 | int StackAPI::GetStackSize() const 16 | { 17 | return stack_->top_ - state_->calls_.back().register_; 18 | } 19 | 20 | ValueT StackAPI::GetValueType(int index) 21 | { 22 | Value *v = GetValue(index); 23 | if (v) 24 | return v->type_; 25 | else 26 | return ValueT_Nil; 27 | } 28 | 29 | double StackAPI::GetNumber(int index) 30 | { 31 | Value *v = GetValue(index); 32 | if (v) 33 | return v->num_; 34 | else 35 | return 0.0; 36 | } 37 | 38 | const char * StackAPI::GetCString(int index) 39 | { 40 | Value *v = GetValue(index); 41 | if (v) 42 | return v->str_->GetCStr(); 43 | else 44 | return ""; 45 | } 46 | 47 | const String * StackAPI::GetString(int index) 48 | { 49 | Value *v = GetValue(index); 50 | if (v) 51 | return v->str_; 52 | else 53 | return nullptr; 54 | } 55 | 56 | bool StackAPI::GetBool(int index) 57 | { 58 | Value *v = GetValue(index); 59 | if (v) 60 | return v->bvalue_; 61 | else 62 | return false; 63 | } 64 | 65 | Closure * StackAPI::GetClosure(int index) 66 | { 67 | Value *v = GetValue(index); 68 | if (v) 69 | return v->closure_; 70 | else 71 | return nullptr; 72 | } 73 | 74 | Table * StackAPI::GetTable(int index) 75 | { 76 | Value *v = GetValue(index); 77 | if (v) 78 | return v->table_; 79 | else 80 | return nullptr; 81 | } 82 | 83 | UserData * StackAPI::GetUserData(int index) 84 | { 85 | Value *v = GetValue(index); 86 | if (v) 87 | return v->user_data_; 88 | else 89 | return nullptr; 90 | } 91 | 92 | CFunctionType StackAPI::GetCFunction(int index) 93 | { 94 | Value *v = GetValue(index); 95 | if (v) 96 | return v->cfunc_; 97 | else 98 | return nullptr; 99 | } 100 | 101 | Value * StackAPI::GetValue(int index) 102 | { 103 | assert(!state_->calls_.empty()); 104 | Value *v = nullptr; 105 | if (index < 0) 106 | v = stack_->top_ + index; 107 | else 108 | v = state_->calls_.back().register_ + index; 109 | 110 | if (v >= stack_->top_ || v < state_->calls_.back().register_) 111 | return nullptr; 112 | else 113 | return v; 114 | } 115 | 116 | void StackAPI::PushNil() 117 | { 118 | PushValue()->type_ = ValueT_Nil; 119 | } 120 | 121 | void StackAPI::PushNumber(double num) 122 | { 123 | Value *v = PushValue(); 124 | v->type_ = ValueT_Number; 125 | v->num_ = num; 126 | } 127 | 128 | void StackAPI::PushString(const char *string) 129 | { 130 | Value *v = PushValue(); 131 | v->type_ = ValueT_String; 132 | v->str_ = state_->GetString(string); 133 | } 134 | 135 | void StackAPI::PushString(const char *str, std::size_t len) 136 | { 137 | Value *v = PushValue(); 138 | v->type_ = ValueT_String; 139 | v->str_ = state_->GetString(str, len); 140 | } 141 | 142 | void StackAPI::PushString(const std::string &str) 143 | { 144 | Value *v = PushValue(); 145 | v->type_ = ValueT_String; 146 | v->str_ = state_->GetString(str); 147 | } 148 | 149 | void StackAPI::PushBool(bool value) 150 | { 151 | Value *v = PushValue(); 152 | v->type_ = ValueT_Bool; 153 | v->bvalue_ = value; 154 | } 155 | 156 | void StackAPI::PushTable(Table *table) 157 | { 158 | Value *v = PushValue(); 159 | v->type_ = ValueT_Table; 160 | v->table_ = table; 161 | } 162 | 163 | void StackAPI::PushUserData(UserData *user_data) 164 | { 165 | Value *v = PushValue(); 166 | v->type_ = ValueT_UserData; 167 | v->user_data_ = user_data; 168 | } 169 | 170 | void StackAPI::PushCFunction(CFunctionType function) 171 | { 172 | Value *v = PushValue(); 173 | v->type_ = ValueT_CFunction; 174 | v->cfunc_ = function; 175 | } 176 | 177 | void StackAPI::PushValue(const Value &value) 178 | { 179 | *PushValue() = value; 180 | } 181 | 182 | void StackAPI::ArgCountError(int expect_count) 183 | { 184 | auto cfunc_error = state_->GetCFunctionErrorData(); 185 | cfunc_error->type_ = CFuntionErrorType_ArgCount; 186 | cfunc_error->expect_arg_count_ = expect_count; 187 | } 188 | 189 | void StackAPI::ArgTypeError(int arg_index, ValueT expect_type) 190 | { 191 | auto cfunc_error = state_->GetCFunctionErrorData(); 192 | cfunc_error->type_ = CFuntionErrorType_ArgType; 193 | cfunc_error->arg_index_ = arg_index; 194 | cfunc_error->expect_type_ = expect_type; 195 | } 196 | 197 | Value * StackAPI::PushValue() 198 | { 199 | return stack_->top_++; 200 | } 201 | 202 | Library::Library(State *state) 203 | : state_(state), 204 | global_(state->global_.table_) 205 | { 206 | } 207 | 208 | void Library::RegisterFunc(const char *name, CFunctionType func) 209 | { 210 | RegisterFunc(global_, name, func); 211 | } 212 | 213 | void Library::RegisterTableFunction(const char *name, const TableMemberReg *table, 214 | std::size_t size) 215 | { 216 | Value k; 217 | k.type_ = ValueT_String; 218 | k.str_ = state_->GetString(name); 219 | 220 | auto t = state_->NewTable(); 221 | Value v; 222 | v.type_ = ValueT_Table; 223 | v.table_ = t; 224 | global_->SetValue(k, v); 225 | 226 | RegisterToTable(t, table, size); 227 | } 228 | 229 | void Library::RegisterMetatable(const char *name, const TableMemberReg *table, 230 | std::size_t size) 231 | { 232 | auto t = state_->GetMetatable(name); 233 | RegisterToTable(t, table, size); 234 | } 235 | 236 | void Library::RegisterToTable(Table *table, const TableMemberReg *table_reg, 237 | std::size_t size) 238 | { 239 | for (std::size_t i = 0; i < size; ++i) 240 | { 241 | switch (table_reg[i].type_) 242 | { 243 | case ValueT_CFunction: 244 | RegisterFunc(table, table_reg[i].name_, table_reg[i].func_); 245 | break; 246 | case ValueT_Number: 247 | RegisterNumber(table, table_reg[i].name_, table_reg[i].number_); 248 | break; 249 | case ValueT_String: 250 | RegisterString(table, table_reg[i].name_, table_reg[i].str_); 251 | break; 252 | default: break; 253 | } 254 | } 255 | } 256 | 257 | void Library::RegisterFunc(Table *table, const char *name, CFunctionType func) 258 | { 259 | Value k; 260 | k.type_ = ValueT_String; 261 | k.str_ = state_->GetString(name); 262 | 263 | Value v; 264 | v.type_ = ValueT_CFunction; 265 | v.cfunc_ = func; 266 | table->SetValue(k, v); 267 | } 268 | 269 | void Library::RegisterNumber(Table *table, const char *name, double number) 270 | { 271 | Value k; 272 | k.type_ = ValueT_String; 273 | k.str_ = state_->GetString(name); 274 | 275 | Value v; 276 | v.type_ = ValueT_Number; 277 | v.num_ = number; 278 | table->SetValue(k, v); 279 | } 280 | 281 | void Library::RegisterString(Table *table, const char *name, const char *str) 282 | { 283 | Value k; 284 | k.type_ = ValueT_String; 285 | k.str_ = state_->GetString(name); 286 | 287 | Value v; 288 | v.type_ = ValueT_String; 289 | v.str_ = state_->GetString(str); 290 | table->SetValue(k, v); 291 | } 292 | } // namespace luna 293 | -------------------------------------------------------------------------------- /luna/LibMath.cpp: -------------------------------------------------------------------------------- 1 | #include "LibMath.h" 2 | #include 3 | #include 4 | #include 5 | 6 | namespace lib { 7 | namespace math { 8 | 9 | // Define one parameter one return value math function 10 | #define MATH_FUNCTION(name, std_name) \ 11 | int name(luna::State *state) \ 12 | { \ 13 | luna::StackAPI api(state); \ 14 | if (!api.CheckArgs(1, luna::ValueT_Number)) \ 15 | return 0; \ 16 | api.PushNumber(std::std_name(api.GetNumber(0))); \ 17 | return 1; \ 18 | } 19 | 20 | // Define two parameters one return value math function 21 | #define MATH_FUNCTION2(name, std_name) \ 22 | int name(luna::State *state) \ 23 | { \ 24 | luna::StackAPI api(state); \ 25 | if (!api.CheckArgs(2, luna::ValueT_Number, \ 26 | luna::ValueT_Number)) \ 27 | return 0; \ 28 | api.PushNumber(std::std_name(api.GetNumber(0), \ 29 | api.GetNumber(1))); \ 30 | return 1; \ 31 | } 32 | 33 | MATH_FUNCTION(Abs, abs) 34 | MATH_FUNCTION(Acos, acos) 35 | MATH_FUNCTION(Asin, asin) 36 | MATH_FUNCTION(Atan, atan) 37 | MATH_FUNCTION(Ceil, ceil) 38 | MATH_FUNCTION(Cos, cos) 39 | MATH_FUNCTION(Cosh, cosh) 40 | MATH_FUNCTION(Exp, exp) 41 | MATH_FUNCTION(Floor, floor) 42 | MATH_FUNCTION(Sin, sin) 43 | MATH_FUNCTION(Sinh, sinh) 44 | MATH_FUNCTION(Sqrt, sqrt) 45 | MATH_FUNCTION(Tan, tan) 46 | MATH_FUNCTION(Tanh, tanh) 47 | 48 | MATH_FUNCTION2(Atan2, atan2) 49 | MATH_FUNCTION2(Fmod, fmod) 50 | MATH_FUNCTION2(Ldexp, ldexp) 51 | MATH_FUNCTION2(Pow, pow) 52 | 53 | int Deg(luna::State *state) 54 | { 55 | luna::StackAPI api(state); 56 | if (!api.CheckArgs(1, luna::ValueT_Number)) 57 | return 0; 58 | 59 | api.PushNumber(api.GetNumber(0) / M_PI * 180); 60 | return 1; 61 | } 62 | 63 | int Rad(luna::State *state) 64 | { 65 | luna::StackAPI api(state); 66 | if (!api.CheckArgs(1, luna::ValueT_Number)) 67 | return 0; 68 | 69 | api.PushNumber(api.GetNumber(0) / 180 * M_PI); 70 | return 1; 71 | } 72 | 73 | int Log(luna::State *state) 74 | { 75 | luna::StackAPI api(state); 76 | if (!api.CheckArgs(1, luna::ValueT_Number, luna::ValueT_Number)) 77 | return 0; 78 | 79 | auto l = std::log(api.GetNumber(0)); 80 | if (api.GetStackSize() > 1) 81 | { 82 | auto b = std::log(api.GetNumber(1)); 83 | l /= b; 84 | } 85 | 86 | api.PushNumber(l); 87 | return 1; 88 | } 89 | 90 | int Min(luna::State *state) 91 | { 92 | luna::StackAPI api(state); 93 | if (!api.CheckArgs(1, luna::ValueT_Number)) 94 | return 0; 95 | 96 | auto min = api.GetNumber(0); 97 | auto params = api.GetStackSize(); 98 | for (int i = 1; i < params; ++i) 99 | { 100 | if (!api.IsNumber(i)) 101 | { 102 | api.ArgTypeError(i, luna::ValueT_Number); 103 | return 0; 104 | } 105 | 106 | auto n = api.GetNumber(i); 107 | if (n < min) min = n; 108 | } 109 | 110 | api.PushNumber(min); 111 | return 1; 112 | } 113 | 114 | int Max(luna::State *state) 115 | { 116 | luna::StackAPI api(state); 117 | if (!api.CheckArgs(1, luna::ValueT_Number)) 118 | return 0; 119 | 120 | auto max = api.GetNumber(0); 121 | auto params = api.GetStackSize(); 122 | for (int i = 1; i < params; ++i) 123 | { 124 | if (!api.IsNumber(i)) 125 | { 126 | api.ArgTypeError(i, luna::ValueT_Number); 127 | return 0; 128 | } 129 | 130 | auto n = api.GetNumber(i); 131 | if (n > max) max = n; 132 | } 133 | 134 | api.PushNumber(max); 135 | return 1; 136 | } 137 | 138 | int Frexp(luna::State *state) 139 | { 140 | luna::StackAPI api(state); 141 | if (!api.CheckArgs(1, luna::ValueT_Number)) 142 | return 0; 143 | 144 | int exp = 0; 145 | auto m = std::frexp(api.GetNumber(0), &exp); 146 | api.PushNumber(m); 147 | api.PushNumber(exp); 148 | return 2; 149 | } 150 | 151 | int Modf(luna::State *state) 152 | { 153 | luna::StackAPI api(state); 154 | if (!api.CheckArgs(1, luna::ValueT_Number)) 155 | return 0; 156 | 157 | double ipart = 0.0; 158 | auto fpart = std::modf(api.GetNumber(0), &ipart); 159 | api.PushNumber(ipart); 160 | api.PushNumber(fpart); 161 | return 2; 162 | } 163 | 164 | // Rand engine for math.random function 165 | class RandEngine 166 | { 167 | public: 168 | typedef unsigned int result_type; 169 | #ifdef _MSC_VER 170 | static result_type min() { return 0; } 171 | static result_type max() { return RAND_MAX; } 172 | #else 173 | static constexpr result_type min() { return 0; } 174 | static constexpr result_type max() { return RAND_MAX; } 175 | #endif // _MSC_VER 176 | result_type operator() () { return std::rand(); } 177 | 178 | RandEngine() { } 179 | RandEngine(const RandEngine&) = delete; 180 | void operator = (const RandEngine&) = delete; 181 | }; 182 | 183 | int Random(luna::State *state) 184 | { 185 | luna::StackAPI api(state); 186 | if (!api.CheckArgs(0, luna::ValueT_Number, luna::ValueT_Number)) 187 | return 0; 188 | 189 | auto params = api.GetStackSize(); 190 | if (params == 0) 191 | { 192 | RandEngine engine; 193 | std::uniform_real_distribution<> dis; 194 | api.PushNumber(dis(engine)); 195 | } 196 | else if (params == 1) 197 | { 198 | auto max = static_cast(api.GetNumber(0)); 199 | 200 | RandEngine engine; 201 | std::uniform_int_distribution dis(1, max); 202 | api.PushNumber(static_cast(dis(engine))); 203 | } 204 | else if (params >= 2) 205 | { 206 | auto min = static_cast(api.GetNumber(0)); 207 | auto max = static_cast(api.GetNumber(1)); 208 | 209 | RandEngine engine; 210 | std::uniform_int_distribution dis(min, max); 211 | api.PushNumber(static_cast(dis(engine))); 212 | } 213 | 214 | return 1; 215 | } 216 | 217 | int RandomSeed(luna::State *state) 218 | { 219 | luna::StackAPI api(state); 220 | if (!api.CheckArgs(1, luna::ValueT_Number)) 221 | return 0; 222 | 223 | std::srand(static_cast(api.GetNumber(0))); 224 | return 0; 225 | } 226 | 227 | void RegisterLibMath(luna::State *state) 228 | { 229 | luna::Library lib(state); 230 | luna::TableMemberReg math[] = { 231 | { "abs", Abs }, 232 | { "acos", Acos }, 233 | { "asin", Asin }, 234 | { "atan", Atan }, 235 | { "atan2", Atan2 }, 236 | { "ceil", Ceil }, 237 | { "cos", Cos }, 238 | { "cosh", Cosh }, 239 | { "deg", Deg }, 240 | { "exp", Exp }, 241 | { "floor", Floor }, 242 | { "fmod", Fmod }, 243 | { "frexp", Frexp }, 244 | { "ldexp", Ldexp }, 245 | { "log", Log }, 246 | { "max", Max }, 247 | { "min", Min }, 248 | { "modf", Modf }, 249 | { "pow", Pow }, 250 | { "rad", Rad }, 251 | { "random", Random }, 252 | { "randomseed", RandomSeed }, 253 | { "sin", Sin }, 254 | { "sinh", Sinh }, 255 | { "sqrt", Sqrt }, 256 | { "tan", Tan }, 257 | { "tanh", Tanh }, 258 | { "huge", HUGE_VAL }, 259 | { "pi", M_PI } 260 | }; 261 | 262 | lib.RegisterTableFunction("math", math); 263 | } 264 | 265 | } // namespace math 266 | } // namespace lib 267 | -------------------------------------------------------------------------------- /luna/State.cpp: -------------------------------------------------------------------------------- 1 | #include "State.h" 2 | #include "GC.h" 3 | #include "VM.h" 4 | #include "Lex.h" 5 | #include "String.h" 6 | #include "Function.h" 7 | #include "Table.h" 8 | #include "TextInStream.h" 9 | #include "Exception.h" 10 | #include 11 | 12 | namespace luna 13 | { 14 | #define METATABLES "__metatables" 15 | #define MODULES_TABLE "__modules" 16 | 17 | State::State() 18 | { 19 | string_pool_.reset(new StringPool); 20 | 21 | // Init GC 22 | gc_.reset(new GC([&](GCObject *obj, unsigned int type) { 23 | if (type == GCObjectType_String) 24 | { 25 | string_pool_->DeleteString(static_cast(obj)); 26 | } 27 | delete obj; 28 | })); 29 | auto root = std::bind(&State::FullGCRoot, this, std::placeholders::_1); 30 | gc_->SetRootTraveller(root, root); 31 | 32 | // New global table 33 | global_.table_ = NewTable(); 34 | global_.type_ = ValueT_Table; 35 | 36 | // New table for store metatables 37 | Value k; 38 | k.type_ = ValueT_String; 39 | k.str_ = GetString(METATABLES); 40 | Value v; 41 | v.type_ = ValueT_Table; 42 | v.table_ = NewTable(); 43 | global_.table_->SetValue(k, v); 44 | 45 | // New table for store modules 46 | k.type_ = ValueT_String; 47 | k.str_ = GetString(MODULES_TABLE); 48 | v.type_ = ValueT_Table; 49 | v.table_ = NewTable(); 50 | global_.table_->SetValue(k, v); 51 | 52 | // Init module manager 53 | module_manager_.reset(new ModuleManager(this, v.table_)); 54 | } 55 | 56 | State::~State() 57 | { 58 | gc_->ResetDeleter(); 59 | } 60 | 61 | bool State::IsModuleLoaded(const std::string &module_name) const 62 | { 63 | return module_manager_->IsLoaded(module_name); 64 | } 65 | 66 | void State::LoadModule(const std::string &module_name) 67 | { 68 | auto value = module_manager_->GetModuleClosure(module_name); 69 | if (value.IsNil()) 70 | module_manager_->LoadModule(module_name); 71 | else 72 | *stack_.top_++ = value; 73 | } 74 | 75 | void State::DoModule(const std::string &module_name) 76 | { 77 | LoadModule(module_name); 78 | if (CallFunction(stack_.top_ - 1, 0, 0)) 79 | { 80 | VM vm(this); 81 | vm.Execute(); 82 | } 83 | } 84 | 85 | void State::DoString(const std::string &str, const std::string &name) 86 | { 87 | module_manager_->LoadString(str, name); 88 | if (CallFunction(stack_.top_ - 1, 0, 0)) 89 | { 90 | VM vm(this); 91 | vm.Execute(); 92 | } 93 | } 94 | 95 | bool State::CallFunction(Value *f, int arg_count, int expect_result) 96 | { 97 | assert(f->type_ == ValueT_Closure || f->type_ == ValueT_CFunction); 98 | 99 | // Set stack top when arg_count is fixed 100 | if (arg_count != EXP_VALUE_COUNT_ANY) 101 | stack_.top_ = f + 1 + arg_count; 102 | 103 | if (f->type_ == ValueT_Closure) 104 | { 105 | // We need enter next ExecuteFrame 106 | CallClosure(f, expect_result); 107 | return true; 108 | } 109 | else 110 | { 111 | CallCFunction(f, expect_result); 112 | return false; 113 | } 114 | } 115 | 116 | String * State::GetString(const std::string &str) 117 | { 118 | auto s = string_pool_->GetString(str); 119 | if (!s) 120 | { 121 | s = gc_->NewString(); 122 | s->SetValue(str); 123 | string_pool_->AddString(s); 124 | } 125 | return s; 126 | } 127 | 128 | String * State::GetString(const char *str, std::size_t len) 129 | { 130 | auto s = string_pool_->GetString(str, len); 131 | if (!s) 132 | { 133 | s = gc_->NewString(); 134 | s->SetValue(str, len); 135 | string_pool_->AddString(s); 136 | } 137 | return s; 138 | } 139 | 140 | String * State::GetString(const char *str) 141 | { 142 | auto s = string_pool_->GetString(str); 143 | if (!s) 144 | { 145 | s = gc_->NewString(); 146 | s->SetValue(str); 147 | string_pool_->AddString(s); 148 | } 149 | return s; 150 | } 151 | 152 | Function * State::NewFunction() 153 | { 154 | return gc_->NewFunction(); 155 | } 156 | 157 | Closure * State::NewClosure() 158 | { 159 | return gc_->NewClosure(); 160 | } 161 | 162 | Upvalue * State::NewUpvalue() 163 | { 164 | return gc_->NewUpvalue(); 165 | } 166 | 167 | Table * State::NewTable() 168 | { 169 | return gc_->NewTable(); 170 | } 171 | 172 | UserData * State::NewUserData() 173 | { 174 | return gc_->NewUserData(); 175 | } 176 | 177 | CallInfo * State::GetCurrentCall() 178 | { 179 | if (calls_.empty()) 180 | return nullptr; 181 | return &calls_.back(); 182 | } 183 | 184 | Value * State::GetGlobal() 185 | { 186 | return &global_; 187 | } 188 | 189 | Table * State::GetMetatable(const char *metatable_name) 190 | { 191 | Value k; 192 | k.type_ = ValueT_String; 193 | k.str_ = GetString(metatable_name); 194 | 195 | auto metatables = GetMetatables(); 196 | auto metatable = metatables->GetValue(k); 197 | 198 | // Create table when metatable not existed 199 | if (metatable.type_ == ValueT_Nil) 200 | { 201 | metatable.type_ = ValueT_Table; 202 | metatable.table_ = NewTable(); 203 | metatables->SetValue(k, metatable); 204 | } 205 | 206 | assert(metatable.type_ == ValueT_Table); 207 | return metatable.table_; 208 | } 209 | 210 | void State::EraseMetatable(const char *metatable_name) 211 | { 212 | Value k; 213 | k.type_ = ValueT_String; 214 | k.str_ = GetString(metatable_name); 215 | 216 | Value nil; 217 | auto metatables = GetMetatables(); 218 | metatables->SetValue(k, nil); 219 | } 220 | 221 | void State::FullGCRoot(GCObjectVisitor *v) 222 | { 223 | // Visit global table 224 | global_.Accept(v); 225 | 226 | // Visit stack values 227 | for (const auto &value : stack_.stack_) 228 | { 229 | value.Accept(v); 230 | } 231 | 232 | // Visit call info 233 | for (const auto &call : calls_) 234 | { 235 | call.register_->Accept(v); 236 | if (call.func_) 237 | { 238 | call.func_->Accept(v); 239 | } 240 | } 241 | } 242 | 243 | Table * State::GetMetatables() 244 | { 245 | Value k; 246 | k.type_ = ValueT_String; 247 | k.str_ = GetString(METATABLES); 248 | 249 | auto v = global_.table_->GetValue(k); 250 | assert(v.type_ == ValueT_Table); 251 | return v.table_; 252 | } 253 | 254 | void State::CallClosure(Value *f, int expect_result) 255 | { 256 | CallInfo callee; 257 | Function *callee_proto = f->closure_->GetPrototype(); 258 | 259 | callee.func_ = f; 260 | callee.instruction_ = callee_proto->GetOpCodes(); 261 | callee.end_ = callee.instruction_ + callee_proto->OpCodeSize(); 262 | callee.expect_result_ = expect_result; 263 | 264 | Value *arg = f + 1; 265 | int fixed_args = callee_proto->FixedArgCount(); 266 | 267 | // Fixed arg start from base register 268 | if (callee_proto->HasVararg()) 269 | { 270 | Value *top = stack_.top_; 271 | callee.register_ = top; 272 | int count = top - arg; 273 | for (int i = 0; i < count && i < fixed_args; ++i) 274 | *top++ = *arg++; 275 | } 276 | else 277 | { 278 | callee.register_ = arg; 279 | // fill nil for overflow args 280 | auto new_top = callee.register_ + fixed_args; 281 | for (auto arg = stack_.top_; arg < new_top; arg++) 282 | { 283 | arg->SetNil(); 284 | } 285 | } 286 | 287 | stack_.SetNewTop(callee.register_ + fixed_args); 288 | calls_.push_back(callee); 289 | } 290 | 291 | void State::CallCFunction(Value *f, int expect_result) 292 | { 293 | // Push the c function CallInfo 294 | CallInfo callee; 295 | callee.register_ = f + 1; 296 | callee.func_ = f; 297 | callee.expect_result_ = expect_result; 298 | calls_.push_back(callee); 299 | 300 | // Call c function 301 | CFunctionType cfunc = f->cfunc_; 302 | ClearCFunctionError(); 303 | int res_count = cfunc(this); 304 | CheckCFunctionError(); 305 | 306 | Value *src = nullptr; 307 | if (res_count > 0) 308 | src = stack_.top_ - res_count; 309 | 310 | // Copy c function result to caller stack 311 | Value *dst = f; 312 | if (expect_result == EXP_VALUE_COUNT_ANY) 313 | { 314 | for (int i = 0; i < res_count; ++i) 315 | *dst++ = *src++; 316 | } 317 | else 318 | { 319 | int count = std::min(expect_result, res_count); 320 | for (int i = 0; i < count; ++i) 321 | *dst++ = *src++; 322 | // Set all remain expect results to nil 323 | count = expect_result - res_count; 324 | for (int i = 0; i < count; ++i, ++dst) 325 | dst->SetNil(); 326 | } 327 | 328 | // Set registers which after dst to nil 329 | // and set new stack top pointer 330 | stack_.SetNewTop(dst); 331 | 332 | // Pop the c function CallInfo 333 | calls_.pop_back(); 334 | } 335 | 336 | void State::CheckCFunctionError() 337 | { 338 | auto error = GetCFunctionErrorData(); 339 | if (error->type_ == CFuntionErrorType_NoError) 340 | return ; 341 | 342 | CallCFuncException exp; 343 | if (error->type_ == CFuntionErrorType_ArgCount) 344 | { 345 | exp = CallCFuncException("expect ", 346 | error->expect_arg_count_, " arguments"); 347 | } 348 | else if (error->type_ == CFuntionErrorType_ArgType) 349 | { 350 | auto &call = calls_.back(); 351 | auto arg = call.register_ + error->arg_index_; 352 | exp = CallCFuncException("argument #", error->arg_index_ + 1, 353 | " is a ", arg->TypeName(), " value, expect a ", 354 | Value::TypeName(error->expect_type_), " value"); 355 | } 356 | 357 | // Pop the c function CallInfo 358 | calls_.pop_back(); 359 | throw exp; 360 | } 361 | } // namespace luna 362 | -------------------------------------------------------------------------------- /unittests/GCTest.cpp: -------------------------------------------------------------------------------- 1 | #include "luna/GC.h" 2 | #include "luna/Table.h" 3 | #include "luna/Function.h" 4 | #include "luna/String.h" 5 | #include "luna/Value.h" 6 | 7 | #ifdef _MSC_VER 8 | #include 9 | #else 10 | #include 11 | #endif // _MSC_VER 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | luna::GC g_gc(luna::GC::DefaultDeleter(), true); 19 | std::deque g_globalTable; 20 | std::deque g_globalFunction; 21 | std::deque g_globalClosure; 22 | std::deque g_globalString; 23 | 24 | std::deque g_scopeTable; 25 | std::deque g_scopeClosure; 26 | std::deque g_scopeString; 27 | 28 | template 29 | inline T RandomRange(T min, T max) 30 | { 31 | return rand() % (max + 1 - min) + min; 32 | } 33 | 34 | template 35 | inline T RandomNum(T max) 36 | { 37 | if (max == 0) 38 | return 0; 39 | else 40 | return RandomRange(static_cast(0), max - 1); 41 | } 42 | 43 | void MinorRoot(luna::GCObjectVisitor *v) 44 | { 45 | for (auto t : g_scopeTable) 46 | t->Accept(v); 47 | for (auto c : g_scopeClosure) 48 | c->Accept(v); 49 | for (auto s : g_scopeString) 50 | s->Accept(v); 51 | } 52 | 53 | void MajorRoot(luna::GCObjectVisitor *v) 54 | { 55 | for (auto t : g_globalTable) 56 | t->Accept(v); 57 | for (auto f : g_globalFunction) 58 | f->Accept(v); 59 | for (auto c : g_globalClosure) 60 | c->Accept(v); 61 | for (auto s : g_globalString) 62 | s->Accept(v); 63 | MinorRoot(v); 64 | } 65 | 66 | luna::Table * RandomTable(); 67 | luna::Function * RandomFunction(); 68 | luna::Closure * RandomClosure(); 69 | luna::String * RandomString(); 70 | luna::Value RandomValue(bool exclude_table = false); 71 | 72 | luna::Table * RandomTable() 73 | { 74 | auto t = g_gc.NewTable(); 75 | 76 | int array_count = RandomNum(10); 77 | for (int i = 0; i < array_count; ++i) 78 | t->SetArrayValue(i + 1, RandomValue(true)); 79 | 80 | int hash_count = RandomNum(20); 81 | for (int i = 0; i < hash_count; ++i) 82 | t->SetValue(RandomValue(true), RandomValue(true)); 83 | 84 | return t; 85 | } 86 | 87 | luna::Function * RandomFunction() 88 | { 89 | auto f = g_gc.NewFunction(); 90 | 91 | auto s = RandomString(); 92 | f->SetModuleName(s); 93 | f->SetLine(RandomNum(1000)); 94 | 95 | int instruction_count = RandomRange(10, 1000); 96 | for (int i = 0; i < instruction_count; ++i) 97 | { 98 | unsigned int op_min = luna::OpType_LoadNil; 99 | unsigned int op_max = luna::OpType_GetGlobal; 100 | luna::OpType op = static_cast(RandomRange(op_min, op_max)); 101 | luna::Instruction instruction(op, RandomNum(128), RandomNum(128), RandomNum(128)); 102 | f->AddInstruction(instruction, i); 103 | } 104 | 105 | int const_num = RandomNum(5); 106 | for (int i = 0; i < const_num; ++i) 107 | f->AddConstNumber(RandomNum(100000)); 108 | 109 | int const_str = RandomNum(5); 110 | for (int i = 0; i < const_str; ++i) 111 | f->AddConstString(RandomString()); 112 | 113 | CHECK_BARRIER(g_gc, f); 114 | 115 | return f; 116 | } 117 | 118 | luna::Closure * RandomClosure() 119 | { 120 | if (g_globalFunction.empty()) 121 | g_globalFunction.push_back(RandomFunction()); 122 | 123 | auto index = RandomNum(g_globalFunction.size()); 124 | auto proto = g_globalFunction[index]; 125 | 126 | auto c = g_gc.NewClosure(); 127 | c->SetPrototype(proto); 128 | return c; 129 | } 130 | 131 | luna::String * RandomString() 132 | { 133 | std::string str; 134 | int count = RandomRange(1, 150); 135 | for (int i = 0; i < count; ++i) 136 | str.push_back(RandomRange('a', 'z')); 137 | 138 | auto s = g_gc.NewString(); 139 | s->SetValue(str); 140 | return s; 141 | } 142 | 143 | luna::Value RandomValue(bool exclude_table) 144 | { 145 | luna::ValueT type = luna::ValueT_Nil; 146 | int percent = RandomRange(1, 100); 147 | if (percent <= 20) 148 | type = luna::ValueT_Nil; 149 | else if (percent <= 30) 150 | type = luna::ValueT_Bool; 151 | else if (percent <= 60) 152 | type = luna::ValueT_Number; 153 | else if (percent <= 70) 154 | type = luna::ValueT_String; 155 | else if (percent <= 80) 156 | type = luna::ValueT_Closure; 157 | else if (percent <= 90) 158 | type = luna::ValueT_CFunction; 159 | else 160 | type = exclude_table ? luna::ValueT_Number : luna::ValueT_Table; 161 | 162 | luna::Value value; 163 | value.type_ = type; 164 | switch (type) 165 | { 166 | case luna::ValueT_Nil: 167 | break; 168 | case luna::ValueT_Bool: 169 | value.bvalue_ = RandomRange(0, 1) ? true : false; 170 | break; 171 | case luna::ValueT_Number: 172 | value.num_ = RandomNum(100000); 173 | break; 174 | case luna::ValueT_Obj: 175 | value.obj_ = RandomString(); 176 | break; 177 | case luna::ValueT_String: 178 | value.str_ = RandomString(); 179 | break; 180 | case luna::ValueT_Closure: 181 | value.closure_ = RandomClosure(); 182 | break; 183 | case luna::ValueT_Table: 184 | value.table_ = RandomTable(); 185 | break; 186 | case luna::ValueT_CFunction: 187 | break; 188 | default: 189 | break; 190 | } 191 | 192 | return value; 193 | } 194 | 195 | void NewObjInGlobal() 196 | { 197 | int percent = RandomRange(1, 100); 198 | if (percent <= 20) 199 | { 200 | g_globalTable.push_back(g_gc.NewTable(luna::GCGen2)); 201 | } 202 | else if (percent <= 50) 203 | { 204 | g_globalString.push_back(g_gc.NewString(luna::GCGen2)); 205 | } 206 | else if (percent <= 60) 207 | { 208 | if (!g_globalFunction.empty()) 209 | { 210 | auto index = RandomNum(g_globalFunction.size()); 211 | auto proto = g_globalFunction[index]; 212 | auto c = g_gc.NewClosure(luna::GCGen1); 213 | c->SetPrototype(proto); 214 | g_globalClosure.push_back(c); 215 | } 216 | } 217 | } 218 | 219 | void RunScope(int count) 220 | { 221 | for (int i = 0; i < count; ++i) 222 | { 223 | int percent = RandomRange(1, 100); 224 | if (percent <= 15) 225 | RandomValue(); 226 | else if (percent <= 20) 227 | g_globalFunction.push_back(RandomFunction()); 228 | else if (percent <= 30) 229 | g_scopeString.push_back(RandomString()); 230 | else if (percent <= 40) 231 | g_scopeClosure.push_back(RandomClosure()); 232 | else if (percent <= 50) 233 | g_scopeTable.push_back(RandomTable()); 234 | else if (percent <= 55) 235 | NewObjInGlobal(); 236 | } 237 | } 238 | 239 | void TouchGlobalTable(int count) 240 | { 241 | if (g_globalTable.empty()) 242 | return ; 243 | 244 | std::size_t total_scope = 0; 245 | total_scope += g_scopeTable.size(); 246 | total_scope += g_scopeString.size(); 247 | total_scope += g_scopeClosure.size(); 248 | if (total_scope == 0) 249 | return ; 250 | 251 | for (int i = 0; i < count; ++i) 252 | { 253 | auto setter = [&](luna::Value &v, std::size_t index) { 254 | if (index < g_scopeTable.size()) 255 | { 256 | v.type_ = luna::ValueT_Table; 257 | v.table_ = g_scopeTable[index]; 258 | } 259 | else if (index < g_scopeTable.size() + g_scopeString.size()) 260 | { 261 | index -= g_scopeTable.size(); 262 | v.type_ = luna::ValueT_String; 263 | v.str_ = g_scopeString[index]; 264 | } 265 | else 266 | { 267 | index -= g_scopeTable.size() + g_scopeString.size(); 268 | v.type_ = luna::ValueT_Closure; 269 | v.closure_ = g_scopeClosure[index]; 270 | } 271 | }; 272 | 273 | luna::Value key; 274 | luna::Value value; 275 | auto key_index = RandomNum(total_scope); 276 | auto value_index = RandomNum(total_scope); 277 | setter(key, key_index); 278 | setter(value, value_index); 279 | 280 | auto global_index = RandomNum(g_globalTable.size()); 281 | auto global = g_globalTable[global_index]; 282 | global->SetValue(key, value); 283 | CHECK_BARRIER(g_gc, global); 284 | } 285 | } 286 | 287 | void FreeGlobal(int count) 288 | { 289 | for (int i = 0; i < count; ++i) 290 | { 291 | std::size_t size = 0; 292 | size += g_globalFunction.size(); 293 | size += g_globalClosure.size(); 294 | size += g_globalString.size(); 295 | size += g_globalTable.size(); 296 | 297 | if (size == 0) 298 | return ; 299 | 300 | auto index = RandomNum(size); 301 | if (index < g_globalFunction.size()) 302 | { 303 | g_globalFunction.erase(g_globalFunction.begin() + index); 304 | } 305 | else if (index < g_globalFunction.size() + g_globalClosure.size()) 306 | { 307 | index -= g_globalFunction.size(); 308 | g_globalClosure.erase(g_globalClosure.begin() + index); 309 | } 310 | else if (index < g_globalFunction.size() + g_globalClosure.size() + 311 | g_globalString.size()) 312 | { 313 | index -= g_globalFunction.size() + g_globalClosure.size(); 314 | g_globalString.erase(g_globalString.begin() + index); 315 | } 316 | else 317 | { 318 | index -= g_globalFunction.size() + g_globalClosure.size() + g_globalString.size(); 319 | g_globalTable.erase(g_globalTable.begin() + index); 320 | } 321 | } 322 | } 323 | 324 | void RandomLoop() 325 | { 326 | int free_global_count_max = 20; 327 | 328 | while (true) 329 | { 330 | g_scopeClosure.clear(); 331 | g_scopeString.clear(); 332 | g_scopeTable.clear(); 333 | 334 | int scope_count = RandomRange(1, 1000); 335 | RunScope(scope_count); 336 | 337 | TouchGlobalTable(RandomNum(scope_count)); 338 | 339 | int free_count = RandomRange(1, free_global_count_max++); 340 | FreeGlobal(free_count); 341 | 342 | if (free_global_count_max >= 1000) 343 | free_global_count_max = 20; 344 | 345 | g_gc.CheckGC(); 346 | #ifdef _MSC_VER 347 | Sleep(RandomRange(1, 20)); 348 | #else 349 | usleep(RandomRange(1000, 20000)); 350 | #endif // _MSC_VER 351 | } 352 | } 353 | 354 | int main() 355 | { 356 | srand(static_cast(time(nullptr))); 357 | g_gc.SetRootTraveller(MinorRoot, MajorRoot); 358 | RandomLoop(); 359 | return 0; 360 | } 361 | -------------------------------------------------------------------------------- /unittests/TestCommon.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_COMMON_H 2 | #define TEST_COMMON_H 3 | 4 | #include "luna/Lex.h" 5 | #include "luna/Parser.h" 6 | #include "luna/State.h" 7 | #include "luna/String.h" 8 | #include "luna/TextInStream.h" 9 | #include "luna/Exception.h" 10 | #include "luna/Visitor.h" 11 | #include 12 | #include 13 | 14 | class ParserWrapper 15 | { 16 | public: 17 | explicit ParserWrapper(const std::string &str = "") 18 | : iss_(str), state_(), name_("parser"), 19 | lexer_(&state_, &name_, std::bind(&io::text::InStringStream::GetChar, &iss_)) 20 | { 21 | } 22 | 23 | void SetInput(const std::string &input) 24 | { 25 | iss_.SetInputString(input); 26 | } 27 | 28 | bool IsEOF() 29 | { 30 | luna::TokenDetail detail; 31 | return lexer_.GetToken(&detail) == luna::Token_EOF; 32 | } 33 | 34 | std::unique_ptr Parse() 35 | { 36 | return luna::Parse(&lexer_); 37 | } 38 | 39 | luna::State * GetState() 40 | { 41 | return &state_; 42 | } 43 | 44 | private: 45 | io::text::InStringStream iss_; 46 | luna::State state_; 47 | luna::String name_; 48 | luna::Lexer lexer_; 49 | }; 50 | 51 | #define MATCH_AST_TYPE(ast, not_match_stmt) \ 52 | if (!the_ast_node_) \ 53 | { \ 54 | auto f = [&]() not_match_stmt; \ 55 | SetResult(typename std::conditional< \ 56 | std::is_same::type, \ 57 | ASTType>::value, \ 58 | std::true_type, std::false_type>::type(), ast, f); \ 59 | } 60 | 61 | template 62 | class ASTFinder : public luna::Visitor 63 | { 64 | public: 65 | explicit ASTFinder(const FinderType &finder) 66 | : the_ast_node_(nullptr), finder_(finder) { } 67 | 68 | ASTType * GetResult() const 69 | { 70 | return the_ast_node_; 71 | } 72 | 73 | virtual void Visit(luna::Chunk *ast, void *) 74 | { 75 | MATCH_AST_TYPE(ast, { 76 | ast->block_->Accept(this, nullptr); 77 | }) 78 | } 79 | 80 | virtual void Visit(luna::Block *ast, void *) 81 | { 82 | MATCH_AST_TYPE(ast, { 83 | for (auto &stmt : ast->statements_) 84 | stmt->Accept(this, nullptr); 85 | if (ast->return_stmt_) 86 | ast->return_stmt_->Accept(this, nullptr); 87 | }) 88 | } 89 | 90 | virtual void Visit(luna::ReturnStatement *ast, void *) 91 | { 92 | MATCH_AST_TYPE(ast, { 93 | if (ast->exp_list_) 94 | ast->exp_list_->Accept(this, nullptr); 95 | }) 96 | } 97 | 98 | virtual void Visit(luna::BreakStatement *ast, void *) 99 | { 100 | MATCH_AST_TYPE(ast, {}) 101 | } 102 | 103 | virtual void Visit(luna::DoStatement *ast, void *) 104 | { 105 | MATCH_AST_TYPE(ast, { 106 | ast->block_->Accept(this, nullptr); 107 | }) 108 | } 109 | 110 | virtual void Visit(luna::WhileStatement *ast, void *) 111 | { 112 | MATCH_AST_TYPE(ast, { 113 | ast->exp_->Accept(this, nullptr); 114 | ast->block_->Accept(this, nullptr); 115 | }) 116 | } 117 | 118 | virtual void Visit(luna::RepeatStatement *ast, void *) 119 | { 120 | MATCH_AST_TYPE(ast, { 121 | ast->block_->Accept(this, nullptr); 122 | ast->exp_->Accept(this, nullptr); 123 | }) 124 | } 125 | 126 | virtual void Visit(luna::IfStatement *ast, void *) 127 | { 128 | MATCH_AST_TYPE(ast, { 129 | ast->exp_->Accept(this, nullptr); 130 | ast->true_branch_->Accept(this, nullptr); 131 | if (ast->false_branch_) 132 | ast->false_branch_->Accept(this, nullptr); 133 | }) 134 | } 135 | 136 | virtual void Visit(luna::ElseIfStatement *ast, void *) 137 | { 138 | MATCH_AST_TYPE(ast, { 139 | ast->exp_->Accept(this, nullptr); 140 | ast->true_branch_->Accept(this, nullptr); 141 | if (ast->false_branch_) 142 | ast->false_branch_->Accept(this, nullptr); 143 | }) 144 | } 145 | 146 | virtual void Visit(luna::ElseStatement *ast, void *) 147 | { 148 | MATCH_AST_TYPE(ast, { 149 | ast->block_->Accept(this, nullptr); 150 | }) 151 | } 152 | 153 | virtual void Visit(luna::NumericForStatement *ast, void *) 154 | { 155 | MATCH_AST_TYPE(ast, { 156 | ast->exp1_->Accept(this, nullptr); 157 | ast->exp2_->Accept(this, nullptr); 158 | if (ast->exp3_) 159 | ast->exp3_->Accept(this, nullptr); 160 | ast->block_->Accept(this, nullptr); 161 | }) 162 | } 163 | 164 | virtual void Visit(luna::GenericForStatement *ast, void *) 165 | { 166 | MATCH_AST_TYPE(ast, { 167 | ast->name_list_->Accept(this, nullptr); 168 | ast->exp_list_->Accept(this, nullptr); 169 | ast->block_->Accept(this, nullptr); 170 | }) 171 | } 172 | 173 | virtual void Visit(luna::FunctionStatement *ast, void *) 174 | { 175 | MATCH_AST_TYPE(ast, { 176 | ast->func_name_->Accept(this, nullptr); 177 | ast->func_body_->Accept(this, nullptr); 178 | }) 179 | } 180 | 181 | virtual void Visit(luna::FunctionName *ast, void *) 182 | { 183 | MATCH_AST_TYPE(ast, {}) 184 | } 185 | 186 | virtual void Visit(luna::LocalFunctionStatement *ast, void *) 187 | { 188 | MATCH_AST_TYPE(ast, { 189 | ast->func_body_->Accept(this, nullptr); 190 | }) 191 | } 192 | 193 | virtual void Visit(luna::LocalNameListStatement *ast, void *) 194 | { 195 | MATCH_AST_TYPE(ast, { 196 | ast->name_list_->Accept(this, nullptr); 197 | if (ast->exp_list_) 198 | ast->exp_list_->Accept(this, nullptr); 199 | }) 200 | } 201 | 202 | virtual void Visit(luna::AssignmentStatement *ast, void *) 203 | { 204 | MATCH_AST_TYPE(ast, { 205 | ast->var_list_->Accept(this, nullptr); 206 | ast->exp_list_->Accept(this, nullptr); 207 | }) 208 | } 209 | 210 | virtual void Visit(luna::VarList *ast, void *) 211 | { 212 | MATCH_AST_TYPE(ast, { 213 | for (auto &var : ast->var_list_) 214 | var->Accept(this, nullptr); 215 | }) 216 | } 217 | 218 | virtual void Visit(luna::Terminator *ast, void *) 219 | { 220 | MATCH_AST_TYPE(ast, {}) 221 | } 222 | 223 | virtual void Visit(luna::BinaryExpression *ast, void *) 224 | { 225 | MATCH_AST_TYPE(ast, { 226 | ast->left_->Accept(this, nullptr); 227 | ast->right_->Accept(this, nullptr); 228 | }) 229 | } 230 | 231 | virtual void Visit(luna::UnaryExpression *ast, void *) 232 | { 233 | MATCH_AST_TYPE(ast, { 234 | ast->exp_->Accept(this, nullptr); 235 | }) 236 | } 237 | 238 | virtual void Visit(luna::FunctionBody *ast, void *) 239 | { 240 | MATCH_AST_TYPE(ast, { 241 | if (ast->param_list_) 242 | ast->param_list_->Accept(this, nullptr); 243 | ast->block_->Accept(this, nullptr); 244 | }) 245 | } 246 | 247 | virtual void Visit(luna::ParamList *ast, void *) 248 | { 249 | MATCH_AST_TYPE(ast, { 250 | if (ast->name_list_) 251 | ast->name_list_->Accept(this, nullptr); 252 | }) 253 | } 254 | 255 | virtual void Visit(luna::NameList *ast, void *) 256 | { 257 | MATCH_AST_TYPE(ast, {}) 258 | } 259 | 260 | virtual void Visit(luna::TableDefine *ast, void *) 261 | { 262 | MATCH_AST_TYPE(ast, { 263 | for (auto &field : ast->fields_) 264 | field->Accept(this, nullptr); 265 | }) 266 | } 267 | 268 | virtual void Visit(luna::TableIndexField *ast, void *) 269 | { 270 | MATCH_AST_TYPE(ast, { 271 | ast->index_->Accept(this, nullptr); 272 | ast->value_->Accept(this, nullptr); 273 | }) 274 | } 275 | 276 | virtual void Visit(luna::TableNameField *ast, void *) 277 | { 278 | MATCH_AST_TYPE(ast, { 279 | ast->value_->Accept(this, nullptr); 280 | }) 281 | } 282 | 283 | virtual void Visit(luna::TableArrayField *ast, void *) 284 | { 285 | MATCH_AST_TYPE(ast, { 286 | ast->value_->Accept(this, nullptr); 287 | }) 288 | } 289 | 290 | virtual void Visit(luna::IndexAccessor *ast, void *) 291 | { 292 | MATCH_AST_TYPE(ast, { 293 | ast->table_->Accept(this, nullptr); 294 | ast->index_->Accept(this, nullptr); 295 | }) 296 | } 297 | 298 | virtual void Visit(luna::MemberAccessor *ast, void *) 299 | { 300 | MATCH_AST_TYPE(ast, { 301 | ast->table_->Accept(this, nullptr); 302 | }) 303 | } 304 | 305 | virtual void Visit(luna::NormalFuncCall *ast, void *) 306 | { 307 | MATCH_AST_TYPE(ast, { 308 | ast->caller_->Accept(this, nullptr); 309 | ast->args_->Accept(this, nullptr); 310 | }) 311 | } 312 | 313 | virtual void Visit(luna::MemberFuncCall *ast, void *) 314 | { 315 | MATCH_AST_TYPE(ast, { 316 | ast->caller_->Accept(this, nullptr); 317 | ast->args_->Accept(this, nullptr); 318 | }) 319 | } 320 | 321 | virtual void Visit(luna::FuncCallArgs *ast, void *) 322 | { 323 | MATCH_AST_TYPE(ast, { 324 | if (ast->arg_) 325 | ast->arg_->Accept(this, nullptr); 326 | }) 327 | } 328 | 329 | virtual void Visit(luna::ExpressionList *ast, void *) 330 | { 331 | MATCH_AST_TYPE(ast, { 332 | for (auto &exp : ast->exp_list_) 333 | exp->Accept(this, nullptr); 334 | }) 335 | } 336 | 337 | private: 338 | template 339 | void SetResult(std::true_type, Type *t, const Func &op) 340 | { 341 | if (finder_(t)) 342 | the_ast_node_ = t; 343 | else 344 | op(); 345 | } 346 | 347 | template 348 | void SetResult(std::false_type, Type *t, const Func &op) 349 | { 350 | op(); 351 | } 352 | 353 | ASTType *the_ast_node_; 354 | FinderType finder_; 355 | }; 356 | 357 | template 358 | ASTType * ASTFind(const std::unique_ptr &root, 359 | const FinderType &finder) 360 | { 361 | ASTFinder ast_finder(finder); 362 | root->Accept(&ast_finder, nullptr); 363 | return ast_finder.GetResult(); 364 | } 365 | 366 | struct FindName 367 | { 368 | FindName(const std::string &name) : name_(name) { } 369 | 370 | bool operator () (const luna::Terminator *term) const 371 | { 372 | if (term->token_.token_ == luna::Token_Id) 373 | return term->token_.str_->GetStdString() == name_; 374 | else 375 | return false; 376 | } 377 | 378 | std::string name_; 379 | }; 380 | 381 | struct AcceptAST 382 | { 383 | bool operator () (const luna::SyntaxTree *) const 384 | { return true; } 385 | }; 386 | 387 | #endif // TEST_COMMON_H 388 | -------------------------------------------------------------------------------- /luna/LibIO.cpp: -------------------------------------------------------------------------------- 1 | #include "LibIO.h" 2 | #include "State.h" 3 | #include "String.h" 4 | #include "UserData.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace lib { 10 | namespace io { 11 | 12 | #define METATABLE_FILE "file" 13 | 14 | // For close userdata file 15 | void CloseFile(void *data) 16 | { 17 | std::fclose(reinterpret_cast(data)); 18 | } 19 | 20 | // Helper function for report strerror 21 | int PushError(luna::StackAPI &api) 22 | { 23 | api.PushNil(); 24 | api.PushString(std::strerror(errno)); 25 | return 2; 26 | } 27 | 28 | // Read by bytes for userdata file 29 | void ReadBytes(luna::StackAPI &api, std::FILE *file, int bytes) 30 | { 31 | if (bytes <= 0) 32 | api.PushString(""); 33 | else 34 | { 35 | std::vector buf(bytes); 36 | bytes = std::fread(&buf[0], sizeof(buf[0]), bytes, file); 37 | if (bytes == 0) 38 | api.PushNil(); 39 | else 40 | api.PushString(&buf[0], bytes); 41 | } 42 | } 43 | 44 | // Read by format for userdata file 45 | void ReadByFormat(luna::StackAPI &api, std::FILE *file, 46 | const std::string format) 47 | { 48 | if (format == "*n") 49 | { 50 | // Read a number 51 | double num = 0.0; 52 | if (std::fscanf(file, "%lg", &num) == 1) 53 | api.PushNumber(num); 54 | else 55 | api.PushNil(); 56 | } 57 | else if (format == "*a") 58 | { 59 | // Read total content of file 60 | auto cur = std::ftell(file); 61 | std::fseek(file, 0, SEEK_END); 62 | auto bytes = std::ftell(file) - cur; 63 | std::fseek(file, cur, SEEK_SET); 64 | if (bytes == 0) 65 | api.PushString(""); 66 | else 67 | { 68 | std::vector buf(bytes); 69 | bytes = std::fread(&buf[0], sizeof(buf[0]), bytes, file); 70 | api.PushString(&buf[0], bytes); 71 | } 72 | } 73 | else if (format == "*l" || format == "*L") 74 | { 75 | // Read line 76 | const std::size_t part = 128; 77 | const std::size_t part_count = part + 1; 78 | 79 | std::size_t count = 0; 80 | std::vector buf(part_count); 81 | do 82 | { 83 | if (!std::fgets(&buf[count], part_count, file)) 84 | { 85 | // EOF or an error occured 86 | // if count is 0, push a nil as end of file 87 | // otherwise, push the buf as string 88 | if (count == 0) 89 | api.PushNil(); 90 | else 91 | api.PushString(&buf[0], count); 92 | break; 93 | } 94 | else 95 | { 96 | auto size = buf.size(); 97 | while (count < size - 1 && 98 | buf[count] != '\n' && buf[count] != '\0') 99 | ++count; 100 | // buf[size - 1] must be '\0' 101 | if (count == size - 1) 102 | { 103 | // '\n' is not exist, extend buf to continue read 104 | buf.resize(size + part); 105 | } 106 | else 107 | { 108 | // If buf[count] is '\n', keep '\n' when 109 | // format is "*L" 110 | if (buf[count] == '\n' && format == "*L") ++count; 111 | api.PushString(&buf[0], count); 112 | break; 113 | } 114 | } 115 | } while (true); 116 | } 117 | else 118 | api.PushNil(); 119 | } 120 | 121 | int Close(luna::State *state) 122 | { 123 | luna::StackAPI api(state); 124 | if (!api.CheckArgs(1, luna::ValueT_UserData)) 125 | return 0; 126 | 127 | auto user_data = api.GetUserData(0); 128 | CloseFile(user_data->GetData()); 129 | user_data->MarkDestroyed(); 130 | return 0; 131 | } 132 | 133 | int Flush(luna::State *state) 134 | { 135 | luna::StackAPI api(state); 136 | if (!api.CheckArgs(1, luna::ValueT_UserData)) 137 | return 0; 138 | 139 | auto user_data = api.GetUserData(0); 140 | std::fflush(reinterpret_cast(user_data->GetData())); 141 | return 0; 142 | } 143 | 144 | int Read(luna::State *state) 145 | { 146 | luna::StackAPI api(state); 147 | if (!api.CheckArgs(1, luna::ValueT_UserData)) 148 | return 0; 149 | 150 | auto user_data = api.GetUserData(0); 151 | auto file = reinterpret_cast(user_data->GetData()); 152 | 153 | auto params = api.GetStackSize(); 154 | for (int i = 1; i < params; ++i) 155 | { 156 | auto type = api.GetValueType(i); 157 | if (type == luna::ValueT_Number) 158 | { 159 | auto bytes = static_cast(api.GetNumber(i)); 160 | ReadBytes(api, file, bytes); 161 | } 162 | else if (type == luna::ValueT_String) 163 | { 164 | auto format = api.GetString(i)->GetStdString(); 165 | ReadByFormat(api, file, format); 166 | } 167 | else 168 | api.PushNil(); 169 | } 170 | 171 | return params - 1; 172 | } 173 | 174 | int Seek(luna::State *state) 175 | { 176 | luna::StackAPI api(state); 177 | if (!api.CheckArgs(1, luna::ValueT_UserData, 178 | luna::ValueT_String, luna::ValueT_Number)) 179 | return 0; 180 | 181 | auto user_data = api.GetUserData(0); 182 | auto file = reinterpret_cast(user_data->GetData()); 183 | 184 | auto params = api.GetStackSize(); 185 | if (params > 1) 186 | { 187 | auto whence = api.GetString(1)->GetStdString(); 188 | long offset = 0; 189 | if (params > 2) 190 | offset = static_cast(api.GetNumber(2)); 191 | 192 | int res = 0; 193 | if (whence == "set") 194 | res = std::fseek(file, offset, SEEK_SET); 195 | else if (whence == "cur") 196 | res = std::fseek(file, offset, SEEK_CUR); 197 | else if (whence == "end") 198 | res = std::fseek(file, offset, SEEK_END); 199 | 200 | if (res != 0) 201 | return PushError(api); 202 | } 203 | 204 | auto pos = std::ftell(file); 205 | if (pos < 0) 206 | return PushError(api); 207 | 208 | api.PushNumber(pos); 209 | return 1; 210 | } 211 | 212 | int Setvbuf(luna::State *state) 213 | { 214 | luna::StackAPI api(state); 215 | if (!api.CheckArgs(2, luna::ValueT_UserData, 216 | luna::ValueT_String, luna::ValueT_Number)) 217 | return 0; 218 | 219 | auto user_data = api.GetUserData(0); 220 | auto mode = api.GetString(1)->GetStdString(); 221 | 222 | std::size_t size = BUFSIZ; 223 | if (api.GetStackSize() > 2) 224 | size = static_cast(api.GetNumber(2)); 225 | 226 | auto file = reinterpret_cast(user_data->GetData()); 227 | if (mode == "no") 228 | std::setvbuf(file, nullptr, _IONBF, 0); 229 | else if (mode == "full") 230 | std::setvbuf(file, nullptr, _IOFBF, size); 231 | else if (mode == "line") 232 | std::setvbuf(file, nullptr, _IOLBF, size); 233 | 234 | return 0; 235 | } 236 | 237 | int Write(luna::State *state) 238 | { 239 | luna::StackAPI api(state); 240 | if (!api.CheckArgs(1, luna::ValueT_UserData)) 241 | return 0; 242 | 243 | auto user_data = api.GetUserData(0); 244 | auto file = reinterpret_cast(user_data->GetData()); 245 | auto params = api.GetStackSize(); 246 | for (int i = 1; i < params; ++i) 247 | { 248 | auto type = api.GetValueType(i); 249 | if (type == luna::ValueT_String) 250 | { 251 | auto str = api.GetString(i); 252 | auto c_str = str->GetCStr(); 253 | auto len = str->GetLength(); 254 | if (std::fwrite(c_str, len, 1, file) != 1) 255 | return PushError(api); 256 | } 257 | else if (type == luna::ValueT_Number) 258 | { 259 | if (std::fprintf(file, "%.14g", api.GetNumber(i)) < 0) 260 | return PushError(api); 261 | } 262 | else 263 | { 264 | api.ArgTypeError(i, luna::ValueT_String); 265 | return 0; 266 | } 267 | } 268 | 269 | api.PushUserData(user_data); 270 | return 1; 271 | } 272 | 273 | int Open(luna::State *state) 274 | { 275 | luna::StackAPI api(state); 276 | if (!api.CheckArgs(1, luna::ValueT_String, luna::ValueT_String)) 277 | return 0; 278 | 279 | auto file_name = api.GetString(0); 280 | auto mode = "r"; 281 | 282 | if (api.GetStackSize() > 1) 283 | mode = api.GetString(1)->GetCStr(); 284 | 285 | auto file = std::fopen(file_name->GetCStr(), mode); 286 | if (!file) 287 | return PushError(api); 288 | 289 | auto user_data = state->NewUserData(); 290 | auto metatable = state->GetMetatable(METATABLE_FILE); 291 | user_data->Set(file, metatable); 292 | user_data->SetDestroyer(CloseFile); 293 | api.PushUserData(user_data); 294 | return 1; 295 | } 296 | 297 | int Stdin(luna::State *state) 298 | { 299 | luna::StackAPI api(state); 300 | auto user_data = state->NewUserData(); 301 | auto metatable = state->GetMetatable(METATABLE_FILE); 302 | user_data->Set(stdin, metatable); 303 | api.PushUserData(user_data); 304 | return 1; 305 | } 306 | 307 | int Stdout(luna::State *state) 308 | { 309 | luna::StackAPI api(state); 310 | auto user_data = state->NewUserData(); 311 | auto metatable = state->GetMetatable(METATABLE_FILE); 312 | user_data->Set(stdout, metatable); 313 | api.PushUserData(user_data); 314 | return 1; 315 | } 316 | 317 | int Stderr(luna::State *state) 318 | { 319 | luna::StackAPI api(state); 320 | auto user_data = state->NewUserData(); 321 | auto metatable = state->GetMetatable(METATABLE_FILE); 322 | user_data->Set(stderr, metatable); 323 | api.PushUserData(user_data); 324 | return 1; 325 | } 326 | 327 | void RegisterLibIO(luna::State *state) 328 | { 329 | luna::Library lib(state); 330 | luna::TableMemberReg file[] = { 331 | { "close", Close }, 332 | { "flush", Flush }, 333 | { "read", Read }, 334 | { "seek", Seek }, 335 | { "setvbuf", Setvbuf }, 336 | { "write", Write } 337 | }; 338 | 339 | lib.RegisterMetatable(METATABLE_FILE, file); 340 | 341 | luna::TableMemberReg io[] = { 342 | { "open", Open }, 343 | { "stdin", Stdin }, 344 | { "stdout", Stdout }, 345 | { "stderr", Stderr } 346 | }; 347 | 348 | lib.RegisterTableFunction("io", io); 349 | } 350 | 351 | } // namespace io 352 | } // namespace lib 353 | -------------------------------------------------------------------------------- /unittests/TestSemantic.cpp: -------------------------------------------------------------------------------- 1 | #include "UnitTest.h" 2 | #include "TestCommon.h" 3 | #include "luna/SemanticAnalysis.h" 4 | 5 | namespace 6 | { 7 | ParserWrapper g_parser; 8 | std::unique_ptr Semantic(const std::string &s) 9 | { 10 | g_parser.SetInput(s); 11 | auto ast = g_parser.Parse(); 12 | luna::SemanticAnalysis(ast.get(), g_parser.GetState()); 13 | return ast; 14 | } 15 | } // namespace 16 | 17 | TEST_CASE(semantic1) 18 | { 19 | auto ast = Semantic("a, b = c, d"); 20 | auto a = ASTFind(ast, FindName("a")); 21 | auto b = ASTFind(ast, FindName("b")); 22 | auto c = ASTFind(ast, FindName("c")); 23 | auto d = ASTFind(ast, FindName("d")); 24 | EXPECT_TRUE(a->semantic_ == luna::SemanticOp_Write); 25 | EXPECT_TRUE(b->semantic_ == luna::SemanticOp_Write); 26 | EXPECT_TRUE(c->semantic_ == luna::SemanticOp_Read); 27 | EXPECT_TRUE(d->semantic_ == luna::SemanticOp_Read); 28 | } 29 | 30 | TEST_CASE(semantic2) 31 | { 32 | auto ast = Semantic("f(a, b)"); 33 | auto f = ASTFind(ast, FindName("f")); 34 | auto a = ASTFind(ast, FindName("a")); 35 | auto b = ASTFind(ast, FindName("b")); 36 | EXPECT_TRUE(f->semantic_ == luna::SemanticOp_Read); 37 | EXPECT_TRUE(a->semantic_ == luna::SemanticOp_Read); 38 | EXPECT_TRUE(b->semantic_ == luna::SemanticOp_Read); 39 | } 40 | 41 | TEST_CASE(semantic3) 42 | { 43 | auto ast = Semantic("m:f(a, b)"); 44 | auto m = ASTFind(ast, FindName("m")); 45 | auto a = ASTFind(ast, FindName("a")); 46 | auto b = ASTFind(ast, FindName("b")); 47 | EXPECT_TRUE(m->semantic_ == luna::SemanticOp_Read); 48 | EXPECT_TRUE(a->semantic_ == luna::SemanticOp_Read); 49 | EXPECT_TRUE(b->semantic_ == luna::SemanticOp_Read); 50 | } 51 | 52 | TEST_CASE(semantic4) 53 | { 54 | auto ast = Semantic("t.m.n = a"); 55 | auto t_m_n = ASTFind(ast, [](luna::MemberAccessor *ma) { 56 | return ma->member_.str_->GetStdString() == "n"; 57 | }); 58 | auto t_m = ASTFind(ast, [](luna::MemberAccessor *ma) { 59 | return ma->member_.str_->GetStdString() == "m"; 60 | }); 61 | auto t = ASTFind(ast, FindName("t")); 62 | auto a = ASTFind(ast, FindName("a")); 63 | EXPECT_TRUE(t_m_n->semantic_ == luna::SemanticOp_Write); 64 | EXPECT_TRUE(t_m->semantic_ == luna::SemanticOp_Read); 65 | EXPECT_TRUE(t->semantic_ == luna::SemanticOp_Read); 66 | EXPECT_TRUE(a->semantic_ == luna::SemanticOp_Read); 67 | } 68 | 69 | TEST_CASE(semantic5) 70 | { 71 | auto ast = Semantic("t[i][j] = a"); 72 | auto t_i_j = ASTFind(ast, AcceptAST()); 73 | auto t_i = ASTFind(t_i_j->table_, AcceptAST()); 74 | auto t = ASTFind(ast, FindName("t")); 75 | auto i = ASTFind(ast, FindName("i")); 76 | auto j = ASTFind(ast, FindName("j")); 77 | auto a = ASTFind(ast, FindName("a")); 78 | EXPECT_TRUE(t_i_j->semantic_ == luna::SemanticOp_Write); 79 | EXPECT_TRUE(t_i->semantic_ == luna::SemanticOp_Read); 80 | EXPECT_TRUE(t->semantic_ == luna::SemanticOp_Read); 81 | EXPECT_TRUE(i->semantic_ == luna::SemanticOp_Read); 82 | EXPECT_TRUE(j->semantic_ == luna::SemanticOp_Read); 83 | EXPECT_TRUE(a->semantic_ == luna::SemanticOp_Read); 84 | } 85 | 86 | TEST_CASE(semantic6) 87 | { 88 | auto ast = Semantic("a = t.m.n"); 89 | auto t_m_n = ASTFind(ast, [](luna::MemberAccessor *ma) { 90 | return ma->member_.str_->GetStdString() == "n"; 91 | }); 92 | auto t_m = ASTFind(ast, [](luna::MemberAccessor *ma) { 93 | return ma->member_.str_->GetStdString() == "m"; 94 | }); 95 | auto t = ASTFind(ast, FindName("t")); 96 | auto a = ASTFind(ast, FindName("a")); 97 | EXPECT_TRUE(t_m_n->semantic_ == luna::SemanticOp_Read); 98 | EXPECT_TRUE(t_m->semantic_ == luna::SemanticOp_Read); 99 | EXPECT_TRUE(t->semantic_ == luna::SemanticOp_Read); 100 | EXPECT_TRUE(a->semantic_ == luna::SemanticOp_Write); 101 | } 102 | 103 | TEST_CASE(semantic7) 104 | { 105 | auto ast = Semantic("a = t[i][j]"); 106 | auto t_i_j = ASTFind(ast, AcceptAST()); 107 | auto t_i = ASTFind(t_i_j->table_, AcceptAST()); 108 | auto t = ASTFind(ast, FindName("t")); 109 | auto i = ASTFind(ast, FindName("i")); 110 | auto j = ASTFind(ast, FindName("j")); 111 | auto a = ASTFind(ast, FindName("a")); 112 | EXPECT_TRUE(t_i_j->semantic_ == luna::SemanticOp_Read); 113 | EXPECT_TRUE(t_i->semantic_ == luna::SemanticOp_Read); 114 | EXPECT_TRUE(t->semantic_ == luna::SemanticOp_Read); 115 | EXPECT_TRUE(i->semantic_ == luna::SemanticOp_Read); 116 | EXPECT_TRUE(j->semantic_ == luna::SemanticOp_Read); 117 | EXPECT_TRUE(a->semantic_ == luna::SemanticOp_Write); 118 | } 119 | 120 | TEST_CASE(semantic8) 121 | { 122 | auto ast = Semantic("t = { [i] = a, m = b, c }"); 123 | auto i = ASTFind(ast, FindName("i")); 124 | auto a = ASTFind(ast, FindName("a")); 125 | auto b = ASTFind(ast, FindName("b")); 126 | auto c = ASTFind(ast, FindName("c")); 127 | EXPECT_TRUE(i->semantic_ == luna::SemanticOp_Read); 128 | EXPECT_TRUE(a->semantic_ == luna::SemanticOp_Read); 129 | EXPECT_TRUE(b->semantic_ == luna::SemanticOp_Read); 130 | EXPECT_TRUE(c->semantic_ == luna::SemanticOp_Read); 131 | } 132 | 133 | TEST_CASE(semantic9) 134 | { 135 | EXPECT_TRUE(Semantic("a = 1 + 1")); 136 | EXPECT_TRUE(Semantic("a = 1 + b")); 137 | EXPECT_EXCEPTION(luna::SemanticException, { 138 | Semantic("a = 1 - {}"); 139 | }); 140 | EXPECT_EXCEPTION(luna::SemanticException, { 141 | Semantic("a = 1 * nil"); 142 | }); 143 | EXPECT_EXCEPTION(luna::SemanticException, { 144 | Semantic("a = 1 / true"); 145 | }); 146 | EXPECT_EXCEPTION(luna::SemanticException, { 147 | Semantic("a = 1 % 'str'"); 148 | }); 149 | EXPECT_EXCEPTION(luna::SemanticException, { 150 | Semantic("a = 1 ^ ..."); 151 | }); 152 | } 153 | 154 | TEST_CASE(semantic10) 155 | { 156 | EXPECT_TRUE(Semantic("a = 1 > 2")); 157 | EXPECT_TRUE(Semantic("a = 'str' >= 'str'")); 158 | EXPECT_TRUE(Semantic("a = 1 > b")); 159 | EXPECT_EXCEPTION(luna::SemanticException, { 160 | Semantic("a = 1 < 'str'"); 161 | }); 162 | EXPECT_EXCEPTION(luna::SemanticException, { 163 | Semantic("a = true <= false"); 164 | }); 165 | } 166 | 167 | TEST_CASE(semantic11) 168 | { 169 | EXPECT_TRUE(Semantic("a = 'str' .. 'str'")); 170 | EXPECT_TRUE(Semantic("a = 1 .. 'str'")); 171 | EXPECT_TRUE(Semantic("a = 'str' .. 1")); 172 | EXPECT_TRUE(Semantic("a = 'str' .. b")); 173 | EXPECT_EXCEPTION(luna::SemanticException, { 174 | Semantic("a = 1 .. 1"); 175 | }); 176 | EXPECT_EXCEPTION(luna::SemanticException, { 177 | Semantic("a = true .. nil"); 178 | }); 179 | } 180 | 181 | TEST_CASE(semantic12) 182 | { 183 | EXPECT_TRUE(Semantic("a = -(1 + 1)")); 184 | EXPECT_TRUE(Semantic("a = #{1, 2, 3}")); 185 | EXPECT_TRUE(Semantic("a = #'str'")); 186 | EXPECT_TRUE(Semantic("a = not a")); 187 | EXPECT_TRUE(Semantic("a = -a")); 188 | EXPECT_TRUE(Semantic("a = #a")); 189 | EXPECT_EXCEPTION(luna::SemanticException, { 190 | Semantic("a = -'str'"); 191 | }); 192 | EXPECT_EXCEPTION(luna::SemanticException, { 193 | Semantic("a = #1"); 194 | }); 195 | } 196 | 197 | TEST_CASE(semantic13) 198 | { 199 | EXPECT_TRUE(Semantic("a = -#{1, 2, 3} + 1")); 200 | EXPECT_EXCEPTION(luna::SemanticException, { 201 | Semantic("a = (1 > 2) + 1"); 202 | }); 203 | EXPECT_EXCEPTION(luna::SemanticException, { 204 | Semantic("a = (1 ~= 2) > 1"); 205 | }); 206 | EXPECT_EXCEPTION(luna::SemanticException, { 207 | Semantic("a = not a >= true"); 208 | }); 209 | } 210 | 211 | TEST_CASE(semantic14) 212 | { 213 | auto ast = Semantic("function test() " 214 | " local a = 1 " 215 | " a = f() " 216 | " return function() return a end " 217 | "end"); 218 | 219 | auto a = ASTFind(ast, FindName("a")); 220 | auto f = ASTFind(ast, FindName("f")); 221 | EXPECT_TRUE(a->scoping_ == luna::LexicalScoping_Local); 222 | EXPECT_TRUE(f->scoping_ == luna::LexicalScoping_Global); 223 | 224 | auto ret = ASTFind(ast, AcceptAST()); 225 | auto a2 = ASTFind(ret->exp_list_, FindName("a")); 226 | EXPECT_TRUE(a2->scoping_ == luna::LexicalScoping_Upvalue); 227 | } 228 | 229 | TEST_CASE(semantic15) 230 | { 231 | auto ast = Semantic("for i = 1, 10 do print(i) end"); 232 | auto i = ASTFind(ast, FindName("i")); 233 | EXPECT_TRUE(i->scoping_ == luna::LexicalScoping_Local); 234 | } 235 | 236 | TEST_CASE(semantic16) 237 | { 238 | auto ast = Semantic("for i, j in f() do print(i, j) end"); 239 | auto i = ASTFind(ast, FindName("i")); 240 | auto j = ASTFind(ast, FindName("j")); 241 | EXPECT_TRUE(i->scoping_ == luna::LexicalScoping_Local); 242 | EXPECT_TRUE(j->scoping_ == luna::LexicalScoping_Local); 243 | } 244 | 245 | TEST_CASE(semantic17) 246 | { 247 | auto ast = Semantic("repeat local i = 1 until i == 1"); 248 | auto i = ASTFind(ast, FindName("i")); 249 | EXPECT_TRUE(i->scoping_ == luna::LexicalScoping_Local); 250 | } 251 | 252 | TEST_CASE(semantic18) 253 | { 254 | auto ast = Semantic("while i == 1 do local i = 1 end"); 255 | auto i = ASTFind(ast, FindName("i")); 256 | EXPECT_TRUE(i->scoping_ == luna::LexicalScoping_Global); 257 | } 258 | 259 | TEST_CASE(semantic19) 260 | { 261 | auto ast = Semantic("if i == 1 then local i = 1 elseif j == 1 then local j = 1 end"); 262 | auto i = ASTFind(ast, FindName("i")); 263 | auto j = ASTFind(ast, FindName("j")); 264 | EXPECT_TRUE(i->scoping_ == luna::LexicalScoping_Global); 265 | EXPECT_TRUE(j->scoping_ == luna::LexicalScoping_Global); 266 | } 267 | 268 | TEST_CASE(semantic20) 269 | { 270 | EXPECT_EXCEPTION(luna::SemanticException, { 271 | Semantic("do break end"); 272 | }); 273 | 274 | EXPECT_EXCEPTION(luna::SemanticException, { 275 | Semantic("while true do local f = function() break end end"); 276 | }); 277 | 278 | auto ast = Semantic("while true do break end"); 279 | auto b = ASTFind(ast, AcceptAST()); 280 | auto w = ASTFind(ast, AcceptAST()); 281 | EXPECT_TRUE(b->loop_ == w); 282 | 283 | ast = Semantic("repeat break until true"); 284 | b = ASTFind(ast, AcceptAST()); 285 | auto r = ASTFind(ast, AcceptAST()); 286 | EXPECT_TRUE(b->loop_ == r); 287 | 288 | ast = Semantic("for i = 1, 10 do break end"); 289 | b = ASTFind(ast, AcceptAST()); 290 | auto nf = ASTFind(ast, AcceptAST()); 291 | EXPECT_TRUE(b->loop_ == nf); 292 | 293 | ast = Semantic("for k, v in pairs(t) do break end"); 294 | b = ASTFind(ast, AcceptAST()); 295 | auto gf = ASTFind(ast, AcceptAST()); 296 | EXPECT_TRUE(b->loop_ == gf); 297 | } 298 | 299 | TEST_CASE(semantic21) 300 | { 301 | EXPECT_TRUE(Semantic("function f(...) return ... end")); 302 | EXPECT_EXCEPTION(luna::SemanticException, { 303 | Semantic("function f(...) return function() return ... end end"); 304 | }); 305 | } 306 | --------------------------------------------------------------------------------