Sun, 15 Mar 2015 21:03:23 +0200
* InputSource is refactored into scope
--- a/Source/Scripting.h Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting.h Sun Mar 15 21:03:23 2015 +0200 @@ -33,8 +33,8 @@ /** * @page GScript Gorgon Script * - * Gorgon::Scripting allows embedded scripting capabilities to the users of the library. It has multiple dialects - * and full extension capabilities. + * Gorgon::Scripting allows embedded scripting capabilities to the users of the library. It has multiple + * dialects and an easy method for embedding functions. * */
--- a/Source/Scripting/Builtin.cpp Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/Builtin.cpp Sun Mar 15 21:03:23 2015 +0200 @@ -279,7 +279,7 @@ ), new MappedFunction("Replace", - "Replaces all the given substring in this string to another", String, String, { + "Replaces all instances of the given substring in this string with another string", String, String, { new Parameter("Search", "Search string to be replaced", String), new Parameter("Replace", "String to replace, if not specified, " "empty string is assumed", String, OptionalTag),
--- a/Source/Scripting/Compilers.h Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/Compilers.h Sun Mar 15 21:03:23 2015 +0200 @@ -25,7 +25,7 @@ /// zero. This does not necessarily mean there is no compilable source in the given input /// it may simply be an incomplete line. When there is no more input, caller should finish /// compilation by calling Finalize - virtual unsigned Compile(const std::string &input) = 0; + virtual unsigned Compile(const std::string &input, unsigned long pline) = 0; /// Finalizes the compilation. Compiler may throw an error about missing constructs at this /// point. @@ -39,7 +39,7 @@ class Intermediate : public Base { public: - virtual unsigned Compile(const std::string &input) override; + virtual unsigned Compile(const std::string &input, unsigned long pline) override; virtual void Finalize() override { } private: @@ -63,7 +63,7 @@ public: Programming() : compiler(List) { } - virtual unsigned Compile(const std::string &input) override; + virtual unsigned Compile(const std::string &input, unsigned long pline) override; virtual void Finalize() override;
--- a/Source/Scripting/Compilers/AST.h Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/Compilers/AST.h Sun Mar 15 21:03:23 2015 +0200 @@ -88,6 +88,9 @@ /// Starting character of the node. Used for error locating int Start = -1; + /// Starting line of this ASTNode + int Line = -1; + /// Textual data held by this node std::string Text;
--- a/Source/Scripting/Compilers/Generator.cpp Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/Compilers/Generator.cpp Sun Mar 15 21:03:23 2015 +0200 @@ -1,4 +1,4 @@ -#include "../InputSource.h" +#include "../Scope.h" #include "../Instruction.h" #include "../../Scripting.h" @@ -105,9 +105,9 @@ return ""; } - void Disassemble(InputSource &source, std::ostream &out) { + void Disassemble(Scope &scope, std::ostream &out) { unsigned long line=0; - while(auto inst=source.ReadInstruction(line++)) { + while(auto inst=scope.ReadInstruction(line++)) { out<<Disassemble(inst)<<std::endl; } }
--- a/Source/Scripting/Compilers/IL.cpp Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/Compilers/IL.cpp Sun Mar 15 21:03:23 2015 +0200 @@ -134,7 +134,7 @@ - unsigned Intermediate::Compile(const std::string &input) { + unsigned Intermediate::Compile(const std::string &input, unsigned long pline) { Instruction instruction;
--- a/Source/Scripting/Compilers/PD.cpp Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/Compilers/PD.cpp Sun Mar 15 21:03:23 2015 +0200 @@ -940,7 +940,7 @@ } } - unsigned Programming::Compile(const std::string &input) { + unsigned Programming::Compile(const std::string &input, unsigned long pline) { //If necessary split the line or request more input if(left.size()) { left.push_back('\n'); @@ -958,6 +958,8 @@ extractline(left, process, linestarts); auto ret=parse(process); + + ///... fix line and char start of ast tree if(ret) { try { @@ -992,7 +994,7 @@ void Programming::Finalize() { left.push_back(';'); - Compile(""); + Compile("", -1); } } } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Scripting/Input.h Sun Mar 15 21:03:23 2015 +0200 @@ -0,0 +1,137 @@ +#pragma once + +#include <stdint.h> +#include <string> +#include <vector> +#include <iomanip> +#include <fstream> +#include "../String.h" +#include "../Scripting.h" + +namespace Gorgon { namespace Scripting { + + class InputProvider { + public: + enum Dialect { + Console, + Programming, + Intermediate, + Binary + }; + + InputProvider(Dialect dialect) : dialect(dialect) {} + + Dialect GetDialect() const { + return dialect; + } + + void SetDialect(Dialect dialect) { + this->dialect=dialect; + checkdialect(); + } + + + /// This method should read a single physical line from the source. Logical line separation + /// is handled by InputSource. Return of false means no input is fetched as it is finished. + /// If there is a read error, rather than returning false, this function should throw. + /// newline parameter denotes that this line is a new line, not continuation of another. + virtual bool ReadLine(std::string &, bool newline) = 0; + + //virtual int ReadBinary(std::vector<Byte> &buffer) = 0; + + virtual void Reset() = 0; + + protected: + virtual void checkdialect() { } + + Dialect dialect; + }; + + /// Reads lines from the console + class ConsoleInput : public InputProvider { + public: + + /// Initializes console input. line number will be appended at the start of the prompt + ConsoleInput(Dialect dialect=InputProvider::Console, const std::string &prompt="> ") : InputProvider(dialect), + prompt(prompt) { } + + void SetPrompt(const std::string &prompt) { + this->prompt=prompt; + } + + virtual bool ReadLine(std::string &input, bool newline) override final { + line++; + std::cout<<std::setw(3)<<line<<prompt; + + return bool(std::getline(std::cin, input)); + } + + virtual void Reset() override { + line=0; + } + + protected: + virtual void checkdialect() override { + if(dialect==InputProvider::Binary) { + SetDialect(InputProvider::Console); + throw std::runtime_error("Cannot accept binary code from the console"); + } + } + + private: + std::string prompt; + int line=0; + }; + + /// Reads lines from a stream + class StreamInput : public InputProvider { + public: + StreamInput(std::istream &stream, Dialect dialect) : InputProvider(dialect), stream(stream) { + } + + virtual bool ReadLine(std::string &input, bool) override final { + return bool(std::getline(stream,input)); + } + + virtual void Reset() override { + stream.seekg(0); + } + + private: + std::istream &stream; + }; + + /// Reads lines from a file + class FileInput : public StreamInput { + public: + FileInput(const std::string &filename) : StreamInput(file, InputProvider::Programming) { + auto loc=filename.find_last_of('.'); + std::string ext=""; + if(loc!=filename.npos) + ext=filename.substr(loc); + + //determine dialect from the extension + if(ext.length()>=3 && ext.substr(0,3)=="gsb") { + dialect=InputProvider::Binary; + } + else if(ext.length()>=3 && ext.substr(0,3)=="gsc") { + dialect=InputProvider::Console; + } + else if(ext.length()>=3 && ext.substr(0,3)=="gsi") { + dialect=InputProvider::Intermediate; + } + else { // generally *.gs* + dialect=InputProvider::Programming; + } + + file.open(filename, dialect==InputProvider::Binary ? std::ios::out | std::ios::binary : std::ios::out); + if(!file.is_open()) { + throw std::runtime_error("Cannot open file"); + } + } + + private: + std::ifstream file; + }; + +} }
--- a/Source/Scripting/InputSource.h Sun Mar 15 10:37:34 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ -#pragma once - -#include <stdint.h> -#include <string> -#include <vector> -#include <iomanip> -#include <fstream> -#include "../String.h" -#include "../Scripting.h" -#include "Instruction.h" -#include "Compilers.h" - -namespace Gorgon { - - namespace Scripting { - - class InputProvider { - public: - enum Dialect { - Console, - Programming, - Intermediate, - Binary - }; - - InputProvider(Dialect dialect) : dialect(dialect) {} - - Dialect GetDialect() const { - return dialect; - } - - void SetDialect(Dialect dialect) { - this->dialect=dialect; - checkdialect(); - } - - - /// This method should read a single physical line from the source. Logical line separation - /// is handled by InputSource. Return of false means no input is fetched as it is finished. - /// If there is a read error, rather than returning false, this function should throw. - /// newline parameter denotes that this line is a new line, not continuation of another. - virtual bool ReadLine(std::string &, bool newline) = 0; - - //virtual int ReadBinary(std::vector<Byte> &buffer) = 0; - - virtual void Reset() = 0; - - protected: - virtual void checkdialect() { } - - Dialect dialect; - }; - - /// Reads lines from the console - class ConsoleInput : public InputProvider { - public: - - /// Initializes console input. line number will be appended at the start of the prompt - ConsoleInput(Dialect dialect=InputProvider::Console, const std::string &prompt="> ") : InputProvider(dialect), - prompt(prompt) { } - - void SetPrompt(const std::string &prompt) { - this->prompt=prompt; - } - - virtual bool ReadLine(std::string &input, bool newline) override final { - line++; - std::cout<<std::setw(3)<<line<<prompt; - - return bool(std::getline(std::cin, input)); - } - - virtual void Reset() override { - line=0; - } - - protected: - virtual void checkdialect() override { - if(dialect==InputProvider::Binary) { - SetDialect(InputProvider::Console); - throw std::runtime_error("Cannot accept binary code from the console"); - } - } - - private: - std::string prompt; - int line=0; - }; - - /// Reads lines from a stream - class StreamInput : public InputProvider { - public: - StreamInput(std::istream &stream, Dialect dialect) : InputProvider(dialect), stream(stream) { - } - - virtual bool ReadLine(std::string &input, bool) override final { - return bool(std::getline(stream,input)); - } - - virtual void Reset() override { - stream.seekg(0); - } - - private: - std::istream &stream; - }; - - /// Reads lines from a file - class FileInput : public StreamInput { - public: - FileInput(const std::string &filename) : StreamInput(file, InputProvider::Programming) { - auto loc=filename.find_last_of('.'); - std::string ext=""; - if(loc!=filename.npos) - ext=filename.substr(loc); - - //determine dialect from the extension - if(ext.length()>=3 && ext.substr(0,3)=="gsb") { - dialect=InputProvider::Binary; - } - else if(ext.length()>=3 && ext.substr(0,3)=="gsc") { - dialect=InputProvider::Console; - } - else if(ext.length()>=3 && ext.substr(0,3)=="gsi") { - dialect=InputProvider::Intermediate; - } - else { // generally *.gs* - dialect=InputProvider::Programming; - } - - file.open(filename, dialect==InputProvider::Binary ? std::ios::out | std::ios::binary : std::ios::out); - if(!file.is_open()) { - throw std::runtime_error("Cannot open file"); - } - } - - private: - std::ifstream file; - }; - - /// @cond INTERNAL - /// This class represents a logical line - class Line { - public: - Instruction instruction; - - unsigned long physical; - }; - /// @endcond - - class CompilerBase; - - /** - * Base class for input sources. This system allows different input sources to supply - * code to virtual machine. When source code is loaded into virtual machine, it stays - * until that all execution scopes that are operating in that inputscope is finished. - * This allows faster processing for loops. Additionally, function and similar keywords - * can store their data in their own inputsources - */ - class InputSource { - public: - - /// Constructor requires an input provider and a name to define this input source - InputSource(InputProvider &provider, const std::string &name); - - const Instruction *ReadInstruction(unsigned long line); - - unsigned long GetPhysicalLine() const { - return pline; - } - - unsigned ReadyInstructionCount() const { - return lines.size(); - } - - std::string GetName() const { return name; } - - /// Unloads an input source by erasing all current data. Unload should only be - /// called when no more callbacks can be performed and no more lines are left. - /// Additionally, no keyword scope should be active, otherwise a potential jump - /// back might cause undefined behavior. - void Unload() { - using std::swap; - - std::vector<Line> temp; - swap(temp, lines); - pline=0; - - provider.Reset(); - } - - private: - InputProvider &provider; - - unsigned long pline = 0; - std::string name; - - Compilers::Base *parser; - - /// Every logical line up until current execution point. They are kept so that - /// it is possible to jump back. Logical lines do not contain comments - std::vector<Line> lines; - }; - - } -}
--- a/Source/Scripting/Runtime.cpp Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/Runtime.cpp Sun Mar 15 21:03:23 2015 +0200 @@ -4,7 +4,7 @@ namespace Scripting { - int VariableScope::nextid=1; + int ExecutionScope::nextid=0; } } \ No newline at end of file
--- a/Source/Scripting/Runtime.h Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/Runtime.h Sun Mar 15 21:03:23 2015 +0200 @@ -8,7 +8,7 @@ #include "Reflection.h" #include "Data.h" -#include "InputSource.h" +#include "Scope.h" namespace Gorgon { @@ -202,11 +202,13 @@ uintptr_t source = 0; }; + + /// This is an instantiation of a scope class ExecutionScope { public: /// Constructor requires an input source. Execution scopes can share same input source - ExecutionScope(InputSource &source) : source(&source), name(source.GetName()+" #"+String::From(nextid++)) { + ExecutionScope(Scope &parent) : parent(parent), name(parent.GetName()+" #"+String::From(nextid++)) { } /// Jumps to the given line, line numbers start at zero. @@ -233,7 +235,7 @@ /// Returns the code at the current line and increments the current line const Instruction *Get() { - auto ret=source->ReadInstruction(current); + auto ret=parent.ReadInstruction(current); current++; return ret; @@ -246,41 +248,39 @@ /// Forces the compilation of entire input source void Compile() { int c=current; - while(source->ReadInstruction(c++)) ; + while(parent.ReadInstruction(c++)) ; } /// Returns the code at the current line without incrementing it. const Instruction *Peek() { - return source->ReadInstruction(current); + return parent.ReadInstruction(current); } /// Returns the code at the given line without incrementing current line. const Instruction *Peek(unsigned long line) { - return source->ReadInstruction(line); + return parent.ReadInstruction(line); } void MoveToEnd() { - current=source->ReadyInstructionCount(); + current=parent.ReadyInstructionCount(); } - InputSource &GetSource() const { return *source; } + Scope &GetScope() const { return parent; } /// Variables defined in this scope Containers::Hashmap<std::string, class Variable, &Variable::GetName, std::map, String::CaseInsensitiveLess> Variables; - private: + private: std::string name; static int nextid; + + Scope &parent; /// Current logical line unsigned long current = 0; - - /// InputSource for the current execution scope - InputSource *source; }; - ///@cond INTERNAL class CustomFunction : public Function { public:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Scripting/Scope.cpp Sun Mar 15 21:03:23 2015 +0200 @@ -0,0 +1,55 @@ +#include "Scope.h" + +namespace Gorgon { namespace Scripting { + + const Instruction *Scope::ReadInstruction(unsigned long line) { + if(line<lines.size()) { + return &lines[line].instruction; + } + else { + while(line>=lines.size()) { + std::string str; + + if(!provider.ReadLine(str, true)) { + parser->Finalize(); + + return nullptr; + } + pline++; + + int compiled=0; + try { + compiled=parser->Compile(str, pline); + } + catch(ParseError &err) { + if(err.GetLine()<0) + err.SetLine(pline-err.GetLine()); + + throw err; + } + + for(int i=parser->List.size()-compiled;i<parser->List.size();i++) { + lines.push_back({parser->List[i], pline}); + } + + parser->List.erase(parser->List.end()-compiled, parser->List.end()); + } + + return &lines[line].instruction; + } + } + + Scope::Scope(InputProvider &provider, const std::string &name) : provider(provider), name(name) { + switch(provider.GetDialect()) { + case InputProvider::Intermediate: + parser=new Compilers::Intermediate(); + break; + case InputProvider::Programming: + parser=new Compilers::Programming(); + break; + default: + Utils::ASSERT_FALSE("Unknown dialect"); + } + } + +} } \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Scripting/Scope.h Sun Mar 15 21:03:23 2015 +0200 @@ -0,0 +1,87 @@ +#pragma once + +#include <string> +#include <vector> +#include "../String.h" +#include "../Scripting.h" +#include "Instruction.h" +#include "Input.h" +#include "Compilers.h" + +namespace Gorgon { namespace Scripting { + + /// @cond INTERNAL + /// This class represents a logical line + class Line { + public: + Instruction instruction; + + unsigned long physical; + }; + /// @endcond + + class CompilerBase; + + /** + * A new scope is created automatically when a new input source or a function like construct + * is created. Scopes can be linked to each other. There are two methods to supply codes to + * a scope: from an InputProvider and directly registering instructions to it. + */ + class Scope { + public: + + /// Constructor requires an input provider and a name to define this input source + Scope(InputProvider &provider, const std::string &name); + + /// This constructor allows a scope without an input provider. This allows function + /// like constructs to have their own scopes with designated source code supplied + /// from external sources + Scope(Scope &parent, const std::string &name); + + /// Reads the instruction in the given line + const Instruction *ReadInstruction(unsigned long line); + + /// Current physical line + unsigned long GetPhysicalLine() const { + return pline; + } + + unsigned ReadyInstructionCount() const { + return lines.size(); + } + + + std::string GetName() const { return name; } + + void SaveInstruction(Instruction inst, unsigned long pline) { + lines.push_back({inst, pline}); + } + + /// Unloads an input source by erasing all current data. Unload should only be + /// called when no more callbacks can be performed and no more lines are left. + /// Additionally, no keyword scope should be active, otherwise a potential jump + /// back might cause undefined behavior. + void Unload() { + using std::swap; + + std::vector<Line> temp; + swap(temp, lines); + pline=0; + + provider.Reset(); + } + + private: + InputProvider &provider; + + unsigned long pline = 0; + std::string name; + + Compilers::Base *parser; + + /// Every logical line up until current execution point. They are kept so that + /// it is possible to jump back. Logical lines do not contain comments + std::vector<Line> lines; + }; + +} } \ No newline at end of file
--- a/Source/Scripting/Scripting.cpp Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/Scripting.cpp Sun Mar 15 21:03:23 2015 +0200 @@ -6,56 +6,6 @@ #include "Embedding.h" namespace Gorgon { namespace Scripting { - - const Instruction *InputSource::ReadInstruction(unsigned long line) { - if(line<lines.size()) { - return &lines[line].instruction; - } - else { - while(line>=lines.size()) { - std::string str; - - if(!provider.ReadLine(str, true)) { - parser->Finalize(); - - return nullptr; - } - pline++; - - int compiled=0; - try { - compiled=parser->Compile(str); - } - catch(ParseError &err) { - if(err.GetLine()<0) - err.SetLine(pline-err.GetLine()); - - throw err; - } - - for(int i=parser->List.size()-compiled;i<parser->List.size();i++) { - lines.push_back({parser->List[i], pline}); - } - - parser->List.erase(parser->List.end()-compiled, parser->List.end()); - } - - return &lines[line].instruction; - } - } - - InputSource::InputSource(InputProvider &provider, const std::string &name) : provider(provider), name(name) { - switch(provider.GetDialect()) { - case InputProvider::Intermediate: - parser=new Compilers::Intermediate(); - break; - case InputProvider::Programming: - parser=new Compilers::Programming(); - break; - default: - Utils::ASSERT_FALSE("Unknown dialect"); - } - } MappedValueType<Data, String::From<Data>, GetVariableValue> Variant = {"Variant", @@ -63,6 +13,6 @@ Data::Invalid() }; - int ExecutionScope::nextid=0; + std::set<std::string, String::CaseInsensitiveLess> KeywordNames; } }
--- a/Source/Scripting/VirtualMachine.cpp Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/VirtualMachine.cpp Sun Mar 15 21:03:23 2015 +0200 @@ -384,20 +384,20 @@ } } - void VirtualMachine::Start(InputSource &source) { + void VirtualMachine::Start(Scope &scope) { Activate(); - inputsources.Add(source); + scopes.Add(scope); - executionscopes.Add(new ExecutionScope(source)); + executionscopes.Add(new ExecutionScope(scope)); Run(); } - void VirtualMachine::Begin(InputSource &source) { + void VirtualMachine::Begin(Scope &scope) { Activate(); - inputsources.Add(source); + scopes.Add(scope); - executionscopes.Add(new ExecutionScope(source)); + executionscopes.Add(new ExecutionScope(scope)); } void VirtualMachine::Run() { @@ -447,7 +447,7 @@ executionscopes.Last()->MoveToEnd(); if(ex.GetLine()<=0) { - ex.SetLine(-ex.GetLine()+executionscopes.Last()->GetSource().GetPhysicalLine()); + ex.SetLine(-ex.GetLine()+executionscopes.Last()->GetScope().GetPhysicalLine()); } } @@ -506,8 +506,6 @@ auto &vars=executionscopes.Last()->Variables; auto var=vars.Find(String::ToLower(name)); - //TODO static variables? may be they can be handled by function function - //if found if(var.IsValid()) { //return
--- a/Source/Scripting/VirtualMachine.h Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/VirtualMachine.h Sun Mar 15 21:03:23 2015 +0200 @@ -11,7 +11,6 @@ #include "Reflection.h" #include "Runtime.h" -#include "InputSource.h" #include "Data.h" #include "Exceptions.h" @@ -58,10 +57,10 @@ void Run(unsigned executiontarget); /// This method starts the virtual machine with the given input source - void Start(InputSource &source); + void Start(Scope &scope); /// This method begins a new execution scope without starting execution - void Begin(InputSource &source); + void Begin(Scope &scope); /// Commands virtual machine to compile current execution scope. Might cause issues with interactive /// input sources. @@ -228,7 +227,7 @@ std::multimap<std::string, const Type*, String::CaseInsensitiveLess> types; Containers::Collection<ExecutionScope> executionscopes; - Containers::Collection<InputSource> inputsources; + Containers::Collection<Scope> scopes; Library runtime;
--- a/Source/Scripting/dir.cmake Sun Mar 15 10:37:34 2015 +0200 +++ b/Source/Scripting/dir.cmake Sun Mar 15 21:03:23 2015 +0200 @@ -6,10 +6,13 @@ Data.cpp Embedding.h Instruction.h - InputSource.h + Input.h Reflection.h Reflection.cpp Runtime.h + Runtime.cpp + Scope.h + Scope.cpp VirtualMachine.h VirtualMachine.cpp
--- a/Testing/Source/Manual/Generic.cpp Sun Mar 15 10:37:34 2015 +0200 +++ b/Testing/Source/Manual/Generic.cpp Sun Mar 15 21:03:23 2015 +0200 @@ -123,7 +123,7 @@ int main() { std::stringstream ss(source); StreamInput streaminput={std::cin, InputProvider::Programming}; - InputSource input={streaminput, ""}; + Scope input={streaminput, ""}; VirtualMachine vm;
--- a/Testing/Source/Manual/PDParser.cpp Sun Mar 15 10:37:34 2015 +0200 +++ b/Testing/Source/Manual/PDParser.cpp Sun Mar 15 21:03:23 2015 +0200 @@ -33,7 +33,7 @@ exit(0); } - parser.Compile(str); + parser.Compile(str, 1); for(; ind<parser.List.size(); ind++) { std::cout<<Disassemble(&parser.List[ind])<<std::endl; }