diff --git a/js4g/StackMachine.cc b/js4g/StackMachine.cc index 05601a1..fca6a0b 100644 --- a/js4g/StackMachine.cc +++ b/js4g/StackMachine.cc @@ -7,6 +7,11 @@ static_assert( __cplusplus >= 2023'02 ); namespace { using namespace std::literals::string_literals; + + namespace C + { + constexpr bool debug= false or true; + } } namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m @@ -21,22 +26,53 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m { while( not tokens.empty() ) { - const auto token= tokens.front(); - tokens.pop_front(); + runWord( next() ); - runWord( token ); + if( C::debug ) std::cerr << "After processing stack is now: " << std::endl; + if( C::debug ) std::copy( rbegin( stack ), rend( stack ), std::ostream_iterator< std::string >{ std::cerr, "\n" } ); } - std::cerr << "Run done with stack at size: " << stack.size() << std::endl; + if( C::debug ) std::cerr << "Run done with stack at size: " << stack.size() << std::endl; + } + + std::string + StackMachine::next() + { + if( tokens.empty() ) + { + throw std::runtime_error{ "FATAL: Token required, no more tokens left." }; + } + + const auto rv= tokens.front(); + tokens.pop_front(); + return rv; } void StackMachine::runWord( const std::string_view word ) { + if( C::debug ) std::cerr << "Processing token: " << word << std::endl; const bool inConditional= not conditionals.empty(); if( false ); else if( word.empty() ); + else if( word == "@enddef" ) + { + if( not definition.has_value() ) + { + throw std::runtime_error{ "Stray end of function definition" }; + } + + if( C::debug ) std::cerr << "Definition of " << definition.value() << " is done. It is: " << std::endl; + if( C::debug ) std::copy( begin( words.at( definition.value() ) ), end( words.at( definition.value() ) ), + std::ostream_iterator< std::string >{ std::cout, "\n" } ); + definition= std::nullopt; + } + else if( definition.has_value() ) + { + if( C::debug ) std::cerr << "Adding word: " << word << " to function definition: " << definition.value() << std::endl; + words[ definition.value() ].emplace_back( word ); + } else if( inConditional and word == "@else" and currentState != Else ) { currentState= Else; @@ -44,11 +80,18 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m else if( inConditional and word == "@endif" ) { conditionals.pop_back(); + currentState= resumeConditionals.back(); + resumeConditionals.pop_back(); } else if( inConditional and currentState != conditionals.back() ) { - // Pass (We skip things inside the wrong side) - // TODO: Nested if's won't work, here... + if( word != "@if" ) + { + conditionals.push_back( Skipped ); + resumeConditionals.push_back( currentState ); + currentState= If; + } + else ; // We skip the statement. } else if( word == "@else" ) { @@ -63,6 +106,13 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m auto val= peek(); push( std::move( val ) ); } + else if( word == "@swap" ) + { + auto a= pop(); + auto b= pop(); + push( std::move( a ) ); + push( std::move( b ) ); + } else if( word == "@drop" ) { std::ignore= pop(); @@ -72,6 +122,14 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m auto val= pop< Integer >(); push( val == 0 ? 1 : 0 ); } + else if( word == "@i_inc" ) + { + push( pop< Integer >() + 1 ); + } + else if( word == "@i_dec" ) + { + push( pop< Integer >() - 1 ); + } else if( word == "@i_add" ) { const auto lhs= pop< Integer >(); @@ -108,12 +166,31 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m { const auto val= pop< Integer >(); conditionals.push_back( val ? If : Else ); + resumeConditionals.push_back( currentState ); currentState= If; } + else if( word == "@define" ) + { + if( definition.has_value() ) throw std::runtime_error{ "Nested definitions not supported." }; + + definition= next(); + + if( C::debug ) std::cerr << "Starting function definition for " << definition.value() << std::endl; + } else if( word.at( 0 ) == '@' ) { - throw std::runtime_error{ "Unknown keyword: `"s + std::string{ word } - + "`" }; + const std::string invoke{ word.substr( 1 ) }; + if( not words.contains( invoke ) ) + { + throw std::runtime_error{ "Unknown keyword: `"s + std::string{ word } + + "`" }; + } + + const auto &def= words.at( invoke ); + + std::copy( rbegin( def ), rend( def ), front_inserter( tokens ) ); + + if( tokens.size() >= 1000 ) abort(); } else { diff --git a/js4g/StackMachine.h b/js4g/StackMachine.h index 31f3dfb..48bb3a8 100644 --- a/js4g/StackMachine.h +++ b/js4g/StackMachine.h @@ -42,14 +42,20 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m using Float= long double; - std::map< std::string, std::string > words; + std::map< std::string, std::vector< std::string > > words; // Which side of the current conditional to take. - enum ConditionalState { If, Else }; + enum ConditionalState { If, Else, Skipped }; std::vector< ConditionalState > conditionals; + std::vector< ConditionalState > resumeConditionals; ConditionalState currentState; + std::optional< std::string > definition; + + std::ostream &output; + + void runWord( std::string_view word ); std::string pop(); @@ -63,7 +69,7 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m template< typename T > void push( const T &t ); - std::ostream &output; + std::string next(); public: StackMachine( std::ostream &output= std::cout ); diff --git a/js4g/factorial.js4 b/js4g/factorial.js4 new file mode 100644 index 0000000..8029720 --- /dev/null +++ b/js4g/factorial.js4 @@ -0,0 +1,43 @@ +@define factorial + @dup + @if + @dup @i_dec + @factorial + @i_mul + @else + @i_inc + @endif +@enddef + +6 @factorial +@print + + +@define fib + @dup + @not + @if + @drop 1 + @else + @dup + @i_dec + @not + @if + @drop 1 + @else + @i_dec + @dup + @i_dec + @fib + @swap + @fib + @i_add + @endif + @endif +@enddef + + +8 @fib @print + +10 @factorial +@print diff --git a/js4g/fib.js4 b/js4g/fib.js4 new file mode 100644 index 0000000..8fe16a3 --- /dev/null +++ b/js4g/fib.js4 @@ -0,0 +1,26 @@ +@define fib + @dup + @not + @if + @drop 1 + @else + @dup + @i_dec + @not + @if + @drop 1 + @else + @i_dec + @dup + @i_dec + @fib + @swap + @fib + @i_add + @endif + @endif +@enddef + + +9 @dup @print @fib @print +