├── .gitignore ├── README.md ├── platform.hh ├── loxcc.hh ├── bytecc_loxcc.hh ├── interpret_errors.cc ├── bytecc_compiler.hh ├── interpret_loxcc.cc ├── interpret_loxcc.hh ├── bytecc_loxcc.cc ├── interpret_builtins.hh ├── interpret_builtins.cc ├── main.cc ├── interpret_environment.cc ├── loxcc.cc ├── interpret_errors.hh ├── token.cc ├── interpret_value.cc ├── kinds_def.hh ├── interpret_parser.hh ├── interpret_environment.hh ├── bytecc_codes.hh ├── lexer.hh ├── bytecc_chunk.hh ├── CMakeLists.txt ├── token.hh ├── interpret_ast.cc ├── bytecc_vm.hh ├── interpret_resolver.hh ├── interpret_callable.hh ├── interpret_callable.cc ├── common.hh ├── interpret_interpreter.hh ├── lexer.cc ├── bytecc_chunk.cc ├── interpret_value.hh ├── interpret_resolver.cc ├── interpret_interpreter.cc ├── bytecc_value.cc ├── interpret_ast.hh ├── interpret_parser.cc ├── bytecc_value.hh └── bytecc_vm.cc /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.o 3 | *.a 4 | *.so 5 | *.exe 6 | *.lib 7 | *.dll 8 | *.obj 9 | *.pdb 10 | *.manifest 11 | *.exp 12 | *.ilk 13 | *.idb 14 | *.pyc 15 | *.pyo 16 | *.out 17 | *.conf 18 | *.ycm_extra_conf.py 19 | Makefile 20 | build 21 | cmake-build 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **LOXCC** 2 | 3 | This project follows Bob Nystrom's excellent book [**"Crafting Interpreters"**](http://www.craftinginterpreters.com/). 4 | 5 | A C++ port of [jlox](https://github.com/munificent/craftinginterpreters/tree/master/java/com/craftinginterpreters)(Lox language's AST interpreter) and [clox](https://github.com/munificent/craftinginterpreters/tree/master/c)(Lox language's bytecode VM). 6 | -------------------------------------------------------------------------------- /platform.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #if defined(LOXCC_UNUSED) 30 | # define LOXCC_UNUSED(x) ((void)x) 31 | #endif 32 | 33 | #if defined(__GNUC__) || defined(__clang__) 34 | # define LOXCC_GNUC 35 | #else 36 | # define LOXCC_MSVC 37 | #endif 38 | 39 | #if !defined(interface) 40 | # define interface struct 41 | #endif 42 | -------------------------------------------------------------------------------- /loxcc.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include "common.hh" 30 | 31 | namespace loxcc { 32 | 33 | class BaseLoxcc : private UnCopyable { 34 | void eval_with_repl(void); 35 | void eval_with_file(const str_t& fname); 36 | 37 | virtual int eval_impl(const str_t& source_bytes) = 0; 38 | public: 39 | void eval(int argc, char** argv); 40 | }; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /bytecc_loxcc.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include "loxcc.hh" 31 | 32 | namespace loxcc::bytecc { 33 | 34 | class VM; 35 | 36 | class Loxcc final : public BaseLoxcc { 37 | std::shared_ptr vm_; 38 | 39 | virtual int eval_impl(const str_t& source_bytes) override; 40 | public: 41 | Loxcc(void) noexcept; 42 | }; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /interpret_errors.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include 28 | #include "interpret_errors.hh" 29 | 30 | namespace loxcc::interpret { 31 | 32 | void ErrorReport::report(int lineno, const str_t& where, const str_t& message) { 33 | std::cerr 34 | << "[LINE: " << lineno << "]: error: " 35 | << where << ": " << message << std::endl; 36 | had_error_ = true; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /bytecc_compiler.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include "common.hh" 31 | 32 | namespace loxcc::bytecc { 33 | 34 | class FunctionObject; 35 | class GlobalParser; 36 | class VM; 37 | 38 | class GlobalCompiler final : private UnCopyable { 39 | std::shared_ptr gparser_{}; 40 | public: 41 | FunctionObject* compile(VM& vm, const str_t& source_bytes); 42 | void mark_compiler(void); 43 | }; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /interpret_loxcc.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include "interpret_interpreter.hh" 28 | #include "interpret_loxcc.hh" 29 | 30 | namespace loxcc::interpret { 31 | 32 | Loxcc::Loxcc(void) noexcept 33 | : err_report_() 34 | , interp_(new Interpreter(err_report_)) { 35 | } 36 | 37 | int Loxcc::eval_impl(const str_t& source_bytes) { 38 | interp_->interpret(source_bytes); 39 | if (err_report_.had_error()) 40 | return -1; 41 | return 0; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /interpret_loxcc.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include "loxcc.hh" 31 | #include "interpret_errors.hh" 32 | 33 | namespace loxcc::interpret { 34 | 35 | class Interpreter; 36 | 37 | class Loxcc final : public BaseLoxcc { 38 | ErrorReport err_report_; 39 | std::shared_ptr interp_; 40 | 41 | virtual int eval_impl(const str_t& source_bytes) override; 42 | public: 43 | Loxcc(void) noexcept; 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /bytecc_loxcc.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include "bytecc_vm.hh" 28 | #include "bytecc_loxcc.hh" 29 | 30 | namespace loxcc::bytecc { 31 | 32 | Loxcc::Loxcc(void) noexcept 33 | : vm_(new VM()) { 34 | } 35 | 36 | int Loxcc::eval_impl(const str_t& source_bytes) { 37 | InterpretRet r = vm_->interpret(source_bytes); 38 | if (r == InterpretRet::COMPILE_ERR) 39 | return -2; 40 | if (r == InterpretRet::RUNTIME_ERR) 41 | return -3; 42 | return 0; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /interpret_builtins.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include "interpret_callable.hh" 30 | 31 | namespace loxcc::interpret { 32 | 33 | // Nat -> Native callable type prefix 34 | class NatClock 35 | : public Callable, public std::enable_shared_from_this { 36 | public: 37 | virtual Value call( 38 | const InterpreterPtr& interp, const std::vector& args) override; 39 | virtual str_t stringify(void) const override; 40 | }; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /interpret_builtins.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include 28 | #include "interpret_builtins.hh" 29 | 30 | namespace loxcc::interpret { 31 | 32 | Value NatClock::call( 33 | const InterpreterPtr& interp, const std::vector& args) { 34 | // return seconds of now 35 | return std::chrono::duration_cast( 36 | std::chrono::system_clock::now().time_since_epoch()).count() / 1000.0; 37 | } 38 | 39 | str_t NatClock::stringify(void) const { 40 | return ""; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /main.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include 28 | #include "bytecc_loxcc.hh" 29 | #include "interpret_loxcc.hh" 30 | 31 | int main(int argc, char* argv[]) { 32 | (void)argc, (void)argv; 33 | 34 | std::cout << "L O X - C C" << std::endl; 35 | 36 | auto help_fn = [](const char* app) { 37 | std::cerr << "USAGE: " << app << " [i|c] [FILE_NAME]" << std::endl; 38 | std::exit(1); 39 | }; 40 | 41 | if (argc < 2) { 42 | help_fn(argv[0]); 43 | } 44 | else { 45 | switch (argv[1][0]) { 46 | case 'i': 47 | { 48 | std::cout << "Loxcc Interpreter RUNNING ..." << std::endl; 49 | loxcc::interpret::Loxcc lox; 50 | lox.eval(argc, argv); 51 | } break; 52 | case 'c': 53 | { 54 | std::cout << "Loxcc VM RUNNING ..." << std::endl; 55 | loxcc::bytecc::Loxcc lox; 56 | lox.eval(argc, argv); 57 | } break; 58 | default: help_fn(argv[0]); break; 59 | } 60 | } 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /interpret_environment.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include "interpret_errors.hh" 28 | #include "interpret_environment.hh" 29 | 30 | namespace loxcc::interpret { 31 | 32 | EnvironmentPtr Environment::ancestor(int distance) { 33 | auto envp(shared_from_this()); 34 | for (int i = 0; i < distance; ++i) 35 | envp = envp->enclosing_; 36 | return envp; 37 | } 38 | 39 | const Value& Environment::get(const Token& name) const { 40 | if (auto val_iter = values_.find(name.literal()); val_iter != values_.end()) 41 | return val_iter->second; 42 | 43 | if (enclosing_) 44 | return enclosing_->get(name); 45 | 46 | throw RuntimeError(name, "undefined variable `" + name.literal() + "`"); 47 | } 48 | 49 | void Environment::assign(const Token& name, const Value& value) { 50 | if (auto val_iter = values_.find(name.literal()); val_iter != values_.end()) { 51 | val_iter->second = value; 52 | return; 53 | } 54 | 55 | if (enclosing_) { 56 | enclosing_->assign(name, value); 57 | return; 58 | } 59 | 60 | throw RuntimeError(name, "undefine variable `" + name.literal() + "`"); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /loxcc.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include 28 | #include 29 | #include 30 | #include "loxcc.hh" 31 | 32 | namespace loxcc { 33 | 34 | void BaseLoxcc::eval(int argc, char** argv) { 35 | if (argc == 2) { 36 | eval_with_repl(); 37 | } 38 | else if (argc == 3) { 39 | eval_with_file(argv[2]); 40 | } 41 | else { 42 | std::cerr << "USAGE: " << argv[0] << " [i|c] [FILE_NAME]" << std::endl; 43 | std::exit(-1); 44 | } 45 | } 46 | 47 | void BaseLoxcc::eval_with_repl(void) { 48 | str_t line; 49 | for (;;) { 50 | std::cout << ">>> "; 51 | 52 | if (!std::getline(std::cin, line) || line == "exit") 53 | break; 54 | 55 | eval_impl(line); 56 | } 57 | } 58 | 59 | void BaseLoxcc::eval_with_file(const str_t& fname) { 60 | std::fstream fp(fname); 61 | if (fp.is_open()) { 62 | std::stringstream ss; 63 | ss << fp.rdbuf(); 64 | 65 | if (int ec = eval_impl(ss.str()); ec != 0) 66 | std::exit(ec); 67 | } 68 | else { 69 | std::cerr << "ERROR: LOAD `" << fname << "` FAILED ..." << std::endl; 70 | std::exit(-1); 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /interpret_errors.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include "common.hh" 31 | #include "token.hh" 32 | 33 | namespace loxcc::interpret { 34 | 35 | class ErrorReport final : private UnCopyable { 36 | bool had_error_{}; 37 | 38 | void report(int lineno, const str_t& where, const str_t& message); 39 | public: 40 | inline bool had_error(void) const { return had_error_; } 41 | inline void reset_error(void) { had_error_ = false; } 42 | 43 | inline void error(int lineno, const str_t& message) { 44 | report(lineno, "", message); 45 | } 46 | 47 | inline void error(const Token& tok, const str_t& message) { 48 | report(tok.lineno(), tok.literal(), message); 49 | } 50 | }; 51 | 52 | class RuntimeError final : public Copyable, public std::exception { 53 | Token token_; 54 | str_t message_; 55 | 56 | inline const char* what(void) const throw() { return message_.c_str(); } 57 | public: 58 | RuntimeError(const Token& tok, const str_t& msg) noexcept 59 | : token_(tok), message_(msg) { 60 | } 61 | 62 | inline const Token& token(void) const { return token_; } 63 | inline const str_t& message(void) const { return message_; } 64 | }; 65 | 66 | } 67 | -------------------------------------------------------------------------------- /token.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include 28 | #include 29 | #include 30 | #include "token.hh" 31 | 32 | namespace loxcc { 33 | 34 | static constexpr const char* kNames[] = { 35 | #undef TOKDEF 36 | #define TOKDEF(k, s) s, 37 | #include "kinds_def.hh" 38 | nullptr, 39 | }; 40 | 41 | static const std::unordered_map kKeywords = { 42 | #undef KEYWORD 43 | #define KEYWORD(k, s) {s, TokenKind::KW_##k}, 44 | #include "kinds_def.hh" 45 | }; 46 | 47 | const char* get_token_name(TokenKind kind) noexcept { 48 | if (kind >= TokenKind::TK_LPAREN && kind < TokenKind::KINDS_END) 49 | return kNames[Xt::as_type(kind)]; 50 | return nullptr; 51 | } 52 | 53 | TokenKind get_keyword_kind(const str_t& kw) noexcept { 54 | if (auto kw_iter = kKeywords.find(kw); kw_iter != kKeywords.end()) 55 | return kw_iter->second; 56 | return TokenKind::TK_IDENTIFIER; 57 | } 58 | 59 | str_t Token::stringify(void) const noexcept { 60 | std::stringstream ss; 61 | 62 | ss << std::left << std::setw(20) << get_token_name(kind_) << "|" 63 | << std::right << std::setw(24) << literal_ << "|" 64 | << std::right << std::setw(04) << lineno_; 65 | return ss.str(); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /interpret_value.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include "interpret_callable.hh" 28 | #include "interpret_value.hh" 29 | 30 | namespace loxcc::interpret { 31 | 32 | template struct overloaded : Ts... { using Ts::operator()...; }; 33 | template overloaded(Ts...) -> overloaded; 34 | 35 | bool Value::is_truthy(void) const { 36 | return std::visit(overloaded { 37 | [](nil_t) -> bool { return false; }, 38 | [](bool b) -> bool { return b; }, 39 | [](double d) -> bool { return d != 0.f; }, 40 | [](const str_t& s) -> bool { return !s.empty(); }, 41 | [](const CallablePtr&) -> bool { return true; }, 42 | [](const InstancePtr&) -> bool { return true; }, 43 | }, v_); 44 | } 45 | 46 | str_t Value::stringify(void) const { 47 | return std::visit(overloaded { 48 | [](nil_t) -> str_t { return "nil"; }, 49 | [](bool b) -> str_t { return b ? "true" : "false"; }, 50 | [](double d) -> str_t { return Xt::to_string(d); }, 51 | [](const str_t& s) -> str_t { return s; }, 52 | [](const CallablePtr& c) -> str_t { return c->stringify(); }, 53 | [](const InstancePtr& i) -> str_t { return i->stringify(); }, 54 | }, v_); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /kinds_def.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #ifndef TOKDEF 29 | # define TOKDEF(k, s) 30 | #endif 31 | 32 | #ifndef TOKEN 33 | # define TOKEN(k, s) TOKDEF(TK_##k, s) 34 | #endif 35 | 36 | #ifndef PUNCTUATOR 37 | # define PUNCTUATOR(k, s) TOKEN(k, s) 38 | #endif 39 | 40 | #ifndef KEYWORD 41 | # define KEYWORD(k, s) TOKDEF(KW_##k, s) 42 | #endif 43 | 44 | // single-charactor punctuators 45 | PUNCTUATOR(LPAREN, "(") 46 | PUNCTUATOR(RPAREN, ")") 47 | PUNCTUATOR(LBRACE, "{") 48 | PUNCTUATOR(RBRACE, "}") 49 | PUNCTUATOR(COMMA, ",") 50 | PUNCTUATOR(DOT, ".") 51 | PUNCTUATOR(MINUS, "-") 52 | PUNCTUATOR(PLUS, "+") 53 | PUNCTUATOR(SEMI, ";") 54 | PUNCTUATOR(SLASH, "/") 55 | PUNCTUATOR(STAR, "*") 56 | 57 | // one or two charactor punctuators 58 | PUNCTUATOR(BANG, "!") 59 | PUNCTUATOR(BANGEQ, "!=") 60 | PUNCTUATOR(EQ, "=") 61 | PUNCTUATOR(EQEQ, "==") 62 | PUNCTUATOR(GT, ">") 63 | PUNCTUATOR(GTEQ, ">=") 64 | PUNCTUATOR(LT, "<") 65 | PUNCTUATOR(LTEQ, "<=") 66 | 67 | // language tokens 68 | TOKEN(IDENTIFIER, "identifier") 69 | TOKEN(NUMERIC, "numeric") 70 | TOKEN(STRING, "string") 71 | 72 | // keywords 73 | KEYWORD(AND, "and") 74 | KEYWORD(CLASS, "class") 75 | KEYWORD(ELSE, "else") 76 | KEYWORD(FALSE, "false") 77 | KEYWORD(FOR, "for") 78 | KEYWORD(FUN, "fun") 79 | KEYWORD(IF, "if") 80 | KEYWORD(NIL, "nil") 81 | KEYWORD(OR, "or") 82 | KEYWORD(PRINT, "print") 83 | KEYWORD(RETURN, "return") 84 | KEYWORD(SUPER, "super") 85 | KEYWORD(THIS, "this") 86 | KEYWORD(TRUE, "true") 87 | KEYWORD(VAR, "var") 88 | KEYWORD(WHILE, "while") 89 | 90 | TOKEN(EOF, "eof") 91 | TOKEN(ERR, "error") 92 | 93 | #undef KEYWORD 94 | #undef PUNCTUATOR 95 | #undef TOKEN 96 | #undef TOKDEF 97 | -------------------------------------------------------------------------------- /interpret_parser.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include "common.hh" 31 | #include "token.hh" 32 | #include "lexer.hh" 33 | #include "interpret_ast.hh" 34 | 35 | namespace loxcc::interpret { 36 | 37 | class ErrorReport; 38 | 39 | // recursive descent parsing 40 | class Parser final : private UnCopyable { 41 | ErrorReport& err_report_; 42 | Lexer& lex_; 43 | Token prev_; 44 | Token curr_; 45 | 46 | static constexpr sz_t kMaxArguments = 8; 47 | 48 | inline bool is_end(void) const { return curr_.kind() == TokenKind::TK_EOF; } 49 | 50 | Token advance(void); 51 | bool check(TokenKind kind) const; 52 | bool match(TokenKind kind); 53 | bool match(const std::initializer_list& kinds); 54 | Token consume(TokenKind kind, const str_t& message); 55 | void synchronize(void); 56 | 57 | StmtPtr declaration(void); 58 | StmtPtr class_decl(void); 59 | StmtPtr fun_decl(const str_t& kind); 60 | StmtPtr var_decl(void); 61 | StmtPtr statement(void); 62 | StmtPtr expr_stmt(void); 63 | StmtPtr for_stmt(void); 64 | StmtPtr if_stmt(void); 65 | StmtPtr print_stmt(void); 66 | StmtPtr return_stmt(void); 67 | StmtPtr while_stmt(void); 68 | std::vector block_stmt(void); 69 | 70 | ExprPtr expression(void); 71 | ExprPtr assignment(void); 72 | ExprPtr logical_or(void); 73 | ExprPtr logical_and(void); 74 | ExprPtr equality(void); 75 | ExprPtr comparison(void); 76 | ExprPtr addition(void); 77 | ExprPtr multiplication(void); 78 | ExprPtr unary(void); 79 | ExprPtr call(void); 80 | ExprPtr primary(void); 81 | public: 82 | Parser(ErrorReport& err_report, Lexer& lex) noexcept 83 | : err_report_(err_report), lex_(lex) { 84 | (void)advance(); 85 | } 86 | 87 | StmtPtr parse(void); 88 | }; 89 | 90 | } 91 | -------------------------------------------------------------------------------- /interpret_environment.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include "common.hh" 32 | #include "token.hh" 33 | #include "interpret_value.hh" 34 | 35 | namespace loxcc::interpret { 36 | 37 | class Environment; 38 | using EnvironmentPtr = std::shared_ptr; 39 | 40 | class Environment final 41 | : private UnCopyable, public std::enable_shared_from_this { 42 | EnvironmentPtr enclosing_{}; 43 | std::unordered_map values_; 44 | 45 | EnvironmentPtr ancestor(int distance); 46 | public: 47 | Environment(void) noexcept {} 48 | Environment(const EnvironmentPtr& enclosing) noexcept : enclosing_(enclosing) {} 49 | 50 | inline EnvironmentPtr get_enclosing(void) const { 51 | return enclosing_; 52 | } 53 | 54 | inline void define(const str_t& name, const Value& value) { 55 | values_[name] = value; 56 | } 57 | 58 | inline void define(const Token& name, const Value& value) { 59 | values_[name.literal()] = value; 60 | } 61 | 62 | inline const Value& get(const str_t& name) const { 63 | return get(Token::make_from_literal(name)); 64 | } 65 | 66 | inline const Value& get_at(int distance, const str_t& name) { 67 | return ancestor(distance)->get(name); 68 | } 69 | 70 | inline const Value& get_at(int distance, const Token& name) { 71 | return ancestor(distance)->get(name); 72 | } 73 | 74 | inline void assign(const str_t& name, const Value& value) { 75 | assign(Token::make_from_literal(name), value); 76 | } 77 | 78 | inline void assign_at(int distance, const str_t& name, const Value& value) { 79 | ancestor(distance)->assign(name, value); 80 | } 81 | 82 | inline void assign_at(int distance, const Token& name, const Value& value) { 83 | ancestor(distance)->assign(name, value); 84 | } 85 | 86 | const Value& get(const Token& name) const; 87 | void assign(const Token& name, const Value& value); 88 | }; 89 | 90 | } 91 | -------------------------------------------------------------------------------- /bytecc_codes.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #ifndef BYTECC_CODEF 29 | # define BYTECC_CODEF(c) 30 | #endif 31 | 32 | #ifndef BYTECC_CODE 33 | # define BYTECC_CODE(c) BYTECC_CODEF(c) 34 | #endif 35 | 36 | BYTECC_CODE(CONSTANT) 37 | 38 | BYTECC_CODE(NIL) 39 | BYTECC_CODE(TRUE) 40 | BYTECC_CODE(FALSE) 41 | BYTECC_CODE(POP) 42 | 43 | BYTECC_CODE(DEF_GLOBAL) 44 | BYTECC_CODE(GET_GLOBAL) 45 | BYTECC_CODE(SET_GLOBAL) 46 | BYTECC_CODE(GET_LOCAL) 47 | BYTECC_CODE(SET_LOCAL) 48 | BYTECC_CODE(GET_UPVALUE) 49 | BYTECC_CODE(SET_UPVALUE) 50 | BYTECC_CODE(GET_ATTR) 51 | BYTECC_CODE(SET_ATTR) 52 | 53 | BYTECC_CODE(GET_SUPER) 54 | 55 | BYTECC_CODE(EQ) 56 | BYTECC_CODE(NE) 57 | BYTECC_CODE(GT) 58 | BYTECC_CODE(GE) 59 | BYTECC_CODE(LT) 60 | BYTECC_CODE(LE) 61 | 62 | BYTECC_CODE(ADD) 63 | BYTECC_CODE(SUB) 64 | BYTECC_CODE(MUL) 65 | BYTECC_CODE(DIV) 66 | BYTECC_CODE(NOT) 67 | BYTECC_CODE(NEG) 68 | 69 | BYTECC_CODE(PRINT) 70 | 71 | BYTECC_CODE(JUMP) 72 | BYTECC_CODE(JUMP_IF_FALSE) 73 | BYTECC_CODE(LOOP) 74 | 75 | // calls and functions 76 | BYTECC_CODE(CALL_0) 77 | BYTECC_CODE(CALL_1) 78 | BYTECC_CODE(CALL_2) 79 | BYTECC_CODE(CALL_3) 80 | BYTECC_CODE(CALL_4) 81 | BYTECC_CODE(CALL_5) 82 | BYTECC_CODE(CALL_6) 83 | BYTECC_CODE(CALL_7) 84 | BYTECC_CODE(CALL_8) 85 | 86 | // methods and initializers 87 | BYTECC_CODE(INVOKE_0) 88 | BYTECC_CODE(INVOKE_1) 89 | BYTECC_CODE(INVOKE_2) 90 | BYTECC_CODE(INVOKE_3) 91 | BYTECC_CODE(INVOKE_4) 92 | BYTECC_CODE(INVOKE_5) 93 | BYTECC_CODE(INVOKE_6) 94 | BYTECC_CODE(INVOKE_7) 95 | BYTECC_CODE(INVOKE_8) 96 | 97 | // superclasses 98 | BYTECC_CODE(SUPER_0) 99 | BYTECC_CODE(SUPER_1) 100 | BYTECC_CODE(SUPER_2) 101 | BYTECC_CODE(SUPER_3) 102 | BYTECC_CODE(SUPER_4) 103 | BYTECC_CODE(SUPER_5) 104 | BYTECC_CODE(SUPER_6) 105 | BYTECC_CODE(SUPER_7) 106 | BYTECC_CODE(SUPER_8) 107 | 108 | BYTECC_CODE(CLOSURE) 109 | BYTECC_CODE(CLOSE_UPVALUE) 110 | BYTECC_CODE(RETURN) 111 | BYTECC_CODE(CLASS) 112 | BYTECC_CODE(SUBCLASS) 113 | BYTECC_CODE(METHOD) 114 | 115 | #undef BYTECC_CODE 116 | #undef BYTECC_CODEF 117 | -------------------------------------------------------------------------------- /lexer.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include "common.hh" 31 | #include "token.hh" 32 | 33 | namespace loxcc { 34 | 35 | class Lexer final : private UnCopyable { 36 | const str_t& source_bytes_; 37 | sz_t begpos_{}; 38 | sz_t curpos_{}; 39 | int lineno_{1}; 40 | 41 | inline bool isalpha(char c) const noexcept { 42 | return std::isalpha(c) || c == '_'; 43 | } 44 | 45 | inline bool isalnum(char c) const noexcept { 46 | return std::isalnum(c) || c == '_'; 47 | } 48 | 49 | inline bool isdigit(char c) const noexcept { 50 | return std::isdigit(c); 51 | } 52 | 53 | inline str_t gen_literal(sz_t begpos, sz_t endpos) const noexcept { 54 | return source_bytes_.substr(begpos, endpos - begpos); 55 | } 56 | 57 | inline bool is_end(void) const noexcept { 58 | return curpos_ >= source_bytes_.size(); 59 | } 60 | 61 | inline char advance(void) noexcept { 62 | return source_bytes_[curpos_++]; 63 | } 64 | 65 | inline bool match(char expected) noexcept { 66 | if (is_end() || source_bytes_[curpos_] != expected) 67 | return false; 68 | 69 | advance(); 70 | return true; 71 | } 72 | 73 | inline char peek(void) const noexcept { 74 | return curpos_ >= source_bytes_.size() ? 0 : source_bytes_[curpos_]; 75 | } 76 | 77 | inline char peek_next(void) const noexcept { 78 | return curpos_ + 1 >= source_bytes_.size() ? 0 : source_bytes_[curpos_ + 1]; 79 | } 80 | 81 | inline Token make_token(TokenKind kind) const noexcept { 82 | return Token(kind, gen_literal(begpos_, curpos_), lineno_); 83 | } 84 | 85 | inline Token make_token(TokenKind kind, const str_t& literal) const noexcept { 86 | return Token(kind, literal, lineno_); 87 | } 88 | 89 | inline Token make_error(const str_t& message) const noexcept { 90 | return Token(TokenKind::TK_ERR, message, lineno_); 91 | } 92 | 93 | void skip_whitespace(void); 94 | Token make_identifier(void); 95 | Token make_numeric(void); 96 | Token make_string(void); 97 | public: 98 | explicit Lexer(const str_t& source_bytes) noexcept 99 | : source_bytes_(source_bytes) { 100 | } 101 | 102 | Token next_token(void); 103 | }; 104 | 105 | } 106 | -------------------------------------------------------------------------------- /bytecc_chunk.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include "common.hh" 33 | #include "bytecc_value.hh" 34 | 35 | namespace loxcc::bytecc { 36 | 37 | enum class Code : u8_t { 38 | #undef BYTECC_CODEF 39 | #define BYTECC_CODEF(c) c, 40 | #include "bytecc_codes.hh" 41 | }; 42 | 43 | inline u8_t operator+(Code a, Code b) noexcept { 44 | return Xt::as_type(a) + Xt::as_type(b); 45 | } 46 | 47 | inline u8_t operator-(Code a, Code b) noexcept { 48 | return Xt::as_type(a) - Xt::as_type(b); 49 | } 50 | 51 | inline Code operator+(Code c, u8_t n) noexcept { 52 | return Xt::as_type(Xt::as_type(c) + n); 53 | } 54 | 55 | inline Code operator-(Code c, u8_t n) noexcept { 56 | return Xt::as_type(Xt::as_type(c) - n); 57 | } 58 | 59 | inline std::ostream& operator<<(std::ostream& out, Code c) noexcept { 60 | return out << Xt::as_type(c); 61 | } 62 | 63 | class Chunk final : private UnCopyable { 64 | std::vector codes_; 65 | std::vector lines_; 66 | std::vector constants_; 67 | public: 68 | template inline void write(T code, int lineno) noexcept { 69 | codes_.push_back(Xt::as_type(code)); 70 | lines_.push_back(lineno); 71 | } 72 | 73 | inline u8_t add_constant(const Value& value) noexcept { 74 | constants_.push_back(value); 75 | return Xt::as_type(constants_.size() - 1); 76 | } 77 | 78 | inline int codes_count(void) const noexcept { return Xt::as_type(codes_.size()); } 79 | inline const u8_t* codes(void) const noexcept { return codes_.data(); } 80 | inline u8_t get_code(int i) const noexcept { return codes_[i]; } 81 | inline void set_code(int i, u8_t c) noexcept { codes_[i] = c; } 82 | inline int get_line(int i) const noexcept { return lines_[i]; } 83 | inline int constants_count(void) const noexcept { return Xt::as_type(constants_.size()); } 84 | inline const Value* constants(void) const noexcept { return constants_.data(); } 85 | inline const Value& get_constant(int i) const noexcept { return constants_[i]; } 86 | 87 | inline void iter_constants(std::function&& visitor) { 88 | for (auto& c : constants_) 89 | visitor(c); 90 | } 91 | 92 | void dis(const str_t& name); 93 | int dis_ins(int offset); 94 | }; 95 | 96 | } 97 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 ASMlover. All rights reserved. 2 | # 3 | # Redistribution and use in source and binary forms, with or without 4 | # modification, are permitted provided that the following conditions 5 | # are met: 6 | # 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list ofconditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in 12 | # the documentation and/or other materialsprovided with the 13 | # distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | # POSSIBILITY OF SUCH DAMAGE. 27 | 28 | cmake_minimum_required(VERSION 3.14) 29 | project(loxcc LANGUAGES CXX) 30 | 31 | if (NOT CMAKE_BUILD_TYPE) 32 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose build type [Debug|Release|RelWithDebInfo|MinSizeRel]" FORCE) 33 | endif() 34 | message(STATUS "`${PROJECT_NAME}` CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") 35 | 36 | option(TRACE_EXEC "Enable/Disable trace bytecode executation" OFF) 37 | option(TRACE_GC "Enable/Disable trace GC" OFF) 38 | option(NAN_TAGGING "Enable/Disable uses a NaN-tagged double" OFF) 39 | option(COMPUTED_GOTOS "Enable/Disable uses computed gotos for VM's interpreter loop" OFF) 40 | 41 | if (TRACE_EXEC) 42 | add_definitions(-DTRACE_EXEC) 43 | endif() 44 | if (TRACE_GC) 45 | add_definitions(-DTRACE_GC) 46 | endif() 47 | if (NAN_TAGGING) 48 | add_definitions(-DNAN_TAGGING) 49 | endif() 50 | if (COMPUTED_GOTOS) 51 | add_definitions(-DCOMPUTED_GOTOS) 52 | endif() 53 | 54 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 55 | add_definitions( 56 | /DUNICODE 57 | /D_UNICODE 58 | /D_CRT_SECURE_NO_WARNINGS 59 | /D_CRT_NONSTDC_NO_WARNINGS) 60 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /GF /GS /Gs /Zi /EHsc /std:c++latest /await") 61 | set(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Od") 62 | set(CMAKE_CXX_FLAGS_RELEASE "/DNDEBUG /MD /Ox") 63 | else() 64 | add_definitions( 65 | -Wno-unused-function 66 | -Wno-switch) 67 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -std=c++2a -march=native") 68 | set(CMAKE_CXX_FLAGS_DEBUG "-g -ggdb -O0") 69 | set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3") 70 | 71 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 72 | add_definitions( 73 | -Wno-unused-private-field) 74 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") 75 | else() 76 | add_definitions( 77 | -Wno-format-truncation) 78 | endif() 79 | endif() 80 | string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE) 81 | message(STATUS "`${PROJECT_NAME}` CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BUILD_TYPE}}") 82 | 83 | file(GLOB_RECURSE LOXCC_HEADERS *.hh) 84 | file(GLOB_RECURSE LOXCC_SOURCES *.cc) 85 | source_group("Header Files" FILES ${LOXCC_HEADERS}) 86 | source_group("Source Files" FILES ${LOXCC_SOURCES}) 87 | message(STATUS "`${PROJECT_NAME}` LOXCC_HEADERS: ${LOXCC_HEADERS}") 88 | message(STATUS "`${PROJECT_NAME}` LOXCC_SOURCES: ${LOXCC_SOURCES}") 89 | 90 | add_executable(${PROJECT_NAME} ${LOXCC_HEADERS} ${LOXCC_SOURCES}) 91 | -------------------------------------------------------------------------------- /token.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include "common.hh" 31 | 32 | namespace loxcc { 33 | 34 | enum class TokenKind { 35 | KINDS_BEG = -1, 36 | 37 | #undef TOKDEF 38 | #define TOKDEF(k, s) k, 39 | #include "kinds_def.hh" 40 | 41 | KINDS_END, 42 | }; 43 | 44 | const char* get_token_name(TokenKind kind) noexcept; 45 | TokenKind get_keyword_kind(const str_t& kw) noexcept; 46 | 47 | class Token final : public Copyable { 48 | TokenKind kind_{TokenKind::TK_ERR}; 49 | str_t literal_; 50 | int lineno_{}; 51 | public: 52 | Token(void) noexcept {} 53 | 54 | Token(TokenKind kind, const str_t& literal, int lineno = 0) noexcept 55 | : kind_(kind), literal_(literal), lineno_(lineno) { 56 | } 57 | 58 | Token(const Token& r) noexcept 59 | : kind_(r.kind_), literal_(r.literal_), lineno_(r.lineno_) { 60 | } 61 | 62 | Token(Token&& r) noexcept 63 | : kind_(std::move(r.kind_)) 64 | , literal_(std::move(r.literal_)) 65 | , lineno_(std::move(r.lineno_)) { 66 | } 67 | 68 | inline Token& operator=(const Token& r) noexcept { 69 | if (this != &r) { 70 | kind_ = r.kind_; 71 | literal_ = r.literal_; 72 | lineno_ = r.lineno_; 73 | } 74 | return *this; 75 | } 76 | 77 | inline Token& operator=(Token&& r) noexcept { 78 | if (this != &r) { 79 | kind_ = std::move(r.kind_); 80 | literal_ = std::move(r.literal_); 81 | lineno_ = std::move(r.lineno_); 82 | } 83 | return *this; 84 | } 85 | 86 | inline bool operator==(const Token& r) const noexcept { 87 | return this == &r ? true : literal_ == r.literal_; 88 | } 89 | 90 | inline bool operator!=(const Token& r) const noexcept { 91 | return !(*this == r); 92 | } 93 | 94 | inline TokenKind kind(void) const noexcept { return kind_; } 95 | inline const str_t& literal(void) const noexcept { return literal_; } 96 | inline int lineno(void) const noexcept { return lineno_; } 97 | inline double as_numeric(void) const noexcept { return std::atof(literal_.c_str()); } 98 | inline str_t as_string(void) const noexcept { return literal_; } 99 | 100 | str_t stringify(void) const noexcept; 101 | 102 | static Token make_from_literal(const str_t& literal) noexcept { 103 | return Token{TokenKind::TK_STRING, literal}; 104 | } 105 | }; 106 | 107 | inline std::ostream& operator<<(std::ostream& out, const Token& tok) noexcept { 108 | return out << tok.stringify(); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /interpret_ast.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include "interpret_ast.hh" 28 | 29 | namespace loxcc::interpret { 30 | 31 | void AssignExpr::accept(const ExprVisitorPtr& expr) { 32 | expr->visit(shared_from_this()); 33 | } 34 | 35 | void SetExpr::accept(const ExprVisitorPtr& expr) { 36 | expr->visit(shared_from_this()); 37 | } 38 | 39 | void LogicalExpr::accept(const ExprVisitorPtr& expr) { 40 | expr->visit(shared_from_this()); 41 | } 42 | 43 | void BinaryExpr::accept(const ExprVisitorPtr& expr) { 44 | expr->visit(shared_from_this()); 45 | } 46 | 47 | void UnaryExpr::accept(const ExprVisitorPtr& expr) { 48 | expr->visit(shared_from_this()); 49 | } 50 | 51 | void CallExpr::accept(const ExprVisitorPtr& expr) { 52 | expr->visit(shared_from_this()); 53 | } 54 | 55 | void GetExpr::accept(const ExprVisitorPtr& expr) { 56 | expr->visit(shared_from_this()); 57 | } 58 | 59 | void LiteralExpr::accept(const ExprVisitorPtr& expr) { 60 | expr->visit(shared_from_this()); 61 | } 62 | 63 | void GroupingExpr::accept(const ExprVisitorPtr& expr) { 64 | expr->visit(shared_from_this()); 65 | } 66 | 67 | void SuperExpr::accept(const ExprVisitorPtr& expr) { 68 | expr->visit(shared_from_this()); 69 | } 70 | 71 | void ThisExpr::accept(const ExprVisitorPtr& expr) { 72 | expr->visit(shared_from_this()); 73 | } 74 | 75 | void VariableExpr::accept(const ExprVisitorPtr& expr) { 76 | expr->visit(shared_from_this()); 77 | } 78 | 79 | void FunctionExpr::accept(const ExprVisitorPtr& expr) { 80 | expr->visit(shared_from_this()); 81 | } 82 | 83 | void ClassStmt::accept(const StmtVisitorPtr& stmt) { 84 | stmt->visit(shared_from_this()); 85 | } 86 | 87 | void FunctionStmt::accept(const StmtVisitorPtr& stmt) { 88 | stmt->visit(shared_from_this()); 89 | } 90 | 91 | void VarStmt::accept(const StmtVisitorPtr& stmt) { 92 | stmt->visit(shared_from_this()); 93 | } 94 | 95 | void BlockStmt::accept(const StmtVisitorPtr& stmt) { 96 | stmt->visit(shared_from_this()); 97 | } 98 | 99 | void ExprStmt::accept(const StmtVisitorPtr& stmt) { 100 | stmt->visit(shared_from_this()); 101 | } 102 | 103 | void WhileStmt::accept(const StmtVisitorPtr& stmt) { 104 | stmt->visit(shared_from_this()); 105 | } 106 | 107 | void IfStmt::accept(const StmtVisitorPtr& stmt) { 108 | stmt->visit(shared_from_this()); 109 | } 110 | 111 | void PrintStmt::accept(const StmtVisitorPtr& stmt) { 112 | stmt->visit(shared_from_this()); 113 | } 114 | 115 | void ReturnStmt::accept(const StmtVisitorPtr& stmt) { 116 | stmt->visit(shared_from_this()); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /bytecc_vm.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "common.hh" 34 | #include "bytecc_value.hh" 35 | 36 | namespace loxcc::bytecc { 37 | 38 | enum class InterpretRet { 39 | OK, 40 | COMPILE_ERR, 41 | RUNTIME_ERR, 42 | }; 43 | 44 | class CallFrame; 45 | class GlobalCompiler; 46 | 47 | class VM final : private UnCopyable { 48 | static constexpr sz_t kDefaultCap = 256; 49 | static constexpr sz_t kMaxFrames = 256; 50 | static constexpr sz_t kHeapGrowFactor = 2; 51 | static constexpr sz_t kGCThresholds = 1 << 10; 52 | 53 | std::unique_ptr gcompiler_; 54 | 55 | std::vector stack_; 56 | std::vector frames_; 57 | 58 | std::unordered_map globals_; 59 | std::unordered_map interned_strings_; 60 | 61 | StringObject* ctor_string_{}; 62 | UpvalueObject* open_upvalues_{}; 63 | 64 | sz_t objects_allocated_{}; 65 | sz_t next_gc_{kGCThresholds}; 66 | std::list all_objects_; 67 | std::list worked_objects_; 68 | 69 | void runtime_error(const char* format, ...); 70 | void reset(void); 71 | Value* stack_values(int distance); 72 | void stack_resize(int distance); 73 | void set_stack(int distance, const Value& v); 74 | 75 | void push(const Value& value); 76 | Value pop(void); 77 | const Value& peek(int distance = 0) const; 78 | 79 | void define_native(const str_t& name, const NativeFn& fn); 80 | void define_native(const str_t& name, NativeFn&& fn); 81 | void define_method(StringObject* name); 82 | bool bind_method(ClassObject* cls, StringObject* name); 83 | bool call(ClosureObject* closure, int argc); 84 | bool call_value(const Value& callee, int argc); 85 | bool invoke_from_class(ClassObject* cls, StringObject* name, int argc); 86 | bool invoke(StringObject* name, int argc); 87 | 88 | UpvalueObject* capture_upvalue(Value* local); 89 | void close_upvalues(Value* last); 90 | 91 | InterpretRet run(void); 92 | 93 | void collect(void); 94 | public: 95 | VM(void) noexcept; 96 | ~VM(void); 97 | 98 | void set_interned(u32_t h, StringObject* s) { 99 | interned_strings_[h] = s; 100 | } 101 | 102 | StringObject* get_interned(u32_t h) const { 103 | if (auto it = interned_strings_.find(h); it != interned_strings_.end()) 104 | return it->second; 105 | return nullptr; 106 | } 107 | 108 | void append_object(BaseObject* o); 109 | void mark_object(BaseObject* o); 110 | void mark_value(const Value& v); 111 | void free_object(BaseObject* o); 112 | 113 | InterpretRet interpret(const str_t& source_bytes); 114 | }; 115 | 116 | } 117 | -------------------------------------------------------------------------------- /interpret_resolver.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include "common.hh" 33 | #include "interpret_ast.hh" 34 | 35 | namespace loxcc::interpret { 36 | 37 | class ErrorReport; 38 | class Interpreter; 39 | using InterpreterPtr = std::shared_ptr; 40 | 41 | class Resolver final 42 | : public ExprVisitor 43 | , public StmtVisitor 44 | , public std::enable_shared_from_this { 45 | enum class FunKind { NONE, FUNCTION, CTOR, METHOD }; 46 | enum class ClassKind { NONE, CLASS, SUBCLASS }; 47 | 48 | ErrorReport& err_report_; 49 | InterpreterPtr interp_; 50 | int loops_level_{}; 51 | std::vector> scopes_; 52 | FunKind curr_fun_{FunKind::NONE}; 53 | ClassKind curr_cls_{ClassKind::NONE}; 54 | 55 | void resolve(const ExprPtr& expr); 56 | void resolve(const std::vector& exprs); 57 | void resolve(const StmtPtr& stmt); 58 | void resolve(const std::vector& stmts); 59 | void resolve_local(const Token& name, const ExprPtr& expr); 60 | void resolve_function(const FunctionStmtPtr& fn, FunKind kind); 61 | 62 | void enter_scope(void); 63 | void leave_scope(void); 64 | void declare(const Token& name); 65 | void define(const Token& name); 66 | 67 | virtual void visit(const AssignExprPtr& expr) override; 68 | virtual void visit(const SetExprPtr& expr) override; 69 | virtual void visit(const LogicalExprPtr& expr) override; 70 | virtual void visit(const BinaryExprPtr& expr) override; 71 | virtual void visit(const UnaryExprPtr& expr) override; 72 | virtual void visit(const CallExprPtr& expr) override; 73 | virtual void visit(const GetExprPtr& expr) override; 74 | virtual void visit(const LiteralExprPtr& expr) override; 75 | virtual void visit(const GroupingExprPtr& expr) override; 76 | virtual void visit(const SuperExprPtr& expr) override; 77 | virtual void visit(const ThisExprPtr& expr) override; 78 | virtual void visit(const VariableExprPtr& expr) override; 79 | virtual void visit(const FunctionExprPtr& expr) override; 80 | 81 | virtual void visit(const ClassStmtPtr& stmt) override; 82 | virtual void visit(const FunctionStmtPtr& stmt) override; 83 | virtual void visit(const VarStmtPtr& stmt) override; 84 | virtual void visit(const BlockStmtPtr& stmt) override; 85 | virtual void visit(const ExprStmtPtr& stmt) override; 86 | virtual void visit(const WhileStmtPtr& stmt) override; 87 | virtual void visit(const IfStmtPtr& stmt) override; 88 | virtual void visit(const PrintStmtPtr& stmt) override; 89 | virtual void visit(const ReturnStmtPtr& stmt) override; 90 | public: 91 | Resolver(ErrorReport& err_report, const InterpreterPtr& interp) noexcept 92 | : err_report_(err_report), interp_(interp) { 93 | } 94 | 95 | void invoke_resolve(const StmtPtr& stmt); 96 | }; 97 | 98 | } 99 | -------------------------------------------------------------------------------- /interpret_callable.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include "common.hh" 33 | #include "token.hh" 34 | #include "interpret_value.hh" 35 | #include "interpret_ast.hh" 36 | 37 | namespace loxcc::interpret { 38 | 39 | class Environment; 40 | class Interpreter; 41 | class Function; 42 | class Class; 43 | class Instance; 44 | 45 | using EnvironmentPtr = std::shared_ptr; 46 | using InterpreterPtr = std::shared_ptr; 47 | using FunctionPtr = std::shared_ptr; 48 | using ClassPtr = std::shared_ptr; 49 | using InstancePtr = std::shared_ptr; 50 | 51 | struct Callable : private UnCopyable { 52 | virtual ~Callable(void) {} 53 | virtual bool check_arity(void) const { return true; } 54 | virtual sz_t arity(void) const { return 0; } 55 | 56 | virtual Value call( 57 | const InterpreterPtr& interp, const std::vector& args) = 0; 58 | virtual str_t stringify(void) const = 0; 59 | }; 60 | 61 | class Function final 62 | : public Callable, public std::enable_shared_from_this { 63 | FunctionStmtPtr decl_; 64 | EnvironmentPtr closure_; 65 | bool is_ctor_{}; 66 | public: 67 | Function(const FunctionStmtPtr& decl, 68 | const EnvironmentPtr& closure, bool is_ctor = false) noexcept 69 | : decl_(decl), closure_(closure), is_ctor_(is_ctor) { 70 | } 71 | 72 | virtual Value call( 73 | const InterpreterPtr& interp, const std::vector& args) override; 74 | virtual sz_t arity(void) const override; 75 | virtual str_t stringify(void) const override; 76 | 77 | FunctionPtr bind(const InstancePtr& inst); 78 | }; 79 | 80 | class Class final 81 | : public Callable, public std::enable_shared_from_this { 82 | str_t name_; 83 | ClassPtr superclass_; 84 | std::unordered_map methods_; 85 | public: 86 | Class(const str_t& name, const ClassPtr& superclass, 87 | const std::unordered_map& methods) noexcept 88 | : name_(name), superclass_(superclass), methods_(methods) { 89 | } 90 | 91 | inline str_t name(void) const { return name_; } 92 | 93 | virtual Value call( 94 | const InterpreterPtr& interp, const std::vector& args) override; 95 | virtual sz_t arity(void) const override; 96 | virtual str_t stringify(void) const override; 97 | 98 | FunctionPtr get_method(const InstancePtr& inst, const str_t& name); 99 | FunctionPtr get_method(const InstancePtr& inst, const Token& name); 100 | }; 101 | 102 | class Instance final 103 | : private UnCopyable, public std::enable_shared_from_this { 104 | ClassPtr cls_; 105 | std::unordered_map attrs_; 106 | public: 107 | Instance(const ClassPtr& cls) noexcept : cls_(cls) {} 108 | 109 | str_t stringify(void) const; 110 | void set_attr(const Token& name, const Value& value); 111 | Value get_attr(const Token& name); 112 | }; 113 | 114 | } 115 | -------------------------------------------------------------------------------- /interpret_callable.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include 28 | #include "interpret_errors.hh" 29 | #include "interpret_environment.hh" 30 | #include "interpret_interpreter.hh" 31 | #include "interpret_callable.hh" 32 | 33 | namespace loxcc::interpret { 34 | 35 | Value Function::call( 36 | const InterpreterPtr& interp, const std::vector& args) { 37 | auto envp = std::make_shared(closure_); 38 | const auto& params = decl_->params(); 39 | for (sz_t i = 0; i < params.size(); ++i) 40 | envp->define(params[i], args[i]); 41 | 42 | try { 43 | interp->invoke_evaluate(decl_->body(), envp); 44 | } 45 | catch (const Return& r) { 46 | return is_ctor_ ? closure_->get_at(0, "this") : r.value(); 47 | } 48 | 49 | return is_ctor_ ? closure_->get_at(0, "this") : nullptr; 50 | } 51 | 52 | sz_t Function::arity(void) const { 53 | return decl_->params().size(); 54 | } 55 | 56 | str_t Function::stringify(void) const { 57 | std::stringstream ss; 58 | 59 | ss << "name().as_string() << "` " 60 | << "at `" << this << "`>"; 61 | return ss.str(); 62 | } 63 | 64 | FunctionPtr Function::bind(const InstancePtr& inst) { 65 | auto envp = std::make_shared(closure_); 66 | envp->define("this", inst); 67 | return std::make_shared(decl_, envp, is_ctor_); 68 | } 69 | 70 | Value Class::call( 71 | const InterpreterPtr& interp, const std::vector& args) { 72 | auto inst = std::make_shared(shared_from_this()); 73 | if (auto ctor = get_method(inst, "ctor"); ctor) 74 | return ctor->call(interp, args); 75 | return inst; 76 | } 77 | 78 | sz_t Class::arity(void) const { 79 | if (auto meth_iter = methods_.find("ctor"); meth_iter != methods_.end()) 80 | return meth_iter->second->arity(); 81 | return 0; 82 | } 83 | 84 | str_t Class::stringify(void) const { 85 | return ""; 86 | } 87 | 88 | FunctionPtr Class::get_method(const InstancePtr& inst, const str_t& name) { 89 | if (auto meth_iter = methods_.find(name); meth_iter != methods_.end()) 90 | return meth_iter->second->bind(inst); 91 | if (superclass_) 92 | return superclass_->get_method(inst, name); 93 | return nullptr; 94 | } 95 | 96 | FunctionPtr Class::get_method(const InstancePtr& inst, const Token& name) { 97 | return get_method(inst, name.as_string()); 98 | } 99 | 100 | str_t Instance::stringify(void) const { 101 | std::stringstream ss; 102 | 103 | ss << "<`" << cls_->name() << "` object at `" << this << "`>"; 104 | return ss.str(); 105 | } 106 | 107 | void Instance::set_attr(const Token& name, const Value& value) { 108 | attrs_[name.as_string()] = value; 109 | } 110 | 111 | Value Instance::get_attr(const Token& name) { 112 | str_t attr_name = name.as_string(); 113 | if (auto attr_iter = attrs_.find(attr_name); attr_iter != attrs_.end()) 114 | return attr_iter->second; 115 | 116 | CallablePtr method = cls_->get_method(shared_from_this(), attr_name); 117 | if (method) 118 | return method; 119 | 120 | throw RuntimeError(name, "undefined attribute `" + attr_name + "`"); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /common.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "platform.hh" 36 | 37 | namespace loxcc { 38 | 39 | class LoxccSafe {}; 40 | class LoxccUnsafe {}; 41 | 42 | using nil_t = std::nullptr_t; 43 | using byte_t = std::uint8_t; 44 | using i8_t = std::int8_t; 45 | using u8_t = std::uint8_t; 46 | using i16_t = std::int16_t; 47 | using u16_t = std::uint16_t; 48 | using i32_t = std::int32_t; 49 | using u32_t = std::uint32_t; 50 | using i64_t = std::int64_t; 51 | using u64_t = std::uint64_t; 52 | #if defined(LOXCC_GNUC) 53 | using ssz_t = std::ssize_t; 54 | #else 55 | using ssz_t = std::int32_t; 56 | #endif 57 | using sz_t = std::size_t; 58 | using str_t = std::string; 59 | using cstr_t = const char*; 60 | using strv_t = std::string_view; 61 | using ss_t = std::stringstream; 62 | using safe_t = LoxccSafe; 63 | using unsafe_t = LoxccUnsafe; 64 | 65 | class Copyable { 66 | protected: 67 | Copyable(void) noexcept = default; 68 | ~Copyable(void) noexcept = default; 69 | Copyable(const Copyable&) noexcept = default; 70 | Copyable(Copyable&&) noexcept = default; 71 | Copyable& operator=(const Copyable&) noexcept = default; 72 | Copyable& operator=(Copyable&&) noexcept = default; 73 | }; 74 | 75 | class UnCopyable { 76 | UnCopyable(const UnCopyable&) noexcept = delete; 77 | UnCopyable(UnCopyable&&) noexcept = delete; 78 | UnCopyable& operator=(const UnCopyable&) noexcept = delete; 79 | UnCopyable& operator=(UnCopyable&&) noexcept = delete; 80 | protected: 81 | UnCopyable(void) noexcept = default; 82 | ~UnCopyable(void) noexcept = default; 83 | }; 84 | 85 | template class Singleton : private UnCopyable { 86 | public: 87 | static Object& get_instance() noexcept { 88 | static Object ins; 89 | return ins; 90 | } 91 | }; 92 | 93 | namespace Xt { 94 | template inline I as_type(E x) noexcept { return static_cast(x); } 95 | template inline T* as_ptr(const T* x) noexcept { return const_cast(x); } 96 | template inline T* cast(S* x) noexcept { return static_cast(x); } 97 | template inline T* down(S* x) noexcept { return dynamic_cast(x); } 98 | 99 | template 100 | inline T* const_down(const S* x) noexcept { return down(const_cast(x)); } 101 | 102 | template inline u32_t hasher(const char* s, N n) noexcept { 103 | // FNV-1a hash. See: http://www.isthe.com/chongo/tech/comp/fnv/ 104 | u32_t hash = 2166136261u; 105 | for (int i = 0; i < as_type(n); ++i) { 106 | hash ^= s[i]; 107 | hash *= 16777619; 108 | } 109 | return hash; 110 | } 111 | 112 | template inline double to_decimal(T x) noexcept { return as_type(x); } 113 | 114 | inline str_t to_string(double d) noexcept { 115 | std::stringstream ss; 116 | ss << std::setprecision(std::numeric_limits::max_digits10) << d; 117 | return ss.str(); 118 | } 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /interpret_interpreter.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "common.hh" 34 | #include "interpret_value.hh" 35 | #include "interpret_ast.hh" 36 | 37 | namespace loxcc::interpret { 38 | 39 | class ErrorReport; 40 | class Environment; 41 | 42 | using EnvironmentPtr = std::shared_ptr; 43 | 44 | class Return final : public Copyable, public std::exception { 45 | Value value_{}; 46 | public: 47 | Return(const Value& v) noexcept : value_(v) {} 48 | inline const Value& value(void) const { return value_; } 49 | }; 50 | 51 | class Interpreter final 52 | : public ExprVisitor 53 | , public StmtVisitor 54 | , public std::enable_shared_from_this { 55 | ErrorReport& err_report_; 56 | Value value_{}; 57 | EnvironmentPtr globals_; 58 | EnvironmentPtr environment_; 59 | std::unordered_map locals_; 60 | 61 | Value evaluate(const ExprPtr& expr); 62 | void evaluate(const StmtPtr& stmt); 63 | void evaluate( 64 | const std::vector& stmts, const EnvironmentPtr& env); 65 | void check_numeric(const Token& oper, const Value& value); 66 | void check_numerics(const Token& oper, const Value& lhs, const Value& rhs); 67 | void check_plus(const Token& oper, const Value& lhs, const Value& rhs); 68 | Value lookup_variable(const Token& name, const ExprPtr& expr); 69 | 70 | virtual void visit(const AssignExprPtr& expr) override; 71 | virtual void visit(const SetExprPtr& expr) override; 72 | virtual void visit(const LogicalExprPtr& expr) override; 73 | virtual void visit(const BinaryExprPtr& expr) override; 74 | virtual void visit(const UnaryExprPtr& expr) override; 75 | virtual void visit(const CallExprPtr& expr) override; 76 | virtual void visit(const GetExprPtr& expr) override; 77 | virtual void visit(const LiteralExprPtr& expr) override; 78 | virtual void visit(const GroupingExprPtr& expr) override; 79 | virtual void visit(const SuperExprPtr& expr) override; 80 | virtual void visit(const ThisExprPtr& expr) override; 81 | virtual void visit(const VariableExprPtr& expr) override; 82 | virtual void visit(const FunctionExprPtr& expr) override; 83 | 84 | virtual void visit(const ClassStmtPtr& stmt) override; 85 | virtual void visit(const FunctionStmtPtr& stmt) override; 86 | virtual void visit(const VarStmtPtr& stmt) override; 87 | virtual void visit(const BlockStmtPtr& stmt) override; 88 | virtual void visit(const ExprStmtPtr& stmt) override; 89 | virtual void visit(const WhileStmtPtr& stmt) override; 90 | virtual void visit(const IfStmtPtr& stmt) override; 91 | virtual void visit(const PrintStmtPtr& stmt) override; 92 | virtual void visit(const ReturnStmtPtr& stmt) override; 93 | public: 94 | Interpreter(ErrorReport& err_report) noexcept; 95 | 96 | void interpret(const str_t& source_bytes); 97 | 98 | inline void resolve(const ExprPtr& expr, int depth) { locals_[expr] = depth; } 99 | inline EnvironmentPtr get_globals(void) const { return globals_; } 100 | inline void invoke_evaluate( 101 | const std::vector& stmts, const EnvironmentPtr& env) { 102 | evaluate(stmts, env); 103 | } 104 | 105 | }; 106 | 107 | } 108 | -------------------------------------------------------------------------------- /lexer.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include "lexer.hh" 28 | 29 | namespace loxcc { 30 | 31 | Token Lexer::next_token(void) { 32 | skip_whitespace(); 33 | 34 | begpos_ = curpos_; 35 | if (is_end()) 36 | return make_token(TokenKind::TK_EOF); 37 | 38 | char c = advance(); 39 | if (isalpha(c)) 40 | return make_identifier(); 41 | if (isdigit(c)) 42 | return make_numeric(); 43 | 44 | #define _MKTOK(c, k) case c: return make_token(TokenKind::TK_##k) 45 | switch (c) { 46 | _MKTOK('(', LPAREN); 47 | _MKTOK(')', RPAREN); 48 | _MKTOK('{', LBRACE); 49 | _MKTOK('}', RBRACE); 50 | _MKTOK(',', COMMA); 51 | _MKTOK('.', DOT); 52 | _MKTOK('-', MINUS); 53 | _MKTOK('+', PLUS); 54 | _MKTOK(';', SEMI); 55 | _MKTOK('/', SLASH); 56 | _MKTOK('*', STAR); 57 | case '!': return make_token(match('=') ? TokenKind::TK_BANGEQ : TokenKind::TK_BANG); 58 | case '=': return make_token(match('=') ? TokenKind::TK_EQEQ : TokenKind::TK_EQ); 59 | case '>': return make_token(match('=') ? TokenKind::TK_GTEQ : TokenKind::TK_GT); 60 | case '<': return make_token(match('=') ? TokenKind::TK_LTEQ : TokenKind::TK_LT); 61 | case '"': return make_string(); 62 | } 63 | #undef _MKTOK 64 | 65 | return make_error("unexpected charactor"); 66 | } 67 | 68 | void Lexer::skip_whitespace(void) { 69 | for (;;) { 70 | char c = peek(); 71 | switch (c) { 72 | case ' ': case '\r': case '\t': advance(); break; 73 | case '\n': ++lineno_; advance(); break; 74 | case '/': 75 | if (peek_next() == '/') { 76 | while (!is_end() && peek() != '\n') 77 | advance(); 78 | } 79 | else { 80 | return; 81 | } 82 | break; 83 | default: return; 84 | } 85 | } 86 | } 87 | 88 | Token Lexer::make_identifier(void) { 89 | while (isalnum(peek())) 90 | advance(); 91 | 92 | str_t literal = gen_literal(begpos_, curpos_); 93 | return make_token(get_keyword_kind(literal), literal); 94 | } 95 | 96 | Token Lexer::make_numeric(void) { 97 | while (isdigit(peek())) 98 | advance(); 99 | 100 | while (peek() == '.' && isdigit(peek_next())) { 101 | advance(); 102 | while (isdigit(peek())) 103 | advance(); 104 | } 105 | 106 | if (isalpha(peek())) 107 | return make_error("invalid numeric or identifier"); 108 | return make_token(TokenKind::TK_NUMERIC); 109 | } 110 | 111 | Token Lexer::make_string(void) { 112 | #define _MKCHAR(x, y) case x: c = y; advance(); break 113 | 114 | str_t literal; 115 | while (!is_end() && peek() != '"') { 116 | char c = peek(); 117 | switch (c) { 118 | case '\n': ++lineno_; break; 119 | case '\\': 120 | switch (peek_next()) { 121 | _MKCHAR('"', '"'); 122 | _MKCHAR('\\', '\\'); 123 | _MKCHAR('%', '%'); 124 | _MKCHAR('0', '\0'); 125 | _MKCHAR('a', '\a'); 126 | _MKCHAR('b', '\b'); 127 | _MKCHAR('f', '\f'); 128 | _MKCHAR('n', '\n'); 129 | _MKCHAR('r', '\r'); 130 | _MKCHAR('t', '\t'); 131 | _MKCHAR('v', '\v'); 132 | } 133 | break; 134 | } 135 | literal.push_back(c); 136 | advance(); 137 | } 138 | #undef _MKCHAR 139 | 140 | if (is_end()) 141 | return make_error("unterminated string"); 142 | 143 | advance(); // closng the string " 144 | return make_token(TokenKind::TK_STRING, literal); 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /bytecc_chunk.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include 28 | #include "bytecc_chunk.hh" 29 | 30 | namespace loxcc::bytecc { 31 | 32 | inline int dis_compound( 33 | Chunk* chunk, const char* prompt, int i, bool with_constant = false, int n = 0) noexcept { 34 | auto c = chunk->get_code(i + 1); 35 | if (n > 1) 36 | std::fprintf(stdout, "%s_%-*d %4d", prompt, 15 - Xt::as_type(std::strlen(prompt)), n, c); 37 | else 38 | std::fprintf(stdout, "%-16s %4d", prompt, c); 39 | if (with_constant) 40 | std::cout << " `" << chunk->get_constant(c) << "`"; 41 | std::cout << std::endl; 42 | 43 | return i + 2; 44 | } 45 | 46 | inline int dis_simple(Chunk* chunk, const char* prompt, int i, int n = 0) noexcept { 47 | std::cout << prompt; 48 | if (n > 0) 49 | std::cout << "_" << n; 50 | std::cout << std::endl; 51 | 52 | return i + 1; 53 | } 54 | 55 | inline int dis_jump(Chunk* chunk, const char* prompt, int i, int sign) noexcept { 56 | u16_t jump = Xt::as_type((chunk->get_code(i + 1) << 8) | chunk->get_code(i + 2)); 57 | std::fprintf(stdout, "%-16s %4d -> %d\n", prompt, i, i + 3 + jump * sign); 58 | return i + 3; 59 | } 60 | 61 | void Chunk::dis(const str_t& name) { 62 | std::cout << "========= [" << name << "] =========" << std::endl; 63 | for (int i = 0; i < codes_count();) 64 | i = dis_ins(i); 65 | } 66 | 67 | int Chunk::dis_ins(int offset) { 68 | fprintf(stdout, "%04d ", offset); 69 | if (offset > 0 && lines_[offset] == lines_[offset - 1]) 70 | std::cout << " | "; 71 | else 72 | fprintf(stdout, "%4d ", lines_[offset]); 73 | 74 | #define COMPOUND(x) return dis_compound(this, #x, offset) 75 | #define COMPOUND2(x, b) return dis_compound(this, #x, offset, (b)) 76 | #define COMPOUND3(x, n) return dis_compound(this, #x, offset, true, (n)) 77 | #define SIMPLE(x) return dis_simple(this, #x, offset) 78 | #define SIMPLE2(x, n) return dis_simple(this, #x, offset, (n)) 79 | #define JUMP(x, s) return dis_jump(this, #x, offset, (s)) 80 | 81 | switch (Code c = Xt::as_type(codes_[offset])) { 82 | case Code::CONSTANT: COMPOUND2(CONSTANT, true); 83 | case Code::NIL: SIMPLE(NIL); 84 | case Code::TRUE: SIMPLE(TRUE); 85 | case Code::FALSE: SIMPLE(FALSE); 86 | case Code::POP: SIMPLE(POP); 87 | case Code::DEF_GLOBAL: COMPOUND2(DEF_GLOBAL, true); 88 | case Code::GET_GLOBAL: COMPOUND2(GET_GLOBAL, true); 89 | case Code::SET_GLOBAL: COMPOUND2(SET_GLOBAL, true); 90 | case Code::GET_LOCAL: COMPOUND(GET_LOCAL); 91 | case Code::SET_LOCAL: COMPOUND(SET_LOCAL); 92 | case Code::GET_UPVALUE: COMPOUND(GET_UPVALUE); 93 | case Code::SET_UPVALUE: COMPOUND(SET_UPVALUE); 94 | case Code::GET_ATTR: COMPOUND2(GET_ATTR, true); 95 | case Code::SET_ATTR: COMPOUND2(SET_ATTR, true); 96 | case Code::GET_SUPER: COMPOUND2(GET_SUPER, true); 97 | case Code::EQ: SIMPLE(EQ); 98 | case Code::NE: SIMPLE(NE); 99 | case Code::GT: SIMPLE(GT); 100 | case Code::GE: SIMPLE(GE); 101 | case Code::LT: SIMPLE(LT); 102 | case Code::LE: SIMPLE(LE); 103 | case Code::ADD: SIMPLE(ADD); 104 | case Code::SUB: SIMPLE(SUB); 105 | case Code::MUL: SIMPLE(MUL); 106 | case Code::DIV: SIMPLE(DIV); 107 | case Code::NOT: SIMPLE(NOT); 108 | case Code::NEG: SIMPLE(NEG); 109 | case Code::PRINT: SIMPLE(PRINT); 110 | case Code::JUMP: JUMP(JUMP, 1); 111 | case Code::JUMP_IF_FALSE: JUMP(JUMP_IF_FALSE, 1); 112 | case Code::LOOP: JUMP(LOOP, -1); 113 | case Code::CALL_0: 114 | case Code::CALL_1: 115 | case Code::CALL_2: 116 | case Code::CALL_3: 117 | case Code::CALL_4: 118 | case Code::CALL_5: 119 | case Code::CALL_6: 120 | case Code::CALL_7: 121 | case Code::CALL_8: SIMPLE2(CALL, c - Code::CALL_0); 122 | case Code::INVOKE_0: 123 | case Code::INVOKE_1: 124 | case Code::INVOKE_2: 125 | case Code::INVOKE_3: 126 | case Code::INVOKE_4: 127 | case Code::INVOKE_5: 128 | case Code::INVOKE_6: 129 | case Code::INVOKE_7: 130 | case Code::INVOKE_8: COMPOUND3(INVOKE_, c - Code::INVOKE_0); 131 | case Code::SUPER_0: 132 | case Code::SUPER_1: 133 | case Code::SUPER_2: 134 | case Code::SUPER_3: 135 | case Code::SUPER_4: 136 | case Code::SUPER_5: 137 | case Code::SUPER_6: 138 | case Code::SUPER_7: 139 | case Code::SUPER_8: COMPOUND3(SUPER_, c - Code::SUPER_0); 140 | case Code::CLOSURE: COMPOUND2(CLOSURE, true); 141 | case Code::CLOSE_UPVALUE: SIMPLE(CLOSE_UPVALUE); 142 | case Code::RETURN: SIMPLE(RETURN); 143 | case Code::CLASS: COMPOUND2(CLASS, true); 144 | case Code::SUBCLASS: SIMPLE(SUBCLASS); 145 | case Code::METHOD: COMPOUND2(METHOD, true); 146 | default: std::cerr << "UNKNOWN CODE: " << c << std::endl; break; 147 | } 148 | 149 | #undef JUMP 150 | #undef SIMPLE2 151 | #undef SIMPLE 152 | #undef COMPOUND3 153 | #undef COMPOUND2 154 | #undef COMPOUND 155 | 156 | return offset + 1; 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /interpret_value.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include "common.hh" 33 | 34 | namespace loxcc::interpret { 35 | 36 | struct Callable; 37 | class Instance; 38 | 39 | using CallablePtr = std::shared_ptr; 40 | using InstancePtr = std::shared_ptr; 41 | 42 | class Value final : public Copyable { 43 | std::variant< 44 | nil_t, 45 | bool, 46 | double, 47 | str_t, 48 | CallablePtr, 49 | InstancePtr> v_{}; 50 | public: 51 | Value(void) noexcept : v_(nullptr) {} 52 | Value(nil_t) noexcept : v_(nullptr) {} 53 | Value(bool b) noexcept : v_(b) {} 54 | Value(i8_t x) noexcept : v_(Xt::to_decimal(x)) {} 55 | Value(u8_t x) noexcept : v_(Xt::to_decimal(x)) {} 56 | Value(i16_t x) noexcept : v_(Xt::to_decimal(x)) {} 57 | Value(u16_t x) noexcept : v_(Xt::to_decimal(x)) {} 58 | Value(i32_t x) noexcept : v_(Xt::to_decimal(x)) {} 59 | Value(u32_t x) noexcept : v_(Xt::to_decimal(x)) {} 60 | Value(i64_t x) noexcept : v_(Xt::to_decimal(x)) {} 61 | Value(u64_t x) noexcept : v_(Xt::to_decimal(x)) {} 62 | Value(float x) noexcept : v_(Xt::to_decimal(x)) {} 63 | Value(double d) noexcept : v_(d) {} 64 | #if defined(LOXCC_GNUC) 65 | Value(long long x) noexcept : v_(Xt::to_decimal(x)) {} 66 | Value(unsigned long long x) noexcept : v_(Xt::to_decimal(x)) {} 67 | #endif 68 | Value(const char* s) noexcept : v_(str_t(s)) {} 69 | Value(strv_t s) noexcept : v_(str_t(s)) {} 70 | Value(const str_t& s) noexcept : v_(s) {} 71 | Value(const CallablePtr& c) noexcept : v_(c) {} 72 | Value(const InstancePtr& i) noexcept : v_(i) {} 73 | Value(const Value& r) noexcept : v_(r.v_) {} 74 | Value(Value&& r) noexcept : v_(std::move(r.v_)) {} 75 | 76 | inline Value& operator=(const Value& r) noexcept { 77 | if (this != &r) 78 | v_ = r.v_; 79 | return *this; 80 | } 81 | 82 | inline Value& operator=(Value&& r) noexcept { 83 | if (this != &r) 84 | v_ = std::move(r.v_); 85 | return *this; 86 | } 87 | 88 | inline bool is_nil(void) const noexcept { return std::holds_alternative(v_); } 89 | inline bool is_boolean(void) const noexcept { return std::holds_alternative(v_); } 90 | inline bool is_numeric(void) const noexcept { return std::holds_alternative(v_); } 91 | inline bool is_string(void) const noexcept { return std::holds_alternative(v_); } 92 | inline bool is_callable(void) const noexcept { return std::holds_alternative(v_); } 93 | inline bool is_instance(void) const noexcept { return std::holds_alternative(v_); } 94 | 95 | inline bool as_boolean(void) const noexcept { return std::get(v_); } 96 | inline double as_numeric(void) const noexcept { return std::get(v_); } 97 | template inline T as_integer() const noexcept { return Xt::as_type(as_numeric()); } 98 | inline str_t as_string(void) const noexcept { return std::get(v_); } 99 | inline CallablePtr as_callable(void) const noexcept { return std::get(v_); } 100 | inline InstancePtr as_instance(void) const noexcept { return std::get(v_); } 101 | 102 | inline bool operator==(const Value& r) const noexcept { 103 | if (is_numeric() && r.is_numeric()) 104 | return as_numeric() == r.as_numeric(); 105 | return v_ == r.v_; 106 | } 107 | 108 | inline bool operator!=(const Value& r) const noexcept { return !(*this == r); } 109 | inline bool operator>(const Value& r) const noexcept { return as_numeric() > r.as_numeric(); } 110 | inline bool operator>=(const Value& r) const noexcept { return as_numeric() >= r.as_numeric(); } 111 | inline bool operator<(const Value& r) const noexcept { return as_numeric() < r.as_numeric(); } 112 | inline bool operator<=(const Value& r) const noexcept { return as_numeric() <= r.as_numeric(); } 113 | 114 | inline Value operator+(const Value& r) const noexcept { 115 | if (is_string() && r.is_string()) 116 | return as_string() + r.as_string(); 117 | return as_numeric() + r.as_numeric(); 118 | } 119 | 120 | inline Value operator-(const Value& r) const noexcept { return as_numeric() - r.as_numeric(); } 121 | inline Value operator*(const Value& r) const noexcept { return as_numeric() * r.as_numeric(); } 122 | inline Value operator/(const Value& r) const noexcept { return as_numeric() / r.as_numeric(); } 123 | inline Value operator-(void) const noexcept { return -as_numeric(); } 124 | inline Value operator!(void) const noexcept { return !is_truthy(); } 125 | 126 | inline bool is_abs_equal(const Value& r) const noexcept { return v_ == r.v_; } 127 | inline bool is_equal(const Value& r) const noexcept { return (this == &r) || (*this == r); } 128 | 129 | bool is_truthy(void) const; 130 | str_t stringify(void) const; 131 | }; 132 | 133 | inline std::ostream& operator<<(std::ostream& out, const Value& val) noexcept { 134 | return out << val.stringify(); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /interpret_resolver.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include "interpret_errors.hh" 28 | #include "interpret_interpreter.hh" 29 | #include "interpret_resolver.hh" 30 | 31 | namespace loxcc::interpret { 32 | 33 | void Resolver::invoke_resolve(const StmtPtr& stmt) { 34 | resolve(stmt); 35 | } 36 | 37 | void Resolver::resolve(const ExprPtr& expr) { 38 | expr->accept(shared_from_this()); 39 | } 40 | 41 | void Resolver::resolve(const std::vector& exprs) { 42 | for (auto& expr : exprs) 43 | resolve(expr); 44 | } 45 | 46 | void Resolver::resolve(const StmtPtr& stmt) { 47 | stmt->accept(shared_from_this()); 48 | } 49 | 50 | void Resolver::resolve(const std::vector& stmts) { 51 | for (auto& stmt : stmts) 52 | resolve(stmt); 53 | } 54 | 55 | void Resolver::resolve_local(const Token& name, const ExprPtr& expr) { 56 | int n = Xt::as_type(scopes_.size()) - 1; 57 | for (int i = n; i >= 0; --i) { 58 | auto& scope = scopes_[i]; 59 | if (scope.find(name.as_string()) != scope.end()) { 60 | interp_->resolve(expr, n - i); 61 | return; 62 | } 63 | } 64 | // not found, assume it is global 65 | } 66 | 67 | void Resolver::resolve_function(const FunctionStmtPtr& fn, FunKind kind) { 68 | FunKind encloding_fun = curr_fun_; 69 | curr_fun_ = kind; 70 | 71 | enter_scope(); 72 | for (auto& param : fn->params()) { 73 | declare(param); 74 | define(param); 75 | } 76 | resolve(fn->body()); 77 | leave_scope(); 78 | 79 | curr_fun_ = encloding_fun; 80 | } 81 | 82 | void Resolver::enter_scope(void) { 83 | scopes_.push_back({}); 84 | } 85 | 86 | void Resolver::leave_scope(void) { 87 | scopes_.pop_back(); 88 | } 89 | 90 | void Resolver::declare(const Token& name) { 91 | if (!scopes_.empty()) { 92 | auto& scope = scopes_.back(); 93 | str_t var_name = name.as_string(); 94 | if (scope.find(var_name) != scope.end()) { 95 | throw RuntimeError(name, 96 | "variable `" + var_name + "` already declared in thie scope"); 97 | } 98 | else { 99 | scope[var_name] = false; 100 | } 101 | } 102 | } 103 | 104 | void Resolver::define(const Token& name) { 105 | if (!scopes_.empty()) { 106 | auto& scope = scopes_.back(); 107 | scope[name.as_string()] = true; 108 | } 109 | } 110 | 111 | void Resolver::visit(const AssignExprPtr& expr) { 112 | resolve(expr->value()); 113 | resolve_local(expr->name(), expr); 114 | } 115 | 116 | void Resolver::visit(const SetExprPtr& expr) { 117 | resolve(expr->value()); 118 | resolve(expr->object()); 119 | } 120 | 121 | void Resolver::visit(const LogicalExprPtr& expr) { 122 | resolve(expr->lhs()); 123 | resolve(expr->rhs()); 124 | } 125 | 126 | void Resolver::visit(const BinaryExprPtr& expr) { 127 | resolve(expr->lhs()); 128 | resolve(expr->rhs()); 129 | } 130 | 131 | void Resolver::visit(const UnaryExprPtr& expr) { 132 | resolve(expr->rhs()); 133 | } 134 | 135 | void Resolver::visit(const CallExprPtr& expr) { 136 | resolve(expr->callee()); 137 | resolve(expr->arguments()); 138 | } 139 | 140 | void Resolver::visit(const GetExprPtr& expr) { 141 | resolve(expr->object()); 142 | } 143 | 144 | void Resolver::visit(const LiteralExprPtr& expr) { 145 | } 146 | 147 | void Resolver::visit(const GroupingExprPtr& expr) { 148 | resolve(expr->expression()); 149 | } 150 | 151 | void Resolver::visit(const SuperExprPtr& expr) { 152 | if (curr_cls_ == ClassKind::NONE) { 153 | throw RuntimeError(expr->keyword(), 154 | "cannot use `super` outside of a class"); 155 | } 156 | else if (curr_cls_ != ClassKind::SUBCLASS) { 157 | throw RuntimeError(expr->keyword(), 158 | "cannot use `super` in a class without superclass"); 159 | } 160 | 161 | resolve_local(expr->keyword(), expr); 162 | } 163 | 164 | void Resolver::visit(const ThisExprPtr& expr) { 165 | if (curr_cls_ != ClassKind::CLASS && curr_cls_ != ClassKind::SUBCLASS) 166 | throw RuntimeError(expr->keyword(), "cannot use `this` outside of a class"); 167 | 168 | resolve_local(expr->keyword(), expr); 169 | } 170 | 171 | void Resolver::visit(const VariableExprPtr& expr) { 172 | if (!scopes_.empty()) { 173 | auto& scope = scopes_.back(); 174 | str_t var_name = expr->name().as_string(); 175 | if (scope.find(var_name) != scope.end() && !scope[var_name]) { 176 | throw RuntimeError(expr->name(), 177 | "cannot read local variable `" + 178 | var_name + "` in its own initializer"); 179 | } 180 | } 181 | 182 | resolve_local(expr->name(), expr); 183 | } 184 | 185 | void Resolver::visit(const FunctionExprPtr& expr) { 186 | } 187 | 188 | void Resolver::visit(const ClassStmtPtr& stmt) { 189 | ClassKind encloding_cls = curr_cls_; 190 | curr_cls_ = ClassKind::CLASS; 191 | const ExprPtr& superclass = stmt->superclass(); 192 | 193 | declare(stmt->name()); 194 | if (superclass) { 195 | curr_cls_ = ClassKind::SUBCLASS; 196 | resolve(superclass); 197 | } 198 | define(stmt->name()); 199 | 200 | if (superclass) { 201 | enter_scope(); 202 | scopes_.back()["super"] = true; 203 | } 204 | 205 | enter_scope(); 206 | scopes_.back()["this"] = true; 207 | for (auto& meth_stmt : stmt->methods()) { 208 | FunKind kind = FunKind::METHOD; 209 | if (meth_stmt->name().as_string() == "ctor") 210 | kind = FunKind::CTOR; 211 | resolve_function(meth_stmt, kind); 212 | } 213 | leave_scope(); 214 | 215 | if (superclass) 216 | leave_scope(); 217 | 218 | curr_cls_ = encloding_cls; 219 | } 220 | 221 | void Resolver::visit(const FunctionStmtPtr& stmt) { 222 | declare(stmt->name()); 223 | define(stmt->name()); 224 | resolve_function(stmt, FunKind::FUNCTION); 225 | } 226 | 227 | void Resolver::visit(const VarStmtPtr& stmt) { 228 | declare(stmt->name()); 229 | if (stmt->expr()) 230 | resolve(stmt->expr()); 231 | define(stmt->name()); 232 | } 233 | 234 | void Resolver::visit(const BlockStmtPtr& stmt) { 235 | enter_scope(); 236 | resolve(stmt->stmts()); 237 | leave_scope(); 238 | } 239 | 240 | void Resolver::visit(const ExprStmtPtr& stmt) { 241 | resolve(stmt->expr()); 242 | } 243 | 244 | void Resolver::visit(const WhileStmtPtr& stmt) { 245 | resolve(stmt->cond()); 246 | resolve(stmt->body()); 247 | } 248 | 249 | void Resolver::visit(const IfStmtPtr& stmt) { 250 | resolve(stmt->cond()); 251 | resolve(stmt->then_branch()); 252 | if (stmt->else_branch()) 253 | resolve(stmt->else_branch()); 254 | } 255 | 256 | void Resolver::visit(const PrintStmtPtr& stmt) { 257 | resolve(stmt->exprs()); 258 | } 259 | 260 | void Resolver::visit(const ReturnStmtPtr& stmt) { 261 | if (curr_fun_ == FunKind::NONE) 262 | throw RuntimeError(stmt->keyword(), "cannot return from top-level code"); 263 | 264 | if (stmt->value()) { 265 | if (curr_fun_ == FunKind::CTOR) { 266 | throw RuntimeError(stmt->keyword(), 267 | "cannot return a value from `ctor` of class"); 268 | } 269 | resolve(stmt->value()); 270 | } 271 | } 272 | 273 | } 274 | -------------------------------------------------------------------------------- /interpret_interpreter.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include 28 | #include "lexer.hh" 29 | #include "interpret_errors.hh" 30 | #include "interpret_parser.hh" 31 | #include "interpret_environment.hh" 32 | #include "interpret_callable.hh" 33 | #include "interpret_builtins.hh" 34 | #include "interpret_resolver.hh" 35 | #include "interpret_interpreter.hh" 36 | 37 | namespace loxcc::interpret { 38 | 39 | Interpreter::Interpreter(ErrorReport& err_report) noexcept 40 | : err_report_(err_report) 41 | , globals_(new Environment()) 42 | , environment_(globals_) { 43 | globals_->define("clock", Value(std::make_shared())); 44 | } 45 | 46 | void Interpreter::interpret(const str_t& source_bytes) { 47 | Lexer lex(source_bytes); 48 | Parser parser(err_report_, lex); 49 | auto resolver = std::make_shared(err_report_, shared_from_this()); 50 | 51 | try { 52 | for (;;) { 53 | StmtPtr stmt = parser.parse(); 54 | if (!stmt) 55 | break; 56 | 57 | resolver->invoke_resolve(stmt); 58 | evaluate(stmt); 59 | } 60 | } 61 | catch (const RuntimeError& e) { 62 | err_report_.error(e.token(), e.message()); 63 | } 64 | } 65 | 66 | Value Interpreter::evaluate(const ExprPtr& expr) { 67 | expr->accept(shared_from_this()); 68 | return value_; 69 | } 70 | 71 | void Interpreter::evaluate(const StmtPtr& stmt) { 72 | stmt->accept(shared_from_this()); 73 | } 74 | 75 | void Interpreter::evaluate( 76 | const std::vector& stmts, const EnvironmentPtr& env) { 77 | auto orig_env = environment_; 78 | try { 79 | environment_ = env; 80 | for (auto& stmt : stmts) 81 | evaluate(stmt); 82 | } 83 | catch (...) { 84 | environment_ = orig_env; 85 | throw; 86 | } 87 | environment_ = orig_env; 88 | } 89 | 90 | void Interpreter::check_numeric(const Token& oper, const Value& value) { 91 | if (value.is_numeric()) 92 | return; 93 | throw RuntimeError(oper, "operand must be a numeric"); 94 | } 95 | 96 | void Interpreter::check_numerics( 97 | const Token& oper, const Value& lhs, const Value& rhs) { 98 | if (lhs.is_numeric() && rhs.is_numeric()) 99 | return; 100 | throw RuntimeError(oper, "operands must be two numerics"); 101 | } 102 | 103 | void Interpreter::check_plus( 104 | const Token& oper, const Value& lhs, const Value& rhs) { 105 | if ((lhs.is_string() && rhs.is_string()) 106 | || (lhs.is_numeric() && rhs.is_numeric())) 107 | return; 108 | throw RuntimeError(oper, "operands must be two numerics or two strings"); 109 | } 110 | 111 | Value Interpreter::lookup_variable(const Token& name, const ExprPtr& expr) { 112 | if (auto distance_iter = locals_.find(expr); distance_iter != locals_.end()) 113 | return environment_->get_at(distance_iter->second, name); 114 | else 115 | return globals_->get(name); 116 | } 117 | 118 | void Interpreter::visit(const AssignExprPtr& expr) { 119 | Value value = evaluate(expr->value()); 120 | if (auto distance_iter = locals_.find(expr); distance_iter != locals_.end()) 121 | environment_->assign_at(distance_iter->second, expr->name(), value); 122 | else 123 | environment_->assign(expr->name(), value); 124 | } 125 | 126 | void Interpreter::visit(const SetExprPtr& expr) { 127 | if (auto object = evaluate(expr->object()); object.is_instance()) { 128 | Value value = evaluate(expr->value()); 129 | object.as_instance()->set_attr(expr->name(), value); 130 | } 131 | else { 132 | throw RuntimeError(expr->name(), "only instances have attributes"); 133 | } 134 | } 135 | 136 | void Interpreter::visit(const LogicalExprPtr& expr) { 137 | Value lhs = evaluate(expr->lhs()); 138 | if (expr->oper().kind() == TokenKind::KW_OR) { 139 | if (lhs.is_truthy()) { 140 | value_ = lhs; 141 | return; 142 | } 143 | } 144 | else { 145 | if (!lhs.is_truthy()) { 146 | value_ = lhs; 147 | return; 148 | } 149 | } 150 | value_ = evaluate(expr->rhs()); 151 | } 152 | 153 | void Interpreter::visit(const BinaryExprPtr& expr) { 154 | Value lhs = evaluate(expr->lhs()); 155 | Value rhs = evaluate(expr->rhs()); 156 | const Token& oper = expr->oper(); 157 | 158 | #define BINARYOP(op) do {\ 159 | check_numerics(oper, lhs, rhs);\ 160 | value_ = lhs op rhs;\ 161 | } while (false) 162 | 163 | switch (oper.kind()) { 164 | case TokenKind::TK_PLUS: 165 | check_plus(oper, lhs, rhs); value_ = lhs + rhs; break; 166 | case TokenKind::TK_MINUS: BINARYOP(-); break; 167 | case TokenKind::TK_STAR: BINARYOP(*); break; 168 | case TokenKind::TK_SLASH: BINARYOP(/); break; 169 | case TokenKind::TK_GT: BINARYOP(>); break; 170 | case TokenKind::TK_GTEQ: BINARYOP(>=); break; 171 | case TokenKind::TK_LT: BINARYOP(<); break; 172 | case TokenKind::TK_LTEQ: BINARYOP(<=); break; 173 | case TokenKind::TK_BANGEQ: value_ = lhs != rhs; break; 174 | case TokenKind::TK_EQEQ: value_ = lhs == rhs; break; 175 | } 176 | 177 | #undef BINARYOP 178 | } 179 | 180 | void Interpreter::visit(const UnaryExprPtr& expr) { 181 | Value value = evaluate(expr->rhs()); 182 | const Token& oper = expr->oper(); 183 | 184 | switch (oper.kind()) { 185 | case TokenKind::TK_BANG: value_ = !value; break; 186 | case TokenKind::TK_MINUS: check_numeric(oper, value); value_ = -value; break; 187 | } 188 | } 189 | 190 | void Interpreter::visit(const CallExprPtr& expr) { 191 | Value callee = evaluate(expr->callee()); 192 | if (!callee.is_callable()) 193 | throw RuntimeError(expr->paren(), "can only call functions and classes"); 194 | 195 | std::vector args; 196 | for (auto& arg : expr->arguments()) 197 | args.push_back(evaluate(arg)); 198 | CallablePtr callable = callee.as_callable(); 199 | 200 | if (callable->check_arity() && callable->arity() != args.size()) { 201 | throw RuntimeError(expr->paren(), 202 | "expected " + std::to_string(callable->arity()) + " " + 203 | "arguments but got " + std::to_string(args.size())); 204 | } 205 | else { 206 | value_ = callable->call(shared_from_this(), args); 207 | } 208 | } 209 | 210 | void Interpreter::visit(const GetExprPtr& expr) { 211 | if (auto object = evaluate(expr->object()); object.is_instance()) 212 | value_ = object.as_instance()->get_attr(expr->name()); 213 | else 214 | throw RuntimeError(expr->name(), "only instances have attributes"); 215 | } 216 | 217 | void Interpreter::visit(const LiteralExprPtr& expr) { 218 | value_ = expr->value(); 219 | } 220 | 221 | void Interpreter::visit(const GroupingExprPtr& expr) { 222 | (void)evaluate(expr->expression()); 223 | } 224 | 225 | void Interpreter::visit(const SuperExprPtr& expr) { 226 | int distance = 0; 227 | if (auto super_iter = locals_.find(expr); super_iter != locals_.end()) 228 | distance = super_iter->second; 229 | 230 | const Token& method_name = expr->method(); 231 | ClassPtr superclass = std::static_pointer_cast( 232 | environment_->get_at(distance, "super").as_callable()); 233 | InstancePtr object = std::static_pointer_cast( 234 | environment_->get_at(distance - 1, "this").as_instance()); 235 | 236 | if (auto method = superclass->get_method(object, method_name); method) { 237 | value_ = Value(method); 238 | } 239 | else { 240 | throw RuntimeError(method_name, 241 | "undefined method `" + method_name.as_string() + "`"); 242 | } 243 | } 244 | 245 | void Interpreter::visit(const ThisExprPtr& expr) { 246 | value_ = lookup_variable(expr->keyword(), expr); 247 | } 248 | 249 | void Interpreter::visit(const VariableExprPtr& expr) { 250 | value_ = environment_->get(expr->name()); 251 | } 252 | 253 | void Interpreter::visit(const FunctionExprPtr& expr) { 254 | } 255 | 256 | void Interpreter::visit(const ClassStmtPtr& stmt) { 257 | ClassPtr superclass; 258 | Value superval; 259 | const ExprPtr& superexp = stmt->superclass(); 260 | if (superexp) { 261 | superval = evaluate(superexp); 262 | if (superval.is_callable() && 263 | std::dynamic_pointer_cast(superval.as_callable())) { 264 | superclass = std::static_pointer_cast(superval.as_callable()); 265 | } 266 | else { 267 | throw RuntimeError(stmt->name(), "superclass must be a class"); 268 | } 269 | } 270 | 271 | environment_->define(stmt->name(), nullptr); 272 | 273 | if (superexp) { 274 | environment_ = std::make_shared(environment_); 275 | environment_->define("super", superval); 276 | } 277 | 278 | std::unordered_map methods; 279 | for (auto& meth : stmt->methods()) { 280 | str_t method_name = meth->name().as_string(); 281 | bool is_ctor = method_name == "ctor"; 282 | methods[method_name] = 283 | std::make_shared(meth, environment_, is_ctor); 284 | } 285 | 286 | auto cls = std::make_shared( 287 | stmt->name().as_string(), superclass, methods); 288 | if (!superval.is_nil()) 289 | environment_ = environment_->get_enclosing(); 290 | environment_->assign(stmt->name(), Value(cls)); 291 | } 292 | 293 | void Interpreter::visit(const FunctionStmtPtr& stmt) { 294 | Value fun(std::make_shared(stmt, environment_, false)); 295 | environment_->define(stmt->name(), fun); 296 | } 297 | 298 | void Interpreter::visit(const VarStmtPtr& stmt) { 299 | Value value; 300 | if (stmt->expr()) 301 | value = evaluate(stmt->expr()); 302 | environment_->define(stmt->name(), value); 303 | } 304 | 305 | void Interpreter::visit(const BlockStmtPtr& stmt) { 306 | evaluate(stmt->stmts(), std::make_shared(environment_)); 307 | } 308 | 309 | void Interpreter::visit(const ExprStmtPtr& stmt) { 310 | (void)evaluate(stmt->expr()); 311 | } 312 | 313 | void Interpreter::visit(const WhileStmtPtr& stmt) { 314 | while (evaluate(stmt->cond()).is_truthy()) 315 | evaluate(stmt->body()); 316 | } 317 | 318 | void Interpreter::visit(const IfStmtPtr& stmt) { 319 | if (evaluate(stmt->cond()).is_truthy()) 320 | evaluate(stmt->then_branch()); 321 | else if (stmt->else_branch()) 322 | evaluate(stmt->else_branch()); 323 | } 324 | 325 | void Interpreter::visit(const PrintStmtPtr& stmt) { 326 | for (auto& expr : stmt->exprs()) 327 | std::cout << evaluate(expr) << " "; 328 | std::cout << std::endl; 329 | } 330 | 331 | void Interpreter::visit(const ReturnStmtPtr& stmt) { 332 | Value value; 333 | if (stmt->value()) 334 | value = evaluate(stmt->value()); 335 | 336 | throw Return(value); 337 | } 338 | 339 | } 340 | -------------------------------------------------------------------------------- /bytecc_value.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include 28 | #include "bytecc_chunk.hh" 29 | #include "bytecc_vm.hh" 30 | #include "bytecc_value.hh" 31 | 32 | namespace loxcc::bytecc { 33 | 34 | template 35 | inline T* make_object(VM& vm, Args&&... args) { 36 | auto* o = new T(std::forward(args)...); 37 | vm.append_object(o); 38 | return o; 39 | } 40 | 41 | StringObject* TagValue::as_string(void) const { 42 | return Xt::down(as_object()); 43 | } 44 | 45 | const char* TagValue::as_cstring(void) const { 46 | return Xt::down(as_object())->cstr(); 47 | } 48 | 49 | NativeObject* TagValue::as_native(void) const { 50 | return Xt::down(as_object()); 51 | } 52 | 53 | FunctionObject* TagValue::as_function(void) const { 54 | return Xt::down(as_object()); 55 | } 56 | 57 | UpvalueObject* TagValue::as_upvalue(void) const { 58 | return Xt::down(as_object()); 59 | } 60 | 61 | ClosureObject* TagValue::as_closure(void) const { 62 | return Xt::down(as_object()); 63 | } 64 | 65 | ClassObject* TagValue::as_class(void) const { 66 | return Xt::down(as_object()); 67 | } 68 | 69 | InstanceObject* TagValue::as_instance(void) const { 70 | return Xt::down(as_object()); 71 | } 72 | 73 | BoundMehtodObject* TagValue::as_bound_method(void) const { 74 | return Xt::down(as_object()); 75 | } 76 | 77 | bool TagValue::is_truthy(void) const { 78 | if (is_nil()) 79 | return false; 80 | else if (is_boolean()) 81 | return as_boolean(); 82 | else if (is_numeric()) 83 | return as_numeric() != 0.f; 84 | else if (is_object()) 85 | return as_object()->is_truthy(); 86 | return false; 87 | } 88 | 89 | str_t TagValue::stringify(void) const { 90 | if (is_nil()) 91 | return "nil"; 92 | else if (is_boolean()) 93 | return as_boolean() ? "true" : "false"; 94 | else if (is_numeric()) 95 | return Xt::to_string(as_numeric()); 96 | else if (is_object()) 97 | return as_object()->stringify(); 98 | return ""; 99 | } 100 | 101 | StringObject* ObjValue::as_string(void) const { 102 | return Xt::down(as_.object); 103 | } 104 | 105 | const char* ObjValue::as_cstring(void) const { 106 | return Xt::down(as_.object)->cstr(); 107 | } 108 | 109 | NativeObject* ObjValue::as_native(void) const { 110 | return Xt::down(as_.object); 111 | } 112 | 113 | FunctionObject* ObjValue::as_function(void) const { 114 | return Xt::down(as_.object); 115 | } 116 | 117 | UpvalueObject* ObjValue::as_upvalue(void) const { 118 | return Xt::down(as_.object); 119 | } 120 | 121 | ClosureObject* ObjValue::as_closure(void) const { 122 | return Xt::down(as_.object); 123 | } 124 | 125 | ClassObject* ObjValue::as_class(void) const { 126 | return Xt::down(as_.object); 127 | } 128 | 129 | InstanceObject* ObjValue::as_instance(void) const { 130 | return Xt::down(as_.object); 131 | } 132 | 133 | BoundMehtodObject* ObjValue::as_bound_method(void) const { 134 | return Xt::down(as_.object); 135 | } 136 | 137 | bool ObjValue::operator==(const ObjValue& r) const { 138 | if (this == &r) 139 | return true; 140 | if (type_ != r.type_) 141 | return false; 142 | 143 | switch (type_) { 144 | case ValueType::NIL: return true; 145 | case ValueType::BOOLEAN: return as_.boolean == r.as_.boolean; 146 | case ValueType::NUMERIC: return as_.numeric == r.as_.numeric; 147 | case ValueType::OBJECT: return as_.object == r.as_.object; // TODO: FIXME: 148 | } 149 | return false; 150 | } 151 | 152 | bool ObjValue::operator!=(const ObjValue& r) const { 153 | return !(*this == r); 154 | } 155 | 156 | bool ObjValue::is_truthy(void) const { 157 | switch (type_) { 158 | case ValueType::NIL: return false; 159 | case ValueType::BOOLEAN: return as_.boolean; 160 | case ValueType::NUMERIC: return as_.numeric == 0.f; 161 | case ValueType::OBJECT: return as_.object->is_truthy(); 162 | } 163 | return false; 164 | } 165 | 166 | str_t ObjValue::stringify(void) const { 167 | switch (type_) { 168 | case ValueType::NIL: return "nil"; 169 | case ValueType::BOOLEAN: return as_.boolean ? "true" : "false"; 170 | case ValueType::NUMERIC: return Xt::to_string(as_.numeric); 171 | case ValueType::OBJECT: return as_.object->stringify(); 172 | } 173 | return ""; 174 | } 175 | 176 | StringObject::StringObject( 177 | const char* s, int n, u32_t h, bool replace_owner) noexcept 178 | : BaseObject(ObjType::STRING) 179 | , size_(n) 180 | , hash_(h) { 181 | if (replace_owner) { 182 | data_ = Xt::as_ptr(s); 183 | } 184 | else { 185 | data_ = new char[Xt::as_type(size_) + 1]; 186 | memcpy(data_, s, size_); 187 | data_[size_] = 0; 188 | } 189 | } 190 | 191 | StringObject::~StringObject(void) { 192 | delete [] data_; 193 | } 194 | 195 | str_t StringObject::stringify(void) const { 196 | return data_; 197 | } 198 | 199 | StringObject* StringObject::create(VM& vm, const str_t& s) { 200 | return create(vm, s.c_str(), Xt::as_type(s.size())); 201 | } 202 | 203 | StringObject* StringObject::create(VM& vm, const char* s, int n) { 204 | u32_t h = Xt::hasher(s, n); 205 | if (auto* o = vm.get_interned(h); o != nullptr) 206 | return o; 207 | 208 | auto* o = make_object(vm, s, n, h); 209 | vm.set_interned(h, o); 210 | return o; 211 | } 212 | 213 | StringObject* StringObject::concat(VM& vm, StringObject* a, StringObject* b) { 214 | int n = a->size() + b->size(); 215 | char* s = new char[Xt::as_type(n) + 1]; 216 | memcpy(s, a->data(), a->size()); 217 | memcpy(s + a->size(), b->data(), b->size()); 218 | s[n] = 0; 219 | 220 | u32_t h = Xt::hasher(s, n); 221 | if (auto* o = vm.get_interned(h); o != nullptr) { 222 | delete [] s; 223 | return o; 224 | } 225 | 226 | auto* o = make_object(vm, s, n, h, true); 227 | vm.set_interned(h, o); 228 | return o; 229 | } 230 | 231 | NativeObject::NativeObject(const NativeFn& fn) noexcept 232 | : BaseObject(ObjType::NATIVE) 233 | , fn_(fn) { 234 | } 235 | 236 | NativeObject::NativeObject(NativeFn&& fn) noexcept 237 | : BaseObject(ObjType::NATIVE) 238 | , fn_(std::move(fn)) { 239 | } 240 | 241 | str_t NativeObject::stringify(void) const { 242 | std::stringstream ss; 243 | ss << ""; 244 | return ss.str(); 245 | } 246 | 247 | NativeObject* NativeObject::create(VM& vm, const NativeFn& fn) { 248 | return make_object(vm, fn); 249 | } 250 | 251 | NativeObject* NativeObject::create(VM& vm, NativeFn&& fn) { 252 | return make_object(vm, std::move(fn)); 253 | } 254 | 255 | FunctionObject::FunctionObject(StringObject* name) noexcept 256 | : BaseObject(ObjType::FUNCTION) 257 | , name_(name) 258 | , chunk_(new Chunk()) { 259 | } 260 | 261 | FunctionObject::~FunctionObject(void) { 262 | delete chunk_; 263 | } 264 | 265 | str_t FunctionObject::stringify(void) const { 266 | std::stringstream ss; 267 | ss << ""; 268 | return ss.str(); 269 | } 270 | 271 | void FunctionObject::blacken(VM& vm) { 272 | vm.mark_object(name_); 273 | chunk_->iter_constants([&vm](const Value& v) { vm.mark_value(v); }); 274 | } 275 | 276 | FunctionObject* FunctionObject::create(VM& vm, StringObject* name) { 277 | return make_object(vm, name); 278 | } 279 | 280 | UpvalueObject::UpvalueObject(Value* value, UpvalueObject* next) noexcept 281 | : BaseObject(ObjType::UPVALUE) 282 | , value_(value) 283 | , next_(next) { 284 | } 285 | 286 | str_t UpvalueObject::stringify(void) const { 287 | return ""; 288 | } 289 | 290 | void UpvalueObject::blacken(VM& vm) { 291 | vm.mark_value(closed_); 292 | } 293 | 294 | UpvalueObject* UpvalueObject::create( 295 | VM& vm, Value* value, UpvalueObject* next) { 296 | return make_object(vm, value, next); 297 | } 298 | 299 | ClosureObject::ClosureObject(FunctionObject* fn) noexcept 300 | : BaseObject(ObjType::CLOSURE) 301 | , fn_(fn) 302 | , upvalues_count_(fn->upvalues_count()) { 303 | if (upvalues_count_ > 0) { 304 | upvalues_ = new UpvalueObject*[upvalues_count_]; 305 | for (int i = 0; i < upvalues_count_; ++i) 306 | upvalues_[i] = nullptr; 307 | } 308 | } 309 | 310 | ClosureObject::~ClosureObject(void) { 311 | if (upvalues_ != nullptr) 312 | delete [] upvalues_; 313 | } 314 | 315 | str_t ClosureObject::stringify(void) const { 316 | std::stringstream ss; 317 | ss << "name_astr() << "` at `" << this << "`>"; 318 | return ss.str(); 319 | } 320 | 321 | void ClosureObject::blacken(VM& vm) { 322 | vm.mark_object(fn_); 323 | for (int i = 0; i < upvalues_count_; ++i) 324 | vm.mark_object(upvalues_[i]); 325 | } 326 | 327 | ClosureObject* ClosureObject::create(VM& vm, FunctionObject* fn) { 328 | return make_object(vm, fn); 329 | } 330 | 331 | ClassObject::ClassObject(StringObject* name) noexcept 332 | : BaseObject(ObjType::CLASS) 333 | , name_(name) { 334 | } 335 | 336 | void ClassObject::inherit_from(ClassObject* superclass) { 337 | for (auto& method : superclass->methods_) 338 | methods_[method.first] = method.second; 339 | } 340 | 341 | str_t ClassObject::stringify(void) const { 342 | std::stringstream ss; 343 | ss << "cstr() << "`>"; 344 | return ss.str(); 345 | } 346 | 347 | void ClassObject::blacken(VM& vm) { 348 | vm.mark_value(name_); 349 | for (auto& m : methods_) { 350 | vm.mark_value(m.second); 351 | } 352 | } 353 | 354 | ClassObject* ClassObject::create(VM& vm, StringObject* name) { 355 | return make_object(vm, name); 356 | } 357 | 358 | InstanceObject::InstanceObject(ClassObject* cls) noexcept 359 | : BaseObject(ObjType::INSTANCE) 360 | , cls_(cls) { 361 | } 362 | 363 | str_t InstanceObject::stringify(void) const { 364 | std::stringstream ss; 365 | ss << "<`" << cls_->name_astr() << "` object at `" << this << "`>"; 366 | return ss.str(); 367 | } 368 | 369 | void InstanceObject::blacken(VM& vm) { 370 | vm.mark_object(cls_); 371 | for (auto& attr : attrs_) 372 | vm.mark_value(attr.second); 373 | } 374 | 375 | InstanceObject* InstanceObject::create(VM& vm, ClassObject* cls) { 376 | return make_object(vm, cls); 377 | } 378 | 379 | BoundMehtodObject::BoundMehtodObject( 380 | const Value& owner, ClosureObject* method) noexcept 381 | : BaseObject(ObjType::BOUND_METHOD) 382 | , owner_(owner) 383 | , method_(method) { 384 | } 385 | 386 | str_t BoundMehtodObject::stringify(void) const { 387 | std::stringstream ss; 388 | 389 | InstanceObject* inst = owner_.as_instance(); 390 | ss << "fn()->name_astr() << "` of " 392 | << inst->stringify() << ">"; 393 | return ss.str(); 394 | } 395 | 396 | void BoundMehtodObject::blacken(VM& vm) { 397 | vm.mark_value(owner_); 398 | vm.mark_object(method_); 399 | } 400 | 401 | BoundMehtodObject* BoundMehtodObject::create( 402 | VM& vm, const Value& owner, ClosureObject* method) { 403 | return make_object(vm, owner, method); 404 | } 405 | 406 | } 407 | -------------------------------------------------------------------------------- /interpret_ast.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include "common.hh" 32 | #include "token.hh" 33 | #include "interpret_value.hh" 34 | 35 | namespace loxcc::interpret { 36 | 37 | struct Expr; 38 | struct Stmt; 39 | struct ExprVisitor; 40 | struct StmtVisitor; 41 | 42 | using ExprPtr = std::shared_ptr; 43 | using StmtPtr = std::shared_ptr; 44 | using ExprVisitorPtr = std::shared_ptr; 45 | using StmtVisitorPtr = std::shared_ptr; 46 | 47 | class AssignExpr; 48 | class SetExpr; 49 | class LogicalExpr; 50 | class BinaryExpr; 51 | class UnaryExpr; 52 | class CallExpr; 53 | class GetExpr; 54 | class LiteralExpr; 55 | class GroupingExpr; 56 | class SuperExpr; 57 | class ThisExpr; 58 | class VariableExpr; 59 | class FunctionExpr; 60 | 61 | class ClassStmt; 62 | class FunctionStmt; 63 | class VarStmt; 64 | class BlockStmt; 65 | class ExprStmt; 66 | class WhileStmt; 67 | class IfStmt; 68 | class PrintStmt; 69 | class ReturnStmt; 70 | 71 | using AssignExprPtr = std::shared_ptr; 72 | using SetExprPtr = std::shared_ptr; 73 | using LogicalExprPtr = std::shared_ptr; 74 | using BinaryExprPtr = std::shared_ptr; 75 | using UnaryExprPtr = std::shared_ptr; 76 | using CallExprPtr = std::shared_ptr; 77 | using GetExprPtr = std::shared_ptr; 78 | using LiteralExprPtr = std::shared_ptr; 79 | using GroupingExprPtr = std::shared_ptr; 80 | using SuperExprPtr = std::shared_ptr; 81 | using ThisExprPtr = std::shared_ptr; 82 | using VariableExprPtr = std::shared_ptr; 83 | using FunctionExprPtr = std::shared_ptr; 84 | 85 | using ClassStmtPtr = std::shared_ptr; 86 | using FunctionStmtPtr = std::shared_ptr; 87 | using VarStmtPtr = std::shared_ptr; 88 | using BlockStmtPtr = std::shared_ptr; 89 | using ExprStmtPtr = std::shared_ptr; 90 | using WhileStmtPtr = std::shared_ptr; 91 | using IfStmtPtr = std::shared_ptr; 92 | using PrintStmtPtr = std::shared_ptr; 93 | using ReturnStmtPtr = std::shared_ptr; 94 | 95 | struct ExprVisitor : private UnCopyable { 96 | virtual ~ExprVisitor(void) {} 97 | 98 | virtual void visit(const AssignExprPtr& expr) = 0; 99 | virtual void visit(const SetExprPtr& expr) = 0; 100 | virtual void visit(const LogicalExprPtr& expr) = 0; 101 | virtual void visit(const BinaryExprPtr& expr) = 0; 102 | virtual void visit(const UnaryExprPtr& expr) = 0; 103 | virtual void visit(const CallExprPtr& expr) = 0; 104 | virtual void visit(const GetExprPtr& expr) = 0; 105 | virtual void visit(const LiteralExprPtr& expr) = 0; 106 | virtual void visit(const GroupingExprPtr& expr) = 0; 107 | virtual void visit(const SuperExprPtr& expr) = 0; 108 | virtual void visit(const ThisExprPtr& expr) = 0; 109 | virtual void visit(const VariableExprPtr& expr) = 0; 110 | virtual void visit(const FunctionExprPtr& expr) = 0; 111 | }; 112 | 113 | struct StmtVisitor : private UnCopyable { 114 | virtual ~StmtVisitor(void) {} 115 | 116 | virtual void visit(const ClassStmtPtr& stmt) = 0; 117 | virtual void visit(const FunctionStmtPtr& stmt) = 0; 118 | virtual void visit(const VarStmtPtr& stmt) = 0; 119 | virtual void visit(const BlockStmtPtr& stmt) = 0; 120 | virtual void visit(const ExprStmtPtr& stmt) = 0; 121 | virtual void visit(const WhileStmtPtr& stmt) = 0; 122 | virtual void visit(const IfStmtPtr& stmt) = 0; 123 | virtual void visit(const PrintStmtPtr& stmt) = 0; 124 | virtual void visit(const ReturnStmtPtr& stmt) = 0; 125 | }; 126 | 127 | struct Expr : private UnCopyable { 128 | virtual ~Expr(void) {} 129 | virtual void accept(const ExprVisitorPtr& visitor) = 0; 130 | }; 131 | 132 | struct Stmt : private UnCopyable { 133 | virtual ~Stmt(void) {} 134 | virtual void accept(const StmtVisitorPtr& visitor) = 0; 135 | }; 136 | 137 | class AssignExpr final 138 | : public Expr, public std::enable_shared_from_this { 139 | Token name_; 140 | Token oper_; 141 | ExprPtr value_; 142 | public: 143 | inline const Token& name(void) const { return name_; } 144 | inline const Token& oper(void) const { return oper_; } 145 | inline const ExprPtr& value(void) const { return value_; } 146 | 147 | AssignExpr( 148 | const Token& name, const Token& oper, const ExprPtr& value) noexcept 149 | : name_(name), oper_(oper), value_(value) { 150 | } 151 | 152 | virtual void accept(const ExprVisitorPtr& visitor) override; 153 | }; 154 | 155 | class SetExpr final 156 | : public Expr, public std::enable_shared_from_this { 157 | ExprPtr object_; 158 | Token name_; 159 | ExprPtr value_; 160 | public: 161 | inline const ExprPtr& object(void) const { return object_; } 162 | inline const Token& name(void) const { return name_; } 163 | inline const ExprPtr& value(void) const { return value_; } 164 | 165 | SetExpr(const ExprPtr& object, 166 | const Token& name, const ExprPtr& value) noexcept 167 | : object_(object), name_(name), value_(value) { 168 | } 169 | 170 | virtual void accept(const ExprVisitorPtr& visitor) override; 171 | }; 172 | 173 | class LogicalExpr final 174 | : public Expr, public std::enable_shared_from_this { 175 | ExprPtr lhs_; 176 | Token oper_; 177 | ExprPtr rhs_; 178 | public: 179 | inline const ExprPtr& lhs(void) const { return lhs_; } 180 | inline const Token& oper(void) const { return oper_; } 181 | inline const ExprPtr& rhs(void) const { return rhs_; } 182 | 183 | LogicalExpr( 184 | const ExprPtr& lhs, const Token& oper, const ExprPtr& rhs) noexcept 185 | : lhs_(lhs), oper_(oper), rhs_(rhs) { 186 | } 187 | 188 | virtual void accept(const ExprVisitorPtr& visitor) override; 189 | }; 190 | 191 | class BinaryExpr final 192 | : public Expr, public std::enable_shared_from_this { 193 | ExprPtr lhs_; 194 | Token oper_; 195 | ExprPtr rhs_; 196 | public: 197 | inline const ExprPtr& lhs(void) const { return lhs_; } 198 | inline const Token& oper(void) const { return oper_; } 199 | inline const ExprPtr& rhs(void) const { return rhs_; } 200 | 201 | BinaryExpr( 202 | const ExprPtr& lhs, const Token& oper, const ExprPtr& rhs) noexcept 203 | : lhs_(lhs), oper_(oper), rhs_(rhs) { 204 | } 205 | 206 | virtual void accept(const ExprVisitorPtr& visitor) override; 207 | }; 208 | 209 | class UnaryExpr final 210 | : public Expr, public std::enable_shared_from_this { 211 | Token oper_; 212 | ExprPtr rhs_; 213 | public: 214 | inline const Token& oper(void) const { return oper_; } 215 | inline const ExprPtr& rhs(void) const { return rhs_; } 216 | 217 | UnaryExpr(const Token& oper, const ExprPtr& rhs) noexcept 218 | : oper_(oper), rhs_(rhs) { 219 | } 220 | 221 | virtual void accept(const ExprVisitorPtr& visitor) override; 222 | }; 223 | 224 | class CallExpr final 225 | : public Expr, public std::enable_shared_from_this { 226 | ExprPtr callee_; 227 | Token paren_; 228 | std::vector arguments_; 229 | public: 230 | inline const ExprPtr& callee(void) const { return callee_; } 231 | inline const Token& paren(void) const { return paren_; } 232 | inline const std::vector& arguments(void) const { return arguments_; } 233 | 234 | CallExpr(const ExprPtr& callee, 235 | const Token& paren, const std::vector& arguments) noexcept 236 | : callee_(callee), paren_(paren), arguments_(arguments) { 237 | } 238 | 239 | virtual void accept(const ExprVisitorPtr& visitor) override; 240 | }; 241 | 242 | class GetExpr final 243 | : public Expr, public std::enable_shared_from_this { 244 | ExprPtr object_; 245 | Token name_; 246 | public: 247 | inline const ExprPtr& object(void) const { return object_; } 248 | inline const Token& name(void) const { return name_; } 249 | 250 | GetExpr(const ExprPtr& object, const Token& name) noexcept 251 | : object_(object), name_(name) { 252 | } 253 | 254 | virtual void accept(const ExprVisitorPtr& visitor) override; 255 | }; 256 | 257 | class LiteralExpr final 258 | : public Expr, public std::enable_shared_from_this { 259 | Value value_; 260 | public: 261 | inline const Value& value(void) const { return value_; } 262 | 263 | LiteralExpr(const Value& value) noexcept 264 | : value_(value) { 265 | } 266 | 267 | virtual void accept(const ExprVisitorPtr& visitor) override; 268 | }; 269 | 270 | class GroupingExpr final 271 | : public Expr, public std::enable_shared_from_this { 272 | ExprPtr expression_; 273 | public: 274 | inline const ExprPtr& expression(void) const { return expression_; } 275 | 276 | GroupingExpr(const ExprPtr& expression) noexcept 277 | : expression_(expression) { 278 | } 279 | 280 | virtual void accept(const ExprVisitorPtr& visitor) override; 281 | }; 282 | 283 | class SuperExpr final 284 | : public Expr, public std::enable_shared_from_this { 285 | Token keyword_; 286 | Token method_; 287 | public: 288 | inline const Token& keyword(void) const { return keyword_; } 289 | inline const Token& method(void) const { return method_; } 290 | 291 | SuperExpr(const Token& keyword, const Token& method) noexcept 292 | : keyword_(keyword), method_(method) { 293 | } 294 | 295 | virtual void accept(const ExprVisitorPtr& visitor) override; 296 | }; 297 | 298 | class ThisExpr final 299 | : public Expr, public std::enable_shared_from_this { 300 | Token keyword_; 301 | public: 302 | inline const Token& keyword(void) const { return keyword_; } 303 | 304 | ThisExpr(const Token& keyword) noexcept 305 | : keyword_(keyword) { 306 | } 307 | 308 | virtual void accept(const ExprVisitorPtr& visitor) override; 309 | }; 310 | 311 | class VariableExpr final 312 | : public Expr, public std::enable_shared_from_this { 313 | Token name_; 314 | public: 315 | inline const Token& name(void) const { return name_; } 316 | 317 | VariableExpr(const Token& name) noexcept 318 | : name_(name) { 319 | } 320 | 321 | virtual void accept(const ExprVisitorPtr& visitor) override; 322 | }; 323 | 324 | class FunctionExpr final 325 | : public Expr, public std::enable_shared_from_this { 326 | std::vector params_; 327 | std::vector body_; 328 | public: 329 | inline const std::vector& params(void) const { return params_; } 330 | inline const std::vector& body(void) const { return body_; } 331 | 332 | FunctionExpr(const std::vector& params, 333 | const std::vector& body) noexcept 334 | : params_(params), body_(body) { 335 | } 336 | 337 | virtual void accept(const ExprVisitorPtr& visitor) override; 338 | }; 339 | 340 | class ClassStmt final 341 | : public Stmt, public std::enable_shared_from_this { 342 | Token name_; 343 | ExprPtr superclass_; 344 | std::vector methods_; 345 | public: 346 | inline const Token& name(void) const { return name_; } 347 | inline const ExprPtr& superclass(void) const { return superclass_; } 348 | inline const std::vector& methods(void) const { return methods_; } 349 | 350 | ClassStmt(const Token& name, 351 | const ExprPtr& superclass, 352 | const std::vector& methods) noexcept 353 | : name_(name), superclass_(superclass), methods_(methods) { 354 | } 355 | 356 | virtual void accept(const StmtVisitorPtr& visitor) override; 357 | }; 358 | 359 | class FunctionStmt final 360 | : public Stmt, public std::enable_shared_from_this { 361 | Token name_; 362 | std::vector params_; 363 | std::vector body_; 364 | public: 365 | inline const Token& name(void) const { return name_; } 366 | inline const std::vector& params(void) const { return params_; } 367 | inline const std::vector& body(void) const { return body_; } 368 | 369 | FunctionStmt(const Token& name, 370 | const std::vector& params, 371 | const std::vector& body) noexcept 372 | : name_(name), params_(params), body_(body) { 373 | } 374 | 375 | virtual void accept(const StmtVisitorPtr& visitor) override; 376 | }; 377 | 378 | class VarStmt final 379 | : public Stmt, public std::enable_shared_from_this { 380 | Token name_; 381 | ExprPtr expr_; 382 | public: 383 | inline const Token& name(void) const { return name_; } 384 | inline const ExprPtr& expr(void) const { return expr_; } 385 | 386 | VarStmt(const Token& name, const ExprPtr& expr) noexcept 387 | : name_(name), expr_(expr) { 388 | } 389 | 390 | virtual void accept(const StmtVisitorPtr& visitor) override; 391 | }; 392 | 393 | class BlockStmt final 394 | : public Stmt, public std::enable_shared_from_this { 395 | std::vector stmts_; 396 | public: 397 | inline const std::vector& stmts(void) const { return stmts_; } 398 | 399 | BlockStmt(const std::vector& stmts) noexcept 400 | : stmts_(stmts) { 401 | } 402 | 403 | virtual void accept(const StmtVisitorPtr& visitor) override; 404 | }; 405 | 406 | class ExprStmt final 407 | : public Stmt, public std::enable_shared_from_this { 408 | ExprPtr expr_; 409 | public: 410 | inline const ExprPtr& expr(void) const { return expr_; } 411 | 412 | ExprStmt(const ExprPtr& expr) noexcept 413 | : expr_(expr) { 414 | } 415 | 416 | virtual void accept(const StmtVisitorPtr& visitor) override; 417 | }; 418 | 419 | class WhileStmt final 420 | : public Stmt, public std::enable_shared_from_this { 421 | ExprPtr cond_; 422 | StmtPtr body_; 423 | public: 424 | inline const ExprPtr& cond(void) const { return cond_; } 425 | inline const StmtPtr& body(void) const { return body_; } 426 | 427 | WhileStmt(const ExprPtr& cond, const StmtPtr& body) noexcept 428 | : cond_(cond), body_(body) { 429 | } 430 | 431 | virtual void accept(const StmtVisitorPtr& visitor) override; 432 | }; 433 | 434 | class IfStmt final 435 | : public Stmt, public std::enable_shared_from_this { 436 | ExprPtr cond_; 437 | StmtPtr then_branch_; 438 | StmtPtr else_branch_; 439 | public: 440 | inline const ExprPtr& cond(void) const { return cond_; } 441 | inline const StmtPtr& then_branch(void) const { return then_branch_; } 442 | inline const StmtPtr& else_branch(void) const { return else_branch_; } 443 | 444 | IfStmt(const ExprPtr& cond, 445 | const StmtPtr& then_branch, const StmtPtr& else_branch) noexcept 446 | : cond_(cond), then_branch_(then_branch), else_branch_(else_branch) { 447 | } 448 | 449 | virtual void accept(const StmtVisitorPtr& visitor) override; 450 | }; 451 | 452 | class PrintStmt final 453 | : public Stmt, public std::enable_shared_from_this { 454 | std::vector exprs_; 455 | public: 456 | inline const std::vector& exprs(void) const { return exprs_; } 457 | 458 | PrintStmt(const std::vector& exprs) noexcept 459 | : exprs_(exprs) { 460 | } 461 | 462 | virtual void accept(const StmtVisitorPtr& visitor) override; 463 | }; 464 | 465 | class ReturnStmt final 466 | : public Stmt, public std::enable_shared_from_this { 467 | Token keyword_; 468 | ExprPtr value_; 469 | public: 470 | inline const Token& keyword(void) const { return keyword_; } 471 | inline const ExprPtr& value(void) const { return value_; } 472 | 473 | ReturnStmt(const Token& keyword, const ExprPtr& value) noexcept 474 | : keyword_(keyword), value_(value) { 475 | } 476 | 477 | virtual void accept(const StmtVisitorPtr& visitor) override; 478 | }; 479 | 480 | } 481 | -------------------------------------------------------------------------------- /interpret_parser.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include "interpret_errors.hh" 28 | #include "interpret_parser.hh" 29 | 30 | namespace loxcc::interpret { 31 | 32 | StmtPtr Parser::parse(void) { 33 | // program -> declaration* EOF ; 34 | 35 | if (!is_end()) 36 | return declaration(); 37 | return nullptr; 38 | } 39 | 40 | Token Parser::advance(void) { 41 | if (!is_end()) { 42 | prev_ = curr_; 43 | curr_ = lex_.next_token(); 44 | 45 | if (curr_.kind() == TokenKind::TK_ERR) 46 | throw RuntimeError(curr_, curr_.literal()); 47 | } 48 | return prev_; 49 | } 50 | 51 | bool Parser::check(TokenKind kind) const { 52 | return is_end() ? false : curr_.kind() == kind; 53 | } 54 | 55 | bool Parser::match(TokenKind kind) { 56 | if (!check(kind)) 57 | return false; 58 | 59 | (void)advance(); 60 | return true; 61 | } 62 | 63 | bool Parser::match(const std::initializer_list& kinds) { 64 | for (auto kind : kinds) { 65 | if (check(kind)) { 66 | (void)advance(); 67 | return true; 68 | } 69 | } 70 | return false; 71 | } 72 | 73 | Token Parser::consume(TokenKind kind, const str_t& message) { 74 | if (check(kind)) 75 | return advance(); 76 | 77 | throw RuntimeError(curr_, message); 78 | } 79 | 80 | void Parser::synchronize(void) { 81 | (void)advance(); 82 | 83 | while (!is_end()) { 84 | if (prev_.kind() == TokenKind::TK_SEMI) 85 | return; 86 | 87 | switch (curr_.kind()) { 88 | case TokenKind::KW_CLASS: 89 | case TokenKind::KW_FUN: 90 | case TokenKind::KW_FOR: 91 | case TokenKind::KW_IF: 92 | case TokenKind::KW_PRINT: 93 | case TokenKind::KW_RETURN: 94 | case TokenKind::KW_VAR: 95 | case TokenKind::KW_WHILE: 96 | return; 97 | } 98 | 99 | (void)advance(); 100 | } 101 | } 102 | 103 | StmtPtr Parser::declaration(void) { 104 | // declaration -> class_decl | fun_decl | var_decl | statement ; 105 | 106 | try { 107 | if (match(TokenKind::KW_CLASS)) 108 | return class_decl(); 109 | if (match(TokenKind::KW_FUN)) 110 | return fun_decl("function"); 111 | if (match(TokenKind::KW_VAR)) 112 | return var_decl(); 113 | 114 | return statement(); 115 | } 116 | catch (const RuntimeError& e) { 117 | err_report_.error(e.token(), e.message()); 118 | synchronize(); 119 | 120 | return nullptr; 121 | } 122 | } 123 | 124 | StmtPtr Parser::class_decl(void) { 125 | // class_decl -> "class" IDENTIFIER ( "<" IDENTIFIER )? "{" fun_decl* "}" ; 126 | 127 | Token name = consume(TokenKind::TK_IDENTIFIER, "expect class name"); 128 | ExprPtr superclass; 129 | if (match(TokenKind::TK_LT)) { 130 | (void)consume(TokenKind::TK_IDENTIFIER, "expect superclass name"); 131 | superclass = std::make_shared(prev_); 132 | } 133 | (void)consume(TokenKind::TK_LBRACE, "expect `{` before class body"); 134 | std::vector methods; 135 | while (!is_end() && !check(TokenKind::TK_RBRACE)) { 136 | auto method = std::static_pointer_cast(fun_decl("method")); 137 | methods.push_back(method); 138 | } 139 | (void)consume(TokenKind::TK_RBRACE, "expect `}` after class body"); 140 | 141 | return std::make_shared(name, superclass, methods); 142 | } 143 | 144 | StmtPtr Parser::fun_decl(const str_t& kind) { 145 | // fun_decl -> "fun" IDENTIFIER "(" parameters? ")" block_stmt ; 146 | // parameters -> IDENTIFIER ( "," IDENTIFIER )* ; 147 | 148 | Token name = consume(TokenKind::TK_IDENTIFIER, "expect " + kind + " name"); 149 | (void)consume(TokenKind::TK_LPAREN, "expect `(` after " + kind + " name"); 150 | std::vector params; 151 | if (!check(TokenKind::TK_RPAREN)) { 152 | do { 153 | if (params.size() >= kMaxArguments) { 154 | throw RuntimeError(curr_, 155 | "cannot have more than " + 156 | std::to_string(kMaxArguments) + " parameters"); 157 | } 158 | params.push_back( 159 | consume(TokenKind::TK_IDENTIFIER, "expect parameter name")); 160 | } while (match(TokenKind::TK_COMMA)); 161 | } 162 | (void)consume(TokenKind::TK_RPAREN, "expect `)` after " + kind + " parameters"); 163 | (void)consume(TokenKind::TK_LBRACE, "expect `{` before " + kind + " body"); 164 | auto body = block_stmt(); 165 | 166 | return std::make_shared(name, params, body); 167 | } 168 | 169 | StmtPtr Parser::var_decl(void) { 170 | // var_decl -> "var" IDENTIFIER ( "=" expression? ) ";" ; 171 | 172 | Token name = consume(TokenKind::TK_IDENTIFIER, "expect variable name"); 173 | ExprPtr expr; 174 | if (match(TokenKind::TK_EQ)) 175 | expr = expression(); 176 | (void)consume(TokenKind::TK_SEMI, "expect `;` after variable declaration"); 177 | 178 | return std::make_shared(name, expr); 179 | } 180 | 181 | StmtPtr Parser::statement(void) { 182 | // statement -> expr_stmt | for_stmt | if_stmt | print_stmt 183 | // | return_stmt | while_stmt | block_stmt ; 184 | 185 | if (match(TokenKind::KW_FOR)) 186 | return for_stmt(); 187 | if (match(TokenKind::KW_IF)) 188 | return if_stmt(); 189 | if (match(TokenKind::KW_PRINT)) 190 | return print_stmt(); 191 | if (match(TokenKind::KW_RETURN)) 192 | return return_stmt(); 193 | if (match(TokenKind::KW_WHILE)) 194 | return while_stmt(); 195 | if (match(TokenKind::TK_LBRACE)) 196 | return std::make_shared(block_stmt()); 197 | return expr_stmt(); 198 | } 199 | 200 | StmtPtr Parser::expr_stmt(void) { 201 | // expr_stmt -> expression ";" ; 202 | 203 | ExprPtr expr = expression(); 204 | (void)consume(TokenKind::TK_SEMI, "expect `;` after expression"); 205 | 206 | return std::make_shared(expr); 207 | } 208 | 209 | StmtPtr Parser::for_stmt(void) { 210 | // for_stmt -> "for" "(" ( var_decl | expr_stmt | ";" ) 211 | // expression? ";" expression? ")" statement ; 212 | 213 | (void)consume(TokenKind::TK_LPAREN, "expect `(` after keyword `for`"); 214 | StmtPtr init_clause; 215 | if (match(TokenKind::TK_SEMI)) 216 | init_clause = nullptr; 217 | else if (match(TokenKind::KW_VAR)) 218 | init_clause = var_decl(); 219 | else 220 | init_clause = expr_stmt(); 221 | ExprPtr cond_expr; 222 | if (!check(TokenKind::TK_SEMI)) 223 | cond_expr = expression(); 224 | (void)consume(TokenKind::TK_SEMI, "expect `;` after `for` loop condition"); 225 | ExprPtr iter_expr; 226 | if (!check(TokenKind::TK_RPAREN)) 227 | iter_expr = expression(); 228 | (void)consume(TokenKind::TK_RPAREN, "expect `)` after `for` loop clauses"); 229 | 230 | auto body = statement(); 231 | if (iter_expr) { 232 | body = std::make_shared( 233 | std::vector{body, std::make_shared(iter_expr)}); 234 | } 235 | if (!cond_expr) 236 | cond_expr = std::make_shared(true); 237 | body = std::make_shared(cond_expr, body); 238 | if (init_clause) 239 | body = std::make_shared(std::vector{init_clause, body}); 240 | 241 | return body; 242 | } 243 | 244 | StmtPtr Parser::if_stmt(void) { 245 | // if_stmt -> "if" "(" expression ")" statement ( "else" statement )? ; 246 | 247 | (void)consume(TokenKind::TK_LPAREN, "expect `(` after keyword `if`"); 248 | ExprPtr cond = expression(); 249 | (void)consume(TokenKind::TK_RPAREN, "expect `)` after if condition"); 250 | 251 | StmtPtr then_branch = statement(); 252 | StmtPtr else_branch; 253 | if (match(TokenKind::KW_ELSE)) 254 | else_branch = statement(); 255 | 256 | return std::make_shared(cond, then_branch, else_branch); 257 | } 258 | 259 | StmtPtr Parser::print_stmt(void) { 260 | // print_stmt -> "print" ( expression ( "," expression )* )? ";" ; 261 | 262 | std::vector exprs; 263 | if (!match(TokenKind::TK_SEMI)) { 264 | do { 265 | exprs.push_back(expression()); 266 | } while (match(TokenKind::TK_COMMA)); 267 | (void)consume(TokenKind::TK_SEMI, "expect `;` after print expressions"); 268 | } 269 | 270 | return std::make_shared(exprs); 271 | } 272 | 273 | StmtPtr Parser::return_stmt(void) { 274 | // return_stmt -> "return" expression? ";" ; 275 | 276 | Token keyword = prev_; 277 | ExprPtr value; 278 | if (!check(TokenKind::TK_SEMI)) 279 | value = expression(); 280 | (void)consume(TokenKind::TK_SEMI, "expect `;` after return value"); 281 | 282 | return std::make_shared(keyword, value); 283 | } 284 | 285 | StmtPtr Parser::while_stmt(void) { 286 | // while_stmt -> "while" "(" expression ")" statement ; 287 | 288 | (void)consume(TokenKind::TK_LPAREN, "expect `(` after keyword `while`"); 289 | ExprPtr cond = expression(); 290 | (void)consume(TokenKind::TK_RPAREN, "expect `)` after while condition"); 291 | StmtPtr body = statement(); 292 | 293 | return std::make_shared(cond, body); 294 | } 295 | 296 | std::vector Parser::block_stmt(void) { 297 | // block_stmt -> "{" declaration* "}" ; 298 | 299 | std::vector stmts; 300 | while (!is_end() && !check(TokenKind::TK_RBRACE)) 301 | stmts.push_back(declaration()); 302 | (void)consume(TokenKind::TK_RBRACE, "expect `}` after block"); 303 | 304 | return stmts; 305 | } 306 | 307 | ExprPtr Parser::expression(void) { 308 | // expression -> assignment ; 309 | 310 | return assignment(); 311 | } 312 | 313 | ExprPtr Parser::assignment(void) { 314 | // assignment -> ( call "." )? IDENTIFIER "=" assignment | logical_or ; 315 | 316 | ExprPtr expr = logical_or(); 317 | if (match(TokenKind::TK_EQ)) { 318 | const Token& oper = prev_; 319 | ExprPtr value = assignment(); 320 | 321 | if (std::dynamic_pointer_cast(expr)) { 322 | const Token& name = std::static_pointer_cast(expr)->name(); 323 | return std::make_shared(name, oper, value); 324 | } 325 | else if (std::dynamic_pointer_cast(expr)) { 326 | GetExprPtr get = std::static_pointer_cast(expr); 327 | return std::make_shared(get->object(), get->name(), value); 328 | } 329 | throw RuntimeError(oper, "invalid assignment target"); 330 | } 331 | 332 | return expr; 333 | } 334 | 335 | ExprPtr Parser::logical_or(void) { 336 | // logical_or -> logical_and ( "or" logical_and )* ; 337 | 338 | ExprPtr expr = logical_and(); 339 | while (match(TokenKind::KW_OR)) { 340 | const Token& oper = prev_; 341 | ExprPtr right = logical_and(); 342 | expr = std::make_shared(expr, oper, right); 343 | } 344 | 345 | return expr; 346 | } 347 | 348 | ExprPtr Parser::logical_and(void) { 349 | // logical_and -> equality ( "and" equality )* ; 350 | 351 | ExprPtr expr = equality(); 352 | while (match(TokenKind::KW_AND)) { 353 | const Token& oper = prev_; 354 | ExprPtr right = equality(); 355 | expr = std::make_shared(expr, oper, right); 356 | } 357 | 358 | return expr; 359 | } 360 | 361 | ExprPtr Parser::equality(void) { 362 | // equality -> comparison ( ( "!=" | "==" ) comparison )* ; 363 | 364 | ExprPtr expr = comparison(); 365 | while (match({TokenKind::TK_BANGEQ, TokenKind::TK_EQEQ})) { 366 | const Token& oper = prev_; 367 | ExprPtr right = comparison(); 368 | expr = std::make_shared(expr, oper, right); 369 | } 370 | 371 | return expr; 372 | } 373 | 374 | ExprPtr Parser::comparison(void) { 375 | // comparison -> addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ; 376 | 377 | ExprPtr expr = addition(); 378 | while (match({TokenKind::TK_GT, 379 | TokenKind::TK_GTEQ, TokenKind::TK_LT, TokenKind::TK_LTEQ})) { 380 | const Token& oper = prev_; 381 | ExprPtr right = addition(); 382 | expr = std::make_shared(expr, oper, right); 383 | } 384 | 385 | return expr; 386 | } 387 | 388 | ExprPtr Parser::addition(void) { 389 | // addition -> multiplication ( ( "+" | "-" ) multiplication )* ; 390 | 391 | ExprPtr expr = multiplication(); 392 | while (match({TokenKind::TK_PLUS, TokenKind::TK_MINUS})) { 393 | const Token& oper = prev_; 394 | ExprPtr right = multiplication(); 395 | expr = std::make_shared(expr, oper, right); 396 | } 397 | 398 | return expr; 399 | } 400 | 401 | ExprPtr Parser::multiplication(void) { 402 | // multiplication -> unary ( ( "*" | "/" ) unary )* ; 403 | 404 | ExprPtr expr = unary(); 405 | while (match({TokenKind::TK_STAR, TokenKind::TK_SLASH})) { 406 | const Token& oper = prev_; 407 | ExprPtr right = unary(); 408 | expr = std::make_shared(expr, oper, right); 409 | } 410 | 411 | return expr; 412 | } 413 | 414 | ExprPtr Parser::unary(void) { 415 | // unary -> ( "!" | "-" ) unary | call ; 416 | 417 | if (match({TokenKind::TK_BANG, TokenKind::TK_MINUS})) { 418 | const Token& oper = prev_; 419 | ExprPtr right = unary(); 420 | return std::make_shared(oper, right); 421 | } 422 | 423 | return call(); 424 | } 425 | 426 | ExprPtr Parser::call(void) { 427 | // call -> primary ( "(" arguments? ")" | "." IDENTIFIER )* ; 428 | // arguments -> expression ( "," expression )* ; 429 | 430 | auto finish_call = [](Parser& p, const ExprPtr& callee) -> ExprPtr { 431 | std::vector args; 432 | if (!p.check(TokenKind::TK_RPAREN)) { 433 | do { 434 | if (args.size() >= kMaxArguments) { 435 | throw RuntimeError(p.curr_, 436 | "cannot have more than " + 437 | std::to_string(kMaxArguments) + " arguments"); 438 | } 439 | args.push_back(p.expression()); 440 | } while (p.match(TokenKind::TK_COMMA)); 441 | } 442 | const Token& paren = p.consume( 443 | TokenKind::TK_RPAREN, "expect `)` after arguments"); 444 | return std::make_shared(callee, paren, args); 445 | }; 446 | 447 | ExprPtr expr = primary(); 448 | while (true) { 449 | if (match(TokenKind::TK_LPAREN)) { 450 | expr = finish_call(*this, expr); 451 | } 452 | else if (match(TokenKind::TK_DOT)) { 453 | const Token& name = consume( 454 | TokenKind::TK_IDENTIFIER, "expect attribute name after `.`"); 455 | expr = std::make_shared(expr, name); 456 | } 457 | else { 458 | break; 459 | } 460 | } 461 | 462 | return expr; 463 | } 464 | 465 | ExprPtr Parser::primary(void) { 466 | // primary -> NUMERIC | STRING | "true" | "false" | "nil" | "this" 467 | // | "(" expression ")" | "super" "." IDENTIFIER ; 468 | 469 | if (match(TokenKind::TK_NUMERIC)) 470 | return std::make_shared(prev_.as_numeric()); 471 | if (match(TokenKind::TK_STRING)) 472 | return std::make_shared(prev_.as_string()); 473 | if (match(TokenKind::KW_TRUE)) 474 | return std::make_shared(true); 475 | if (match(TokenKind::KW_FALSE)) 476 | return std::make_shared(false); 477 | if (match(TokenKind::KW_NIL)) 478 | return std::make_shared(nullptr); 479 | if (match(TokenKind::KW_THIS)) 480 | return std::make_shared(prev_); 481 | 482 | if (match(TokenKind::TK_LPAREN)) { 483 | ExprPtr expr = expression(); 484 | (void)consume(TokenKind::TK_RPAREN, "expect `)` after expression"); 485 | return std::make_shared(expr); 486 | } 487 | 488 | if (match(TokenKind::KW_SUPER)) { 489 | const Token& keyword = prev_; 490 | (void)consume(TokenKind::TK_DOT, "expect `.` after keyword `super`"); 491 | const Token& method = consume( 492 | TokenKind::TK_IDENTIFIER, "expect superclass method name"); 493 | return std::make_shared(keyword, method); 494 | } 495 | 496 | if (match(TokenKind::TK_IDENTIFIER)) 497 | return std::make_shared(prev_); 498 | 499 | throw RuntimeError(curr_, "expect expression"); 500 | } 501 | 502 | } 503 | -------------------------------------------------------------------------------- /bytecc_value.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "common.hh" 35 | 36 | namespace loxcc::bytecc { 37 | 38 | enum class ObjType { 39 | STRING, 40 | NATIVE, 41 | FUNCTION, 42 | UPVALUE, 43 | CLOSURE, 44 | CLASS, 45 | INSTANCE, 46 | BOUND_METHOD, 47 | }; 48 | 49 | class VM; 50 | class Chunk; 51 | 52 | class BaseObject : private UnCopyable { 53 | ObjType type_{}; 54 | bool marked_{}; 55 | public: 56 | BaseObject(ObjType type) noexcept : type_(type) {} 57 | virtual ~BaseObject(void) {} 58 | 59 | inline ObjType type(void) const { return type_; } 60 | inline bool marked(void) const { return marked_; } 61 | inline void set_marked(bool marked = true) { marked_ = marked; } 62 | 63 | virtual str_t stringify(void) const = 0; 64 | virtual bool is_truthy(void) const { return true; } 65 | virtual void blacken(VM& vm) {} 66 | }; 67 | 68 | class StringObject; 69 | class NativeObject; 70 | class FunctionObject; 71 | class UpvalueObject; 72 | class ClosureObject; 73 | class ClassObject; 74 | class InstanceObject; 75 | class BoundMehtodObject; 76 | 77 | enum class ValueType { 78 | NIL, 79 | BOOLEAN, 80 | NUMERIC, 81 | OBJECT, 82 | }; 83 | 84 | class TagValue final : public Copyable { 85 | static constexpr u64_t kSignBit = 1llu << 63; 86 | static constexpr u64_t kQNaN = 0x7ffc000000000000llu; 87 | 88 | enum Tag { 89 | NIL = 0x01, 90 | FALSE = 0x02, 91 | TRUE = 0x03 92 | }; 93 | 94 | union DoubleUnion { 95 | u64_t bits64; 96 | u32_t bits32[2]; 97 | double num; 98 | }; 99 | 100 | inline double as_d64(u64_t u64) const { 101 | DoubleUnion du; 102 | du.bits64 = u64; 103 | return du.num; 104 | } 105 | 106 | inline u64_t as_u64(double d64) const { 107 | DoubleUnion du; 108 | du.num = d64; 109 | return du.bits64; 110 | } 111 | 112 | u64_t bits_{}; 113 | 114 | inline bool check(ObjType type) const { 115 | return is_object() && as_object()->type() == type; 116 | } 117 | public: 118 | TagValue(void) noexcept : bits_(kQNaN | Tag::NIL) {} 119 | TagValue(nil_t) noexcept : bits_(kQNaN | Tag::NIL) {} 120 | TagValue(bool b) noexcept : bits_(b ? (kQNaN | Tag::TRUE) : (kQNaN | Tag::FALSE)) {} 121 | TagValue(i8_t x) noexcept : bits_(x) {} 122 | TagValue(u8_t x) noexcept : bits_(x) {} 123 | TagValue(i16_t x) noexcept : bits_(x) {} 124 | TagValue(u16_t x) noexcept : bits_(x) {} 125 | TagValue(i32_t x) noexcept : bits_(x) {} 126 | TagValue(u32_t x) noexcept : bits_(x) {} 127 | TagValue(i64_t x) noexcept : bits_(x) {} 128 | TagValue(u64_t x) noexcept : bits_(x) {} 129 | TagValue(float x) noexcept : bits_(as_u64(x)) {} 130 | TagValue(double d) noexcept : bits_(as_u64(d)) {} 131 | TagValue(BaseObject* o) noexcept : bits_(kSignBit | kQNaN | (u64_t)(o)) {} 132 | TagValue(const TagValue& r) noexcept : bits_(r.bits_) {} 133 | TagValue(TagValue&& r) noexcept : bits_(std::move(r.bits_)) {} 134 | 135 | inline TagValue& operator=(const TagValue& r) noexcept { 136 | if (this != &r) 137 | bits_ = r.bits_; 138 | return *this; 139 | } 140 | 141 | inline TagValue& operator=(TagValue&& r) noexcept { 142 | if (this != &r) 143 | bits_ = std::move(r.bits_); 144 | return *this; 145 | } 146 | 147 | inline bool operator>(const TagValue& r) const noexcept { 148 | return as_numeric() > r.as_numeric(); 149 | } 150 | 151 | inline bool operator>=(const TagValue& r) const noexcept { 152 | return as_numeric() >= r.as_numeric(); 153 | } 154 | 155 | inline bool operator<(const TagValue& r) const noexcept { 156 | return as_numeric() < r.as_numeric(); 157 | } 158 | 159 | inline bool operator<=(const TagValue& r) const noexcept { 160 | return as_numeric() <= r.as_numeric(); 161 | } 162 | 163 | inline bool operator!(void) const noexcept { 164 | return !is_truthy(); 165 | } 166 | 167 | inline TagValue operator-(void) const noexcept { 168 | return -as_numeric(); 169 | } 170 | 171 | inline bool operator==(const TagValue& r) const noexcept { 172 | return bits_ == r.bits_; 173 | } 174 | 175 | inline bool operator!=(const TagValue& r) const noexcept { 176 | return bits_ != r.bits_; 177 | } 178 | 179 | inline bool is_nil(void) const { return bits_ == (kQNaN | Tag::NIL); } 180 | inline bool is_boolean(void) const { return (bits_ & (kQNaN | Tag::FALSE)) == (kQNaN | Tag::FALSE); } 181 | inline bool is_numeric(void) const { return (bits_ & kQNaN) != kQNaN; } 182 | inline bool is_object(void) const { return (bits_ & (kQNaN | kSignBit)) == (kQNaN | kSignBit); } 183 | inline bool is_string(void) const { return check(ObjType::STRING); } 184 | inline bool is_native(void) const { return check(ObjType::NATIVE); } 185 | inline bool is_function(void) const { return check(ObjType::FUNCTION); } 186 | inline bool is_upvalue(void) const { return check(ObjType::UPVALUE); } 187 | inline bool is_closure(void) const { return check(ObjType::CLOSURE); } 188 | inline bool is_class(void) const { return check(ObjType::CLASS); } 189 | inline bool is_instance(void) const { return check(ObjType::INSTANCE); } 190 | inline bool is_bound_method(void) const { return check(ObjType::BOUND_METHOD); } 191 | 192 | inline bool as_boolean(void) const { return bits_ == (kQNaN | Tag::TRUE); } 193 | inline double as_numeric(void) const { return as_d64(bits_); } 194 | inline BaseObject* as_object(void) const { return (BaseObject*)(bits_ & ~(kQNaN | kSignBit)); } 195 | 196 | StringObject* as_string(void) const; 197 | const char* as_cstring(void) const; 198 | NativeObject* as_native(void) const; 199 | FunctionObject* as_function(void) const; 200 | UpvalueObject* as_upvalue(void) const; 201 | ClosureObject* as_closure(void) const; 202 | ClassObject* as_class(void) const; 203 | InstanceObject* as_instance(void) const; 204 | BoundMehtodObject* as_bound_method(void) const; 205 | 206 | bool is_truthy(void) const; 207 | str_t stringify(void) const; 208 | }; 209 | 210 | class ObjValue final : public Copyable { 211 | ValueType type_{ValueType::NIL}; 212 | union { 213 | bool boolean; 214 | double numeric; 215 | BaseObject* object; 216 | } as_{}; 217 | 218 | template inline void set_numeric(T x) { 219 | as_.numeric = Xt::as_type(x); 220 | } 221 | 222 | inline bool check(ObjType type) const { 223 | return is_object() && as_.object->type() == type; 224 | } 225 | public: 226 | ObjValue(void) noexcept {} 227 | ObjValue(nil_t) noexcept {} 228 | ObjValue(bool b) noexcept : type_(ValueType::BOOLEAN) { as_.boolean = b; } 229 | ObjValue(i8_t x) noexcept : type_(ValueType::NUMERIC) { set_numeric(x); } 230 | ObjValue(u8_t x) noexcept : type_(ValueType::NUMERIC) { set_numeric(x); } 231 | ObjValue(i16_t x) noexcept : type_(ValueType::NUMERIC) { set_numeric(x); } 232 | ObjValue(u16_t x) noexcept : type_(ValueType::NUMERIC) { set_numeric(x); } 233 | ObjValue(i32_t x) noexcept : type_(ValueType::NUMERIC) { set_numeric(x); } 234 | ObjValue(u32_t x) noexcept : type_(ValueType::NUMERIC) { set_numeric(x); } 235 | ObjValue(i64_t x) noexcept : type_(ValueType::NUMERIC) { set_numeric(x); } 236 | ObjValue(u64_t x) noexcept : type_(ValueType::NUMERIC) { set_numeric(x); } 237 | ObjValue(float x) noexcept : type_(ValueType::NUMERIC) { set_numeric(x); } 238 | ObjValue(double d) noexcept : type_(ValueType::NUMERIC) { as_.numeric = d; } 239 | ObjValue(BaseObject* o) noexcept : type_(ValueType::OBJECT) { as_.object = o; } 240 | 241 | ObjValue(const ObjValue& r) noexcept 242 | : type_(r.type_) { 243 | if (type_ == ValueType::OBJECT) 244 | as_.object = r.as_.object; 245 | else 246 | as_.numeric = r.as_.numeric; 247 | } 248 | 249 | ObjValue(ObjValue&& r) noexcept 250 | : type_(std::move(r.type_)) { 251 | if (type_ == ValueType::OBJECT) 252 | as_.object = std::move(r.as_.object); 253 | else 254 | as_.numeric = std::move(r.as_.numeric); 255 | } 256 | 257 | inline ObjValue& operator=(const ObjValue& r) noexcept { 258 | if (this != &r) { 259 | type_ = r.type_; 260 | if (type_ == ValueType::OBJECT) 261 | as_.object = r.as_.object; 262 | else 263 | as_.numeric = r.as_.numeric; 264 | } 265 | return *this; 266 | } 267 | 268 | inline ObjValue& operator=(ObjValue&& r) noexcept { 269 | if (this != &r) { 270 | type_ = std::move(r.type_); 271 | if (type_ == ValueType::OBJECT) 272 | as_.object = std::move(r.as_.object); 273 | else 274 | as_.numeric = std::move(r.as_.numeric); 275 | } 276 | return *this; 277 | } 278 | 279 | inline bool operator>(const ObjValue& r) const noexcept { 280 | return as_.numeric > r.as_.numeric; 281 | } 282 | 283 | inline bool operator>=(const ObjValue& r) const noexcept { 284 | return as_.numeric >= r.as_.numeric; 285 | } 286 | 287 | inline bool operator<(const ObjValue& r) const noexcept { 288 | return as_.numeric < r.as_.numeric; 289 | } 290 | 291 | inline bool operator<=(const ObjValue& r) const noexcept { 292 | return as_.numeric <= r.as_.numeric; 293 | } 294 | 295 | inline bool operator!(void) const noexcept { 296 | return !is_truthy(); 297 | } 298 | 299 | inline ObjValue operator-(void) const noexcept { 300 | return -as_numeric(); 301 | } 302 | 303 | inline bool is_nil(void) const { return type_ == ValueType::NIL; } 304 | inline bool is_boolean(void) const { return type_ == ValueType::BOOLEAN; } 305 | inline bool is_numeric(void) const { return type_ == ValueType::NUMERIC; } 306 | inline bool is_object(void) const { return type_ == ValueType::OBJECT; } 307 | inline bool is_string(void) const { return check(ObjType::STRING); } 308 | inline bool is_native(void) const { return check(ObjType::NATIVE); } 309 | inline bool is_function(void) const { return check(ObjType::FUNCTION); } 310 | inline bool is_upvalue(void) const { return check(ObjType::UPVALUE); } 311 | inline bool is_closure(void) const { return check(ObjType::CLOSURE); } 312 | inline bool is_class(void) const { return check(ObjType::CLASS); } 313 | inline bool is_instance(void) const { return check(ObjType::INSTANCE); } 314 | inline bool is_bound_method(void) const { return check(ObjType::BOUND_METHOD); } 315 | 316 | inline bool as_boolean(void) const { return as_.boolean; } 317 | inline double as_numeric(void) const { return as_.numeric; } 318 | inline BaseObject* as_object(void) const { return as_.object; } 319 | 320 | StringObject* as_string(void) const; 321 | const char* as_cstring(void) const; 322 | NativeObject* as_native(void) const; 323 | FunctionObject* as_function(void) const; 324 | UpvalueObject* as_upvalue(void) const; 325 | ClosureObject* as_closure(void) const; 326 | ClassObject* as_class(void) const; 327 | InstanceObject* as_instance(void) const; 328 | BoundMehtodObject* as_bound_method(void) const; 329 | 330 | bool operator==(const ObjValue& r) const; 331 | bool operator!=(const ObjValue& r) const; 332 | bool is_truthy(void) const; 333 | str_t stringify(void) const; 334 | }; 335 | 336 | #if defined(NAN_TAGGING) 337 | using Value = TagValue; 338 | #else 339 | using Value = ObjValue; 340 | #endif 341 | 342 | using NativeFn = std::function; 343 | 344 | inline std::ostream& operator<<(std::ostream& out, const Value& value) { 345 | return out << value.stringify(); 346 | } 347 | 348 | class StringObject final : public BaseObject { 349 | int size_{}; 350 | char* data_{}; 351 | u32_t hash_{}; 352 | public: 353 | StringObject( 354 | const char* s, int n, u32_t h, bool replace_owner = false) noexcept; 355 | virtual ~StringObject(void); 356 | 357 | inline int size(void) const { return size_; } 358 | inline const char* cstr(void) const { return data_; } 359 | inline const char* data(void) const { return data_; } 360 | inline u32_t hash(void) const { return hash_; } 361 | 362 | virtual str_t stringify(void) const override; 363 | 364 | static StringObject* create(VM& vm, const str_t& s); 365 | static StringObject* create(VM& vm, const char* s, int n); 366 | static StringObject* concat(VM& vm, StringObject* a, StringObject* b); 367 | }; 368 | 369 | class NativeObject final : public BaseObject { 370 | NativeFn fn_{}; 371 | public: 372 | NativeObject(const NativeFn& fn) noexcept; 373 | NativeObject(NativeFn&& fn) noexcept; 374 | 375 | inline NativeFn fn(void) const { return fn_; } 376 | 377 | virtual str_t stringify(void) const override; 378 | 379 | static NativeObject* create(VM& vm, const NativeFn& fn); 380 | static NativeObject* create(VM& vm, NativeFn&& fn); 381 | }; 382 | 383 | class FunctionObject final : public BaseObject { 384 | int arity_{}; 385 | int upvalues_count_{}; 386 | StringObject* name_{}; 387 | Chunk* chunk_{}; 388 | public: 389 | FunctionObject(StringObject* name = nullptr) noexcept; 390 | virtual ~FunctionObject(void); 391 | 392 | inline int arity(void) const { return arity_; } 393 | inline int inc_arity(void) { return arity_++; } 394 | inline int upvalues_count(void) const { return upvalues_count_; } 395 | inline int inc_upvalues_count(void) { return upvalues_count_++; } 396 | inline StringObject* name(void) const { return name_; } 397 | 398 | inline const char* name_astr(void) const { 399 | return name_ != nullptr ? name_->cstr() : ""; 400 | } 401 | 402 | inline void set_name(StringObject* name) { name_ = name; } 403 | inline Chunk* chunk(void) { return chunk_; } 404 | 405 | virtual str_t stringify(void) const override; 406 | virtual void blacken(VM& vm) override; 407 | 408 | static FunctionObject* create(VM& vm, StringObject* name = nullptr); 409 | }; 410 | 411 | class UpvalueObject final : public BaseObject { 412 | Value* value_{}; 413 | Value closed_{}; 414 | UpvalueObject* next_{}; 415 | public: 416 | UpvalueObject(Value* value, UpvalueObject* next = nullptr) noexcept; 417 | 418 | inline Value* value(void) const { return value_; } 419 | inline void set_value(Value* value) { value_ = value; } 420 | inline void set_value_withptr(Value* value) { value_ = value; } 421 | inline void set_value_withref(const Value& value) { *value_ = value; } 422 | inline const Value& closed(void) const { return closed_; } 423 | inline Value* closed_asptr(void) { return &closed_; } 424 | inline void set_closed(const Value& closed) { closed_ = closed; } 425 | inline UpvalueObject* next(void) const { return next_; } 426 | inline void set_next(UpvalueObject* next) { next_ = next; } 427 | 428 | virtual str_t stringify(void) const override; 429 | virtual void blacken(VM& vm) override; 430 | 431 | static UpvalueObject* create( 432 | VM& vm, Value* value, UpvalueObject* next = nullptr); 433 | }; 434 | 435 | class ClosureObject final : public BaseObject { 436 | FunctionObject* fn_{}; 437 | int upvalues_count_{}; 438 | UpvalueObject** upvalues_{}; 439 | public: 440 | ClosureObject(FunctionObject* fn) noexcept; 441 | virtual ~ClosureObject(void); 442 | 443 | inline FunctionObject* fn(void) const { return fn_; } 444 | inline int upvalues_count(void) const { return upvalues_count_; } 445 | inline UpvalueObject** upvalues(void) const { return upvalues_; } 446 | inline UpvalueObject* get_upvalue(int i) const { return upvalues_[i]; } 447 | 448 | inline void set_upvalue(int i, UpvalueObject* upvalue) { 449 | upvalues_[i] = upvalue; 450 | } 451 | 452 | virtual str_t stringify(void) const override; 453 | virtual void blacken(VM& vm) override; 454 | 455 | static ClosureObject* create(VM& vm, FunctionObject* fn); 456 | }; 457 | 458 | class ClassObject final : public BaseObject { 459 | using MethodMap = std::unordered_map; 460 | 461 | StringObject* name_{}; 462 | MethodMap methods_; 463 | public: 464 | ClassObject(StringObject* name) noexcept; 465 | 466 | inline StringObject* name(void) const { return name_; } 467 | inline const char* name_astr(void) const { return name_->cstr(); } 468 | 469 | inline void set_method(const str_t& name, const Value& method) { 470 | methods_[name] = method; 471 | } 472 | 473 | inline void set_method(StringObject* name, const Value& method) { 474 | methods_[name->cstr()] = method; 475 | } 476 | 477 | inline std::optional get_method(StringObject* name) const { 478 | return get_method(name->cstr()); 479 | } 480 | 481 | std::optional get_method(const str_t& name) const { 482 | if (auto meth_iter = methods_.find(name); meth_iter != methods_.end()) 483 | return {meth_iter->second}; 484 | return {}; 485 | } 486 | 487 | void inherit_from(ClassObject* superclass); 488 | 489 | virtual str_t stringify(void) const override; 490 | virtual void blacken(VM& vm) override; 491 | 492 | static ClassObject* create(VM& vm, StringObject* name); 493 | }; 494 | 495 | class InstanceObject final : public BaseObject { 496 | using AttrMap = std::unordered_map; 497 | 498 | ClassObject* cls_{}; 499 | AttrMap attrs_; 500 | public: 501 | InstanceObject(ClassObject* cls) noexcept; 502 | 503 | inline ClassObject* cls(void) const { return cls_; } 504 | 505 | inline void set_attr(const str_t& key, const Value& val) { 506 | attrs_[key] = val; 507 | } 508 | 509 | inline void set_attr(StringObject* key, const Value& val) { 510 | attrs_[key->cstr()] = val; 511 | } 512 | 513 | inline std::optional get_attr(StringObject* key) const { 514 | return get_attr(key->cstr()); 515 | } 516 | 517 | std::optional get_attr(const str_t& key) const { 518 | if (auto attr_iter = attrs_.find(key); attr_iter != attrs_.end()) 519 | return {attr_iter->second}; 520 | return {}; 521 | } 522 | 523 | virtual str_t stringify(void) const override; 524 | virtual void blacken(VM& vm) override; 525 | 526 | static InstanceObject* create(VM& vm, ClassObject* cls); 527 | }; 528 | 529 | class BoundMehtodObject final : public BaseObject { 530 | Value owner_; 531 | ClosureObject* method_{}; 532 | public: 533 | BoundMehtodObject(const Value& owner, ClosureObject* method) noexcept; 534 | 535 | inline const Value& owner(void) const { return owner_; } 536 | inline ClosureObject* method(void) const { return method_; } 537 | 538 | virtual str_t stringify(void) const override; 539 | virtual void blacken(VM& vm) override; 540 | 541 | static BoundMehtodObject* create( 542 | VM& vm, const Value& owner, ClosureObject* method); 543 | }; 544 | 545 | } 546 | -------------------------------------------------------------------------------- /bytecc_vm.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ASMlover. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions 5 | // are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list ofconditions and the following disclaimer. 9 | // 10 | // * Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in 12 | // the documentation and/or other materialsprovided with the 13 | // distribution. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | #include 28 | #include 29 | #include 30 | #include "bytecc_chunk.hh" 31 | #include "bytecc_compiler.hh" 32 | #include "bytecc_vm.hh" 33 | 34 | namespace loxcc::bytecc { 35 | 36 | class CallFrame final : public Copyable { 37 | ClosureObject* closure_{}; 38 | const u8_t* ip_{}; 39 | int begpos_{}; 40 | public: 41 | CallFrame(ClosureObject* closure, const u8_t* ip, int begpos = 0) noexcept 42 | : closure_(closure), ip_(ip), begpos_(begpos) { 43 | } 44 | 45 | inline ClosureObject* closure(void) const { return closure_; } 46 | inline const u8_t* ip(void) const { return ip_; } 47 | inline int begpos(void) const { return begpos_; } 48 | 49 | inline void set_ip(const u8_t* ip) { ip_ = ip; } 50 | inline u8_t get_ip(int i) const { return ip_[i]; } 51 | inline u8_t inc_ip(void) { return *ip_++; } 52 | inline u8_t dec_ip(void) { return *ip_--; } 53 | inline void add_ip(int offset) { ip_ += offset; } 54 | inline void sub_ip(int offset) { ip_ -= offset; } 55 | 56 | inline FunctionObject* frame_fn(void) const { return closure_->fn(); } 57 | inline Chunk* frame_chunk(void) const { return closure_->fn()->chunk(); } 58 | }; 59 | 60 | VM::VM(void) noexcept 61 | : gcompiler_(new GlobalCompiler()) { 62 | stack_.reserve(kDefaultCap); 63 | 64 | define_native("clock", [](int argc, Value* args) -> Value { 65 | return std::chrono::duration_cast( 66 | std::chrono::system_clock::now().time_since_epoch()).count() / 1000.0; 67 | }); 68 | define_native("puts", [](int argc, Value* args) -> Value { 69 | for (int i = 0; i < argc; ++i) 70 | std::cout << args[i] << " "; 71 | std::cout << std::endl; 72 | return nullptr; 73 | }); 74 | 75 | ctor_string_ = StringObject::create(*this, "ctor"); 76 | } 77 | 78 | VM::~VM(void) { 79 | globals_.clear(); 80 | interned_strings_.clear(); 81 | ctor_string_ = nullptr; 82 | 83 | while (!all_objects_.empty()) { 84 | auto* o = all_objects_.back(); 85 | all_objects_.pop_back(); 86 | free_object(o); 87 | } 88 | worked_objects_.clear(); 89 | } 90 | 91 | void VM::runtime_error(const char* format, ...) { 92 | std::cerr << "Traceback (most recent call last):" << std::endl; 93 | int i = Xt::as_type(frames_.size()) - 1; 94 | for (; i >= 0; --i) { 95 | auto& frame = frames_[i]; 96 | Chunk* chunk = frame.frame_chunk(); 97 | int ins = Xt::as_type(frame.ip() - chunk->codes()) - 1; 98 | 99 | std::cerr << " [LINE: " << chunk->get_line(ins) << "] in " 100 | << "`" << frame.frame_fn()->name_astr() << "()`" << std::endl; 101 | } 102 | 103 | va_list ap; 104 | va_start(ap, format); 105 | vfprintf(stderr, format, ap); 106 | va_end(ap); 107 | fprintf(stderr, "\n"); 108 | 109 | reset(); 110 | } 111 | 112 | void VM::reset(void) { 113 | stack_.clear(); 114 | frames_.clear(); 115 | open_upvalues_ = nullptr; 116 | } 117 | 118 | Value* VM::stack_values(int distance) { 119 | if (distance == 0 || stack_.empty()) 120 | return nullptr; 121 | return &stack_[stack_.size() - distance]; 122 | } 123 | 124 | void VM::stack_resize(int distance) { 125 | stack_.resize(stack_.size() - distance); 126 | } 127 | 128 | void VM::set_stack(int distance, const Value& v) { 129 | stack_[stack_.size() - distance] = v; 130 | } 131 | 132 | void VM::push(const Value& value) { 133 | stack_.push_back(value); 134 | } 135 | 136 | Value VM::pop(void) { 137 | Value value = stack_.back(); 138 | stack_.pop_back(); 139 | return value; 140 | } 141 | 142 | const Value& VM::peek(int distance) const { 143 | return stack_[stack_.size() - 1 - distance]; 144 | } 145 | 146 | void VM::define_native(const str_t& name, const NativeFn& fn) { 147 | push(NativeObject::create(*this, fn)); 148 | globals_[name] = peek(); 149 | pop(); 150 | } 151 | 152 | void VM::define_native(const str_t& name, NativeFn&& fn) { 153 | push(NativeObject::create(*this, std::move(fn))); 154 | globals_[name] = peek(); 155 | pop(); 156 | } 157 | 158 | void VM::define_method(StringObject* name) { 159 | const Value& method = peek(); 160 | ClassObject* cls = peek(1).as_class(); 161 | cls->set_method(name, method); 162 | pop(); 163 | pop(); 164 | } 165 | 166 | bool VM::bind_method(ClassObject* cls, StringObject* name) { 167 | if (auto method = cls->get_method(name); method) { 168 | BoundMehtodObject* bound = 169 | BoundMehtodObject::create(*this, peek(0), (*method).as_closure()); 170 | pop(); // pop instance object 171 | push(bound); 172 | return true; 173 | } 174 | 175 | runtime_error("`%s` undefined attribute `%s`", cls->name_astr(), name->cstr()); 176 | return false; 177 | } 178 | 179 | bool VM::call(ClosureObject* closure, int argc) { 180 | if (argc != closure->fn()->arity()) { 181 | runtime_error("`%s` takes %d arguments but %d were given", 182 | closure->fn()->name_astr(), closure->fn()->arity(), argc); 183 | return false; 184 | } 185 | 186 | if (frames_.size() >= kMaxFrames) { 187 | runtime_error("stack overflow"); 188 | return false; 189 | } 190 | 191 | frames_.push_back(CallFrame(closure, 192 | closure->fn()->chunk()->codes(), 193 | Xt::as_type(stack_.size() - argc - 1))); 194 | return true; 195 | } 196 | 197 | bool VM::call_value(const Value& callee, int argc) { 198 | if (callee.is_object()) { 199 | switch (callee.as_object()->type()) { 200 | case ObjType::NATIVE: 201 | { 202 | Value r = callee.as_native()->fn()(argc, stack_values(argc)); 203 | stack_resize(argc + 1); 204 | push(r); 205 | return true; 206 | } 207 | case ObjType::CLOSURE: 208 | return call(callee.as_closure(), argc); 209 | case ObjType::CLASS: 210 | { 211 | ClassObject* cls = callee.as_class(); 212 | set_stack(argc + 1, InstanceObject::create(*this, cls)); 213 | if (auto ctor = cls->get_method(ctor_string_); ctor) { 214 | return call((*ctor).as_closure(), argc); 215 | } 216 | else if (argc != 0) { 217 | runtime_error("`%s` takes 0 arguments but %d were given", 218 | ctor_string_->cstr(), argc); 219 | return false; 220 | } 221 | return true; 222 | } 223 | case ObjType::BOUND_METHOD: 224 | { 225 | BoundMehtodObject* bound = callee.as_bound_method(); 226 | set_stack(argc + 1, bound->owner()); 227 | call(bound->method(), argc); 228 | } 229 | default: break; // do nothing 230 | } 231 | } 232 | 233 | runtime_error("can only call functions and classes"); 234 | return false; 235 | } 236 | 237 | bool VM::invoke_from_class(ClassObject* cls, StringObject* name, int argc) { 238 | if (auto method = cls->get_method(name); method) 239 | return call((*method).as_closure(), argc); 240 | 241 | runtime_error("`%s` object has no method `%s`", 242 | cls->name_astr(), name->cstr()); 243 | return false; 244 | } 245 | 246 | bool VM::invoke(StringObject* name, int argc) { 247 | const Value& owner = peek(argc); 248 | if (!owner.is_instance()) { 249 | runtime_error("only instances have methods"); 250 | return false; 251 | } 252 | 253 | InstanceObject* inst = owner.as_instance(); 254 | if (auto attr = inst->get_attr(name); attr) { 255 | set_stack(argc, (*attr)); 256 | return call_value((*attr), argc); 257 | } 258 | 259 | return invoke_from_class(inst->cls(), name, argc); 260 | } 261 | 262 | UpvalueObject* VM::capture_upvalue(Value* local) { 263 | // if there are no open upvalues at all, need create a new one 264 | if (open_upvalues_ == nullptr) { 265 | open_upvalues_ = UpvalueObject::create(*this, local); 266 | return open_upvalues_; 267 | } 268 | 269 | UpvalueObject* prev_upvalue{}; 270 | UpvalueObject* upvalue = open_upvalues_; 271 | 272 | // walk towards the bottom of the stack until we find a previously 273 | // existing upvalue or reach where it should be 274 | while (upvalue != nullptr && upvalue->value() > local) { 275 | prev_upvalue = upvalue; 276 | upvalue = upvalue->next(); 277 | } 278 | // reuse it if we found it 279 | if (upvalue != nullptr && upvalue->value() == local) 280 | return upvalue; 281 | 282 | // we walked past the local on the stack, so there must not be an upvalue 283 | // for it already, make sure a new one and link it in the right place to 284 | // keep the list sorted 285 | UpvalueObject* created_upvalue = UpvalueObject::create(*this, local, upvalue); 286 | if (prev_upvalue == nullptr) 287 | open_upvalues_ = created_upvalue; 288 | else 289 | prev_upvalue->set_next(created_upvalue); 290 | 291 | return created_upvalue; 292 | } 293 | 294 | void VM::close_upvalues(Value* last) { 295 | while (open_upvalues_ != nullptr && open_upvalues_->value() >= last) { 296 | UpvalueObject* upvalue = open_upvalues_; 297 | 298 | // move the value into the upvalue itself and point the upvalue to it 299 | upvalue->set_closed(*upvalue->value()); 300 | upvalue->set_value(upvalue->closed_asptr()); 301 | 302 | // pop it off the open upvalue list 303 | open_upvalues_ = upvalue->next(); 304 | } 305 | } 306 | 307 | InterpretRet VM::run(void) { 308 | CallFrame* frame = &frames_.back(); 309 | 310 | auto _RDBYTE = [&frame](void) -> u8_t { return frame->inc_ip(); }; 311 | auto _RDWORD = [&frame](void) -> u16_t { 312 | return (frame->add_ip(2), 313 | Xt::as_type((frame->get_ip(-2) << 8) | frame->get_ip(-1))); 314 | }; 315 | auto _RDCONST = [&frame, _RDBYTE](void) -> const Value& { 316 | return frame->frame_chunk()->get_constant(_RDBYTE()); 317 | }; 318 | auto _RDSTRING = [_RDCONST](void) -> StringObject* { 319 | return _RDCONST().as_string(); 320 | }; 321 | #define _BINARYOP(op) do {\ 322 | if (!peek(0).is_numeric() || !peek(1).is_numeric()) {\ 323 | runtime_error("operands must be two numerics");\ 324 | return InterpretRet::RUNTIME_ERR;\ 325 | }\ 326 | double b = pop().as_numeric();\ 327 | double a = pop().as_numeric();\ 328 | push(a op b);\ 329 | } while (false) 330 | 331 | #if defined(TRACE_EXEC) 332 | # define TRACE_EXEC_INSTRUCTION() do { 333 | std::cout << " "; 334 | for (auto& v : stack_) 335 | std::cout << "[" << v << "]"; 336 | std::cout << std::endl; 337 | 338 | frame->frame_chunk()->dis_ins( 339 | Xt::as_type(frame->ip() - frame->frame_chunk()->codes())); 340 | } while (false) 341 | #else 342 | # define TRACE_EXEC_INSTRUCTION() ((void)0) 343 | #endif 344 | 345 | #if defined(COMPUTED_GOTOS) 346 | static void* _dispatchs[] = { 347 | # undef BYTECC_CODEF 348 | # define BYTECC_CODEF(c) &&__code_##c, 349 | # include "bytecc_codes.hh" 350 | }; 351 | # define INTERPRET_LOOP() DISPATCH(); 352 | # define CASE_CODE(name) __code_##name 353 | # define DISPATCH() do {\ 354 | TRACE_EXEC_INSTRUCTION();\ 355 | goto *_dispatchs[Xt::as_type(ins = Xt::as_type(_RDBYTE()))];\ 356 | } while (false) 357 | #else 358 | # define INTERPRET_LOOP()\ 359 | __loop:\ 360 | TRACE_EXEC_INSTRUCTION();\ 361 | switch (ins = Xt::as_type(_RDBYTE())) 362 | # define CASE_CODE(name) case Code::name 363 | # define DISPATCH() goto __loop 364 | #endif 365 | 366 | Code ins; 367 | INTERPRET_LOOP() { 368 | CASE_CODE(CONSTANT): push(_RDCONST()); DISPATCH(); 369 | CASE_CODE(NIL): push(nullptr); DISPATCH(); 370 | CASE_CODE(TRUE): push(true); DISPATCH(); 371 | CASE_CODE(FALSE): push(false); DISPATCH(); 372 | CASE_CODE(POP): pop(); DISPATCH(); 373 | CASE_CODE(DEF_GLOBAL): 374 | { 375 | StringObject* name = _RDSTRING(); 376 | globals_[name->cstr()] = peek(0); 377 | pop(); 378 | 379 | DISPATCH(); 380 | } 381 | CASE_CODE(GET_GLOBAL): 382 | { 383 | StringObject* name = _RDSTRING(); 384 | if (auto it = globals_.find(name->cstr()); it != globals_.end()) { 385 | push(it->second); 386 | } 387 | else { 388 | runtime_error("name `%s` is not defined", name->cstr()); 389 | return InterpretRet::RUNTIME_ERR; 390 | } 391 | 392 | DISPATCH(); 393 | } 394 | CASE_CODE(SET_GLOBAL): 395 | { 396 | StringObject* name = _RDSTRING(); 397 | if (auto it = globals_.find(name->cstr()); it == globals_.end()) { 398 | runtime_error("name `%s` is not defined", name->cstr()); 399 | return InterpretRet::RUNTIME_ERR; 400 | } 401 | else { 402 | globals_[name->cstr()] = peek(0); 403 | } 404 | 405 | DISPATCH(); 406 | } 407 | CASE_CODE(GET_LOCAL): 408 | { 409 | u8_t slot = _RDBYTE(); 410 | push(stack_[Xt::as_type(frame->begpos() + slot)]); 411 | 412 | DISPATCH(); 413 | } 414 | CASE_CODE(SET_LOCAL): 415 | { 416 | u8_t slot = _RDBYTE(); 417 | stack_[Xt::as_type(frame->begpos() + slot)] = peek(0); 418 | 419 | DISPATCH(); 420 | } 421 | CASE_CODE(GET_UPVALUE): 422 | { 423 | u8_t slot = _RDBYTE(); 424 | push(*frame->closure()->get_upvalue(slot)->value()); 425 | 426 | DISPATCH(); 427 | } 428 | CASE_CODE(SET_UPVALUE): 429 | { 430 | u8_t slot = _RDBYTE(); 431 | frame->closure()->get_upvalue(slot)->set_value_withref(peek(0)); 432 | 433 | DISPATCH(); 434 | } 435 | CASE_CODE(GET_ATTR): 436 | { 437 | if (!peek(0).is_instance()) { 438 | runtime_error("only instance objects have attributes"); 439 | return InterpretRet::RUNTIME_ERR; 440 | } 441 | 442 | InstanceObject* inst = peek(0).as_instance(); 443 | StringObject* name = _RDSTRING(); 444 | if (auto attr = inst->get_attr(name); attr) { 445 | pop(); // pop out instance 446 | push(*attr); 447 | DISPATCH(); 448 | } 449 | if (!bind_method(inst->cls(), name)) 450 | return InterpretRet::RUNTIME_ERR; 451 | 452 | DISPATCH(); 453 | } 454 | CASE_CODE(SET_ATTR): 455 | { 456 | if (!peek(1).is_instance()) { 457 | runtime_error("only instance objects have attributes"); 458 | return InterpretRet::RUNTIME_ERR; 459 | } 460 | 461 | InstanceObject* inst = peek(1).as_instance(); 462 | inst->set_attr(_RDSTRING(), peek(0)); 463 | Value value = pop(); 464 | pop(); // pop instance 465 | push(value); 466 | 467 | DISPATCH(); 468 | } 469 | CASE_CODE(GET_SUPER): 470 | { 471 | StringObject* name = _RDSTRING(); 472 | ClassObject* superclass = pop().as_class(); 473 | if (!bind_method(superclass, name)) 474 | return InterpretRet::RUNTIME_ERR; 475 | 476 | DISPATCH(); 477 | } 478 | CASE_CODE(EQ): 479 | { 480 | Value b = pop(); 481 | Value a = pop(); 482 | push(a == b); 483 | 484 | DISPATCH(); 485 | } 486 | CASE_CODE(NE): 487 | { 488 | Value b = pop(); 489 | Value a = pop(); 490 | push(a != b); 491 | 492 | DISPATCH(); 493 | } 494 | CASE_CODE(GT): _BINARYOP(>); DISPATCH(); 495 | CASE_CODE(GE): _BINARYOP(>=); DISPATCH(); 496 | CASE_CODE(LT): _BINARYOP(<); DISPATCH(); 497 | CASE_CODE(LE): _BINARYOP(<=); DISPATCH(); 498 | CASE_CODE(ADD): 499 | { 500 | if (peek(0).is_string() && peek(1).is_string()) { 501 | StringObject* b = peek(0).as_string(); 502 | StringObject* a = peek(1).as_string(); 503 | StringObject* s = StringObject::concat(*this, a, b); 504 | pop(); 505 | pop(); 506 | push(s); 507 | } 508 | else if (peek(0).is_numeric() && peek(1).is_numeric()) { 509 | double b = pop().as_numeric(); 510 | double a = pop().as_numeric(); 511 | push(a + b); 512 | } 513 | else { 514 | runtime_error("operands must be two strings or two numerics"); 515 | return InterpretRet::RUNTIME_ERR; 516 | } 517 | 518 | DISPATCH(); 519 | } 520 | CASE_CODE(SUB): _BINARYOP(-); DISPATCH(); 521 | CASE_CODE(MUL): _BINARYOP(*); DISPATCH(); 522 | CASE_CODE(DIV): _BINARYOP(/); DISPATCH(); 523 | CASE_CODE(NOT): push(!pop()); DISPATCH(); 524 | CASE_CODE(NEG): 525 | { 526 | if (!peek(0).is_numeric()) { 527 | runtime_error("operand must be a numeric"); 528 | return InterpretRet::RUNTIME_ERR; 529 | } 530 | push(-pop()); 531 | 532 | DISPATCH(); 533 | } 534 | CASE_CODE(PRINT): std::cout << pop() << std::endl; DISPATCH(); 535 | CASE_CODE(JUMP): frame->add_ip(_RDWORD()); DISPATCH(); 536 | CASE_CODE(JUMP_IF_FALSE): 537 | { 538 | u16_t offset = _RDWORD(); 539 | if (!peek(0)) 540 | frame->add_ip(offset); 541 | 542 | DISPATCH(); 543 | } 544 | CASE_CODE(LOOP): frame->sub_ip(_RDWORD()); DISPATCH(); 545 | CASE_CODE(CALL_0): 546 | CASE_CODE(CALL_1): 547 | CASE_CODE(CALL_2): 548 | CASE_CODE(CALL_3): 549 | CASE_CODE(CALL_4): 550 | CASE_CODE(CALL_5): 551 | CASE_CODE(CALL_6): 552 | CASE_CODE(CALL_7): 553 | CASE_CODE(CALL_8): 554 | { 555 | int argc = ins - Code::CALL_0; 556 | if (!call_value(peek(argc), argc)) 557 | return InterpretRet::RUNTIME_ERR; 558 | frame = &frames_.back(); 559 | 560 | DISPATCH(); 561 | } 562 | CASE_CODE(INVOKE_0): 563 | CASE_CODE(INVOKE_1): 564 | CASE_CODE(INVOKE_2): 565 | CASE_CODE(INVOKE_3): 566 | CASE_CODE(INVOKE_4): 567 | CASE_CODE(INVOKE_5): 568 | CASE_CODE(INVOKE_6): 569 | CASE_CODE(INVOKE_7): 570 | CASE_CODE(INVOKE_8): 571 | { 572 | StringObject* method_name = _RDSTRING(); 573 | int argc = ins - Code::INVOKE_0; 574 | if (!invoke(method_name, argc)) 575 | return InterpretRet::RUNTIME_ERR; 576 | frame = &frames_.back(); 577 | 578 | DISPATCH(); 579 | } 580 | CASE_CODE(SUPER_0): 581 | CASE_CODE(SUPER_1): 582 | CASE_CODE(SUPER_2): 583 | CASE_CODE(SUPER_3): 584 | CASE_CODE(SUPER_4): 585 | CASE_CODE(SUPER_5): 586 | CASE_CODE(SUPER_6): 587 | CASE_CODE(SUPER_7): 588 | CASE_CODE(SUPER_8): 589 | { 590 | StringObject* method_name = _RDSTRING(); 591 | int argc = ins - Code::SUPER_0; 592 | ClassObject* superclass = pop().as_class(); 593 | if (!invoke_from_class(superclass, method_name, argc)) 594 | return InterpretRet::RUNTIME_ERR; 595 | frame = &frames_.back(); 596 | 597 | DISPATCH(); 598 | } 599 | CASE_CODE(CLOSURE): 600 | { 601 | // create the closure and push it on the stack before creating 602 | // upvalues so that is does not get collected 603 | FunctionObject* fn = _RDCONST().as_function(); 604 | ClosureObject* closure = ClosureObject::create(*this, fn); 605 | push(closure); 606 | 607 | // capture upvalues 608 | for (int i = 0; i < closure->upvalues_count(); ++i) { 609 | u8_t is_local = _RDBYTE(); 610 | u8_t index = _RDBYTE(); 611 | if (is_local) { 612 | // make an new upvalue to close over the parent's local variable 613 | closure->set_upvalue(i, capture_upvalue( 614 | &stack_[Xt::as_type(frame->begpos() + index)])); 615 | } 616 | else { 617 | // use the same upvalue as the current call frame 618 | closure->set_upvalue(i, frame->closure()->get_upvalue(index)); 619 | } 620 | } 621 | 622 | DISPATCH(); 623 | } 624 | CASE_CODE(CLOSE_UPVALUE): close_upvalues(&stack_.back()); pop(); DISPATCH(); 625 | CASE_CODE(RETURN): 626 | { 627 | Value r = pop(); 628 | if (frame->begpos() < 0 || frame->begpos() >= Xt::as_type(stack_.size())) 629 | close_upvalues(nullptr); 630 | else 631 | close_upvalues(&stack_[frame->begpos()]); 632 | 633 | frames_.pop_back(); 634 | if (frames_.empty()) 635 | return InterpretRet::OK; 636 | 637 | stack_.resize(frame->begpos()); 638 | push(r); 639 | frame = &frames_.back(); 640 | 641 | DISPATCH(); 642 | } 643 | CASE_CODE(CLASS): push(ClassObject::create(*this, _RDSTRING())); DISPATCH(); 644 | CASE_CODE(SUBCLASS): 645 | { 646 | const Value& superclass = peek(1); 647 | if (!superclass.is_class()) { 648 | runtime_error("superclass must be a class"); 649 | return InterpretRet::RUNTIME_ERR; 650 | } 651 | 652 | ClassObject* cls = peek(0).as_class(); 653 | cls->inherit_from(superclass.as_class()); 654 | pop(); 655 | 656 | DISPATCH(); 657 | } 658 | CASE_CODE(METHOD): define_method(_RDSTRING()); DISPATCH(); 659 | } 660 | 661 | #undef DISPATCH 662 | #undef CASE_CODE 663 | #undef INTERPRET_LOOP 664 | #undef TRACE_EXEC_INSTRUCTION 665 | #undef _BINARYOP 666 | return InterpretRet::OK; 667 | } 668 | 669 | void VM::collect(void) { 670 | // mark the stack roots 671 | for (auto& v : stack_) 672 | mark_value(v); 673 | for (auto& frame : frames_) 674 | mark_object(frame.closure()); 675 | 676 | // mark the open upvalues 677 | for (auto* uv = open_upvalues_; uv != nullptr; uv = uv->next()) 678 | mark_object(uv); 679 | 680 | // mark the globals roots 681 | for (auto& x : globals_) 682 | mark_value(x.second); 683 | gcompiler_->mark_compiler(); 684 | mark_object(ctor_string_); 685 | 686 | // traverse the references 687 | while (!worked_objects_.empty()) { 688 | // pop and object from the marked worked objects list 689 | BaseObject* o = worked_objects_.back(); 690 | worked_objects_.pop_back(); 691 | 692 | o->blacken(*this); 693 | } 694 | 695 | // remove unused interned strings objects 696 | for (auto it = interned_strings_.begin(); it != interned_strings_.end();) { 697 | if (!it->second->marked()) 698 | interned_strings_.erase(it++); 699 | else 700 | ++it; 701 | } 702 | 703 | // collect the un-marked objects 704 | for (auto it = all_objects_.begin(); it != all_objects_.end();) { 705 | if (!(*it)->marked()) { 706 | free_object(*it); 707 | all_objects_.erase(it++); 708 | } 709 | else { 710 | (*it)->set_marked(false); 711 | ++it; 712 | } 713 | } 714 | 715 | objects_allocated_ = all_objects_.size(); 716 | next_gc_ = objects_allocated_ * kHeapGrowFactor; 717 | } 718 | 719 | void VM::append_object(BaseObject* o) { 720 | if (objects_allocated_ > next_gc_) 721 | collect(); 722 | 723 | ++objects_allocated_; 724 | all_objects_.push_back(o); 725 | } 726 | 727 | void VM::mark_object(BaseObject* o) { 728 | if (o == nullptr) 729 | return; 730 | 731 | if (o->marked()) 732 | return; 733 | 734 | #if defined(TRACE_GC) 735 | std::cout << "mark object at `" << o << "` -> " << Value(o) << std::endl; 736 | #endif 737 | 738 | o->set_marked(true); 739 | worked_objects_.push_back(o); 740 | } 741 | 742 | void VM::mark_value(const Value& v) { 743 | if (v.is_object()) 744 | mark_object(v.as_object()); 745 | } 746 | 747 | void VM::free_object(BaseObject* o) { 748 | #if defined(TRACE_GC) 749 | std::cout << "`" << o << "` free object - " << Value(o) << std::endl; 750 | #endif 751 | 752 | delete o; 753 | } 754 | 755 | InterpretRet VM::interpret(const str_t& source_bytes) { 756 | // compile the source bytes to function object code 757 | FunctionObject* fn = gcompiler_->compile(*this, source_bytes); 758 | if (fn == nullptr) 759 | return InterpretRet::COMPILE_ERR; 760 | 761 | push(fn); 762 | ClosureObject* closure = ClosureObject::create(*this, fn); 763 | pop(); 764 | call(closure, 0); 765 | 766 | return run(); 767 | } 768 | 769 | } 770 | --------------------------------------------------------------------------------