From b0a7797280fff2157d81382c40fcc1de7874fa78 Mon Sep 17 00:00:00 2001 From: howsohazard <143410553+howsohazard@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:19:42 -0400 Subject: [PATCH] 21359: Enhances parse opcode to handle transactional parsing and return warnings, improves warning and error reporting, improves load performance, MINOR (#271) --- docs/language.js | 6 +- src/Amalgam/AssetManager.cpp | 18 +- src/Amalgam/Parser.cpp | 109 +++--- src/Amalgam/Parser.h | 52 ++- src/Amalgam/amlg_code/full_test.amlg | 12 +- src/Amalgam/amlg_code/test.amlg | 68 +--- src/Amalgam/entity/Entity.cpp | 4 +- .../evaluablenode/EvaluableNodeManagement.cpp | 3 + src/Amalgam/interpreter/Interpreter.h | 8 +- .../interpreter/InterpreterDebugger.cpp | 7 +- .../interpreter/InterpreterOpcodesBase.cpp | 37 +- .../InterpreterOpcodesDataTypes.cpp | 4 +- .../InterpreterOpcodesEntityAccess.cpp | 3 +- .../InterpreterOpcodesListManipulation.cpp | 2 +- .../InterpreterOpcodesTransformations.cpp | 4 +- src/Amalgam/out.txt | 325 ++++++++++-------- 16 files changed, 373 insertions(+), 289 deletions(-) diff --git a/docs/language.js b/docs/language.js index 98c5d474..1e80fa37 100644 --- a/docs/language.js +++ b/docs/language.js @@ -27,10 +27,10 @@ var data = [ }, { - "parameter" : "parse string str", - "output" : "code", + "parameter" : "parse string str [bool transactional] [bool return_warnings]", + "output" : "*", "new value" : "new", - "description" : "String is parsed into code, and the result is returned.", + "description" : "String is parsed into code, and the result is returned. If transactional is false, the default, it will attempt to parse the whole string and will return the closest code possible if there are any parse issues. If transactional is true, it will parse the string transactionally, meaning that any node that has a parse error or is incomplete will be omitted along with all child nodes except for the top node. If return_warnings is true, which defaults to false, it will instead return a list, where the first element is the code and the second element is a list of warnings.", "example" : "(parse \"(list 1 2 3 4 5)\")" }, diff --git a/src/Amalgam/AssetManager.cpp b/src/Amalgam/AssetManager.cpp index f66511b6..ac8c892b 100644 --- a/src/Amalgam/AssetManager.cpp +++ b/src/Amalgam/AssetManager.cpp @@ -63,7 +63,10 @@ EvaluableNodeReference AssetManager::LoadResourcePath(std::string &resource_path code.erase(0, 3); } - return Parser::Parse(code, enm, &resource_path, debugSources); + auto [node, warnings, char_with_error] = Parser::Parse(code, enm, false, &resource_path, debugSources); + for(auto &w : warnings) + std::cerr << w << std::endl; + return node; } else if(file_type == FILE_EXTENSION_JSON) return EvaluableNodeReference(EvaluableNodeJSONTranslation::Load(processed_resource_path, enm, status), true); @@ -74,10 +77,10 @@ EvaluableNodeReference AssetManager::LoadResourcePath(std::string &resource_path else if(file_type == FILE_EXTENSION_COMPRESSED_AMALGAM_CODE) { BinaryData compressed_data; - auto [error_mesg, version, success] = LoadFileToBuffer(processed_resource_path, file_type, compressed_data); + auto [error_msg, version, success] = LoadFileToBuffer(processed_resource_path, file_type, compressed_data); if(!success) { - status.SetStatus(false, error_mesg, version); + status.SetStatus(false, error_msg, version); return EvaluableNodeReference::Null(); } @@ -86,17 +89,20 @@ EvaluableNodeReference AssetManager::LoadResourcePath(std::string &resource_path if(strings.size() == 0) return EvaluableNodeReference::Null(); - return Parser::Parse(strings[0], enm, &resource_path, debugSources); + auto [node, warnings, char_with_error] = Parser::Parse(strings[0], enm, false, &resource_path, debugSources); + for(auto &w : warnings) + std::cerr << w << std::endl; + return node; } else //just load the file as a string { std::string s; - auto [error_mesg, version, success] = LoadFileToBuffer(processed_resource_path, file_type, s); + auto [error_msg, version, success] = LoadFileToBuffer(processed_resource_path, file_type, s); if(success) return EvaluableNodeReference(enm->AllocNode(ENT_STRING, s), true); else { - status.SetStatus(false, error_mesg, version); + status.SetStatus(false, error_msg, version); return EvaluableNodeReference::Null(); } } diff --git a/src/Amalgam/Parser.cpp b/src/Amalgam/Parser.cpp index 26d2320c..f0f9bf92 100644 --- a/src/Amalgam/Parser.cpp +++ b/src/Amalgam/Parser.cpp @@ -15,6 +15,9 @@ Parser::Parser() lineNumber = 0; lineStartPos = 0; numOpenParenthesis = 0; + originalSource = ""; + topNode = nullptr; + charOffsetStartOfLastCompletedCode = std::numeric_limits::max(); } std::string Parser::Backslashify(const std::string &s) @@ -57,16 +60,16 @@ std::string Parser::Backslashify(const std::string &s) return b; } -EvaluableNodeReference Parser::Parse(std::string &code_string, EvaluableNodeManager *enm, - std::string *original_source, bool debug_sources) +std::tuple, size_t> + Parser::Parse(std::string &code_string, + EvaluableNodeManager *enm, bool transactional_parse, std::string *original_source, bool debug_sources) { Parser pt; pt.code = &code_string; - pt.pos = 0; pt.preevaluationNodes.clear(); pt.evaluableNodeManager = enm; + pt.transactionalParse = transactional_parse; - pt.originalSource = ""; if(original_source != nullptr) { //convert source to minimal absolute path @@ -84,20 +87,13 @@ EvaluableNodeReference Parser::Parse(std::string &code_string, EvaluableNodeMana pt.debugSources = debug_sources; - EvaluableNode *parse_tree = pt.ParseNextBlock(); - - if(!pt.originalSource.empty()) - { - if(pt.numOpenParenthesis > 0) - std::cerr << "Warning: " << pt.numOpenParenthesis << " missing parenthesis in " << pt.originalSource << std::endl; - else if(pt.numOpenParenthesis < 0) - std::cerr << "Warning: " << -pt.numOpenParenthesis << " extra parenthesis in " << pt.originalSource << std::endl; - } + pt.ParseCode(); pt.PreevaluateNodes(); - EvaluableNodeManager::UpdateFlagsForNodeTree(parse_tree); - return EvaluableNodeReference(parse_tree, true); + return std::make_tuple(EvaluableNodeReference(pt.topNode, true), + std::move(pt.warnings), + pt.charOffsetStartOfLastCompletedCode); } std::string Parser::Unparse(EvaluableNode *tree, EvaluableNodeManager *enm, @@ -303,13 +299,11 @@ void Parser::SkipWhitespaceAndAccumulateAttributes(EvaluableNode *target) if(debugSources) { std::string new_comment = sourceCommentPrefix; - new_comment += std::to_string(lineNumber + 1); + new_comment += StringManipulation::NumberToString(GetCurrentLineNumber()); new_comment += ' '; - std::string_view line_to_opcode(&(*code)[lineStartPos], pos - lineStartPos); - size_t column_number = StringManipulation::GetNumUTF8Characters(line_to_opcode); - - new_comment += std::to_string(column_number + 1); + size_t column_number = GetCurrentCharacterNumberInLine(); + new_comment += StringManipulation::NumberToString(column_number); new_comment += ' '; new_comment += originalSource; new_comment += "\r\n"; @@ -463,12 +457,10 @@ EvaluableNode *Parser::GetNextToken(EvaluableNode *parent_node, EvaluableNode *n } else { - //invalid opcode, warn if possible and store the identifier as a string - if(!originalSource.empty()) - std::cerr << "Warning: " << "Invalid opcode \"" << token << "\" at line " << lineNumber + 1 << " of " << originalSource << std::endl; + EmitWarning("Invalid opcode \"" + token + "\"; transforming to apply opcode using the invalid opcode type"); - new_token->SetType(ENT_STRING, evaluableNodeManager, false); - new_token->SetStringValue(token); + new_token->SetType(ENT_APPLY, evaluableNodeManager, false); + new_token->AppendOrderedChildNode(evaluableNodeManager->AllocNode(token)); } } else if(cur_char == '[') @@ -492,12 +484,12 @@ EvaluableNode *Parser::GetNextToken(EvaluableNode *parent_node, EvaluableNode *n if(cur_char == ']') { if(parent_node_type != ENT_LIST) - std::cerr << "Warning: " << "Mismatched ] at line " << lineNumber + 1 << " of " << originalSource << std::endl; + EmitWarning("Mismatched ]"); } else if(cur_char == '}') { if(parent_node_type != ENT_ASSOC) - std::cerr << "Warning: " << "Mismatched } at line " << lineNumber + 1 << " of " << originalSource << std::endl; + EmitWarning("Mismatched }"); } pos++; //skip closing parenthesis @@ -549,14 +541,18 @@ void Parser::FreeNode(EvaluableNode *node) preevaluationNodes.pop_back(); } -EvaluableNode *Parser::ParseNextBlock() +void Parser::ParseCode() { - EvaluableNode *tree_top = nullptr; EvaluableNode *cur_node = nullptr; //as long as code left while(pos < code->size()) { + //if at the top level node and starting to parse a new structure, + //then all previous ones have completed and can mark this new position as a successful start + if(topNode != nullptr && cur_node == topNode) + charOffsetStartOfLastCompletedCode = pos; + EvaluableNode *n = GetNextToken(cur_node); //if end of a list @@ -564,13 +560,13 @@ EvaluableNode *Parser::ParseNextBlock() { //nothing here at all if(cur_node == nullptr) - return nullptr; + break; const auto &parent = parentNodes.find(cur_node); //if no parent, then all finished if(parent == end(parentNodes) || parent->second == nullptr) - return tree_top; + break; //jump up to the parent node cur_node = parent->second; @@ -579,9 +575,9 @@ EvaluableNode *Parser::ParseNextBlock() else //got some token { //if it's the first token, then put it up top - if(tree_top == nullptr) + if(topNode == nullptr) { - tree_top = n; + topNode = n; cur_node = n; continue; } @@ -606,12 +602,12 @@ EvaluableNode *Parser::ParseNextBlock() } else { - std::cerr << "Warning: " << "Missing ) at line " << lineNumber + 1 << " of " << originalSource << std::endl; + EmitWarning("Missing )"); } } else //no more code { - std::cerr << "Warning: " << "Mismatched ) at line " << lineNumber + 1 << " of " << originalSource << std::endl; + break; } } @@ -628,13 +624,13 @@ EvaluableNode *Parser::ParseNextBlock() { //nothing here at all if(cur_node == nullptr) - return nullptr; + break; const auto &parent = parentNodes.find(cur_node); //if no parent, then all finished if(parent == end(parentNodes) || parent->second == nullptr) - return tree_top; + break; //jump up to the parent node cur_node = parent->second; @@ -652,14 +648,40 @@ EvaluableNode *Parser::ParseNextBlock() if(n->GetType() == ENT_NOT_A_BUILT_IN_TYPE) { n->SetType(ENT_NULL, nullptr, false); - if(!originalSource.empty()) - std::cerr << "Warning: " << " Invalid opcode at line " << lineNumber + 1 << " of " << originalSource << std::endl; + EmitWarning("Invalid opcode"); } } + if(transactionalParse && warnings.size() > 0 && cur_node == topNode) + break; } - return tree_top; + int64_t num_allowed_open_parens = 0; + if(transactionalParse) + { + num_allowed_open_parens = 1; + + //if anything went wrong with the last transaction, remove it + if(warnings.size() > 0 || numOpenParenthesis > 1) + { + if(EvaluableNode::IsOrderedArray(topNode)) + { + auto &top_node_ocn = topNode->GetOrderedChildNodesReference(); + top_node_ocn.pop_back(); + } + else //nothing came through correctly + { + topNode = nullptr; + } + } + } + + if(numOpenParenthesis > num_allowed_open_parens) + EmitWarning(StringManipulation::NumberToString( + static_cast(numOpenParenthesis - num_allowed_open_parens)) + " missing closing parenthesis"); + else if(numOpenParenthesis < 0) + EmitWarning(StringManipulation::NumberToString(static_cast(-numOpenParenthesis)) + + " extra closing parenthesis"); } void Parser::AppendComments(EvaluableNode *n, size_t indentation_depth, bool pretty, std::string &to_append) @@ -1165,6 +1187,8 @@ EvaluableNode *Parser::GetNodeFromRelativeCodePath(EvaluableNode *path) void Parser::PreevaluateNodes() { + //only need to update flags if any nodes actually change + bool any_nodes_changed = false; for(auto &n : preevaluationNodes) { if(n == nullptr) @@ -1190,6 +1214,7 @@ void Parser::PreevaluateNodes() if(cn == n) { cn = target; + any_nodes_changed = true; break; } } @@ -1201,9 +1226,13 @@ void Parser::PreevaluateNodes() if(cn == n) { cn = target; + any_nodes_changed = true; break; } } } } + + if(any_nodes_changed) + EvaluableNodeManager::UpdateFlagsForNodeTree(topNode); } diff --git a/src/Amalgam/Parser.h b/src/Amalgam/Parser.h index a546ab23..06ee4d14 100644 --- a/src/Amalgam/Parser.h +++ b/src/Amalgam/Parser.h @@ -100,11 +100,14 @@ class Parser s.push_back(' '); } - //Parses the code string and returns a tree of EvaluableNodeReference that represents the code + //Parses the code string and returns a tree of EvaluableNodeReference that represents the code, + // as well as the offset of any error, or larger than the length of code_string if no errors + //if transactional_parse is true, then it will ignore any incomplete or erroneous opcodes except the outermost one //if original_source is a valid string, it will emit any warnings to stderr //if debug_sources is true, it will prepend each node with a comment indicating original source - static EvaluableNodeReference Parse(std::string &code_string, EvaluableNodeManager *enm, - std::string *original_source = nullptr, bool debug_sources = false); + static std::tuple, size_t> + Parse(std::string &code_string, EvaluableNodeManager *enm, + bool transactional_parse = false, std::string *original_source = nullptr, bool debug_sources = false); //Returns a string that represents the tree // if expanded_whitespace, will emit additional whitespace to make it easier to read @@ -169,8 +172,8 @@ class Parser //deallocates the current node in case there is an early exit or error void FreeNode(EvaluableNode *node); - //Parses the next block of code, then returns the block - EvaluableNode *ParseNextBlock(); + //Parses the next block of code into topNode + void ParseCode(); //Prints out all comments for the respective node static void AppendComments(EvaluableNode *n, size_t indentation_depth, bool pretty, std::string &to_append); @@ -183,6 +186,31 @@ class Parser StringInternPool::StringID key_sid, EvaluableNode *n, EvaluableNode *parent, bool expanded_whitespace, size_t indentation_depth, bool need_initial_space); + size_t GetCurrentLineNumber() + { + return lineNumber + 1; + } + + size_t GetCurrentCharacterNumberInLine() + { + std::string_view line_to_opcode(&(*code)[lineStartPos], pos - lineStartPos); + size_t char_number = StringManipulation::GetNumUTF8Characters(line_to_opcode); + return char_number + 1; + } + + //appends the warning string on to warnings + inline void EmitWarning(std::string warning) + { + std::string combined = "Warning: " + warning + + " at line " + StringManipulation::NumberToString(GetCurrentLineNumber()) + + ", column " + StringManipulation::NumberToString(GetCurrentCharacterNumberInLine()); + + if(!originalSource.empty()) + combined += " of " + originalSource; + + warnings.emplace_back(combined); + } + //Appends to the string s that represents the code tree //if expanded_whitespace, then it will add whitespace as appropriate to make it pretty // each line is additionally indented by the number of spaces specified @@ -217,14 +245,26 @@ class Parser //if true, will prepend debug sources to node comments bool debugSources; + //the top node of everything being parsed + EvaluableNode *topNode; + //contains a list of nodes that need to be preevaluated on parsing std::vector preevaluationNodes; + //any warnings from parsing + std::vector warnings; + //parentNodes contains each reference as the key and the parent as the value EvaluableNode::ReferenceAssocType parentNodes; EvaluableNodeManager *evaluableNodeManager; - //character used for indendation + //if true, then it will ignore any incomplete or erroneous opcodes except the outermost one + bool transactionalParse; + + //offset of the last code that was properly completed + size_t charOffsetStartOfLastCompletedCode; + + //character used for indentation static const char indentationCharacter = '\t'; }; diff --git a/src/Amalgam/amlg_code/full_test.amlg b/src/Amalgam/amlg_code/full_test.amlg index dd48d3cc..a217ee86 100644 --- a/src/Amalgam/amlg_code/full_test.amlg +++ b/src/Amalgam/amlg_code/full_test.amlg @@ -14,10 +14,6 @@ (#"label 1" print "hello world: " (* #"label number 2" 3 4) #"label 3" " and " (* 1 2) "\n") - (print "--non-keyword token--\n") - (print (call (parse "(6)")) "\n") - (print (call (parse "(notakeyword)")) "\n") - (print "--get_defaults--\n") (print (get_defaults "mutation_opcodes")) (print (get_defaults "mutation_types")) @@ -29,6 +25,14 @@ (print (unparse (associate "a" 1 "b" 2 "c" (list "alpha" "beta" "gamma"))) "\n") (print (unparse (associate "a" 1 "b" 2 "c" (list "alpha" "beta" "gamma")) (true)) "\n") + (print (parse "(6)") "\n") + (print (parse "(notakeyword)") "\n") + + (print (parse "(seq (+ 1 2) (+ " (true) )) + (print (parse "(seq (+ 1 2) (+ " (false) (true))) + (print (parse "(seq (+ 1 2) (+ " (true) (true))) + (print (parse "(seq (+ 1 2) (+ (a ) 3) " (true) (true))) + (print "--if--\n") (if 1 (print "if 1\n")) (if 0 (print "if 2 error\n") (print "if 2\n")) diff --git a/src/Amalgam/amlg_code/test.amlg b/src/Amalgam/amlg_code/test.amlg index 4ee874a5..065c2750 100644 --- a/src/Amalgam/amlg_code/test.amlg +++ b/src/Amalgam/amlg_code/test.amlg @@ -1,59 +1,11 @@ -(call_sandboxed - (lambda - [ - - ;reacts to the input that is passed in - #4ouZLU #react #lxQ3I5 #GxG9 #haploid - (query_mode - #g #_2yemX #qjFY #dbbQ #LBEWx1 #x_g68z1QEE #OXyA #yDfIgfceZMM #2RC20 - (generalized_distance - (null) - @(target 2) - (null) - (null) - (null) - (null) - (null) - (null) - #E00LE #O #crZz_ #Ksarqs #0Od_ #qDbE1_ #V3wrIS #cz2 #a #dBTli #e4y #BoR #IK #BjTV #iRXo4P #po #7sA - (rand (null)) - (null) - @(get - (target 2) - 8 - ) - (null) - @(get - (target 2) - 8 - ) - @(get - (target 2) - 8 - ) - (null) - (null) - (null) - (null) - ) - @(get - (target 2) - 0 - ) - ) - ] - ) - (null) - ;total number of execution cycles - 2000 - ;count the entity size toward total memory allowed to be used - (- 2000 (total_entity_size entity)) - ;limit stack depth to something reasonable - 200 - ;maximum contained entities - 5 - ;maximum entity depth - 3 - ;max entity name length - 25 +(seq + (print (parse + "(seq (+ 1 2) (+ (+ 3 4) 5))" + )) + + (print (parse + "(seq (+ 1 2) (+ (3 4) 5)" + )) + + ) \ No newline at end of file diff --git a/src/Amalgam/entity/Entity.cpp b/src/Amalgam/entity/Entity.cpp index 8e8ad7d3..846e02cf 100644 --- a/src/Amalgam/entity/Entity.cpp +++ b/src/Amalgam/entity/Entity.cpp @@ -921,8 +921,8 @@ void Entity::SetRoot(EvaluableNode *_code, bool allocated_with_entity_enm, Evalu void Entity::SetRoot(std::string &code_string, EvaluableNodeManager::EvaluableNodeMetadataModifier metadata_modifier, std::vector *write_listeners) { - EvaluableNodeReference new_code = Parser::Parse(code_string, &evaluableNodeManager); - SetRoot(new_code, true, metadata_modifier, write_listeners); + auto [node, warnings, char_with_error] = Parser::Parse(code_string, &evaluableNodeManager); + SetRoot(node, true, metadata_modifier, write_listeners); } void Entity::AccumRoot(EvaluableNodeReference accum_code, bool allocated_with_entity_enm, diff --git a/src/Amalgam/evaluablenode/EvaluableNodeManagement.cpp b/src/Amalgam/evaluablenode/EvaluableNodeManagement.cpp index 4b9c7af1..11c12898 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeManagement.cpp +++ b/src/Amalgam/evaluablenode/EvaluableNodeManagement.cpp @@ -68,6 +68,9 @@ EvaluableNode *EvaluableNodeManager::AllocNode(EvaluableNode *original, Evaluabl EvaluableNode *EvaluableNodeManager::AllocListNodeWithOrderedChildNodes(EvaluableNodeType child_node_type, size_t num_child_nodes) { + if(num_child_nodes == 0) + return AllocNode(ENT_LIST); + size_t num_allocated = 0; size_t num_to_alloc = num_child_nodes + 1; size_t num_total_nodes_needed = 0; diff --git a/src/Amalgam/interpreter/Interpreter.h b/src/Amalgam/interpreter/Interpreter.h index 01abb4b0..35a592c6 100644 --- a/src/Amalgam/interpreter/Interpreter.h +++ b/src/Amalgam/interpreter/Interpreter.h @@ -155,8 +155,8 @@ class Interpreter // if performance_constraints is not nullptr, then it will limit execution appropriately Interpreter(EvaluableNodeManager *enm, RandomStream rand_stream, std::vector *write_listeners, PrintListener *print_listener, - PerformanceConstraints *performance_constraints = nullptr, - Entity *t = nullptr, Interpreter *calling_interpreter = nullptr); + PerformanceConstraints *performance_constraints, + Entity *t, Interpreter *calling_interpreter); ~Interpreter() { } @@ -714,7 +714,7 @@ class Interpreter Interpreter interpreter(parentInterpreter->evaluableNodeManager, rand_seed, parentInterpreter->writeListeners, parentInterpreter->printListener, - parentInterpreter->performanceConstraints, parentInterpreter->curEntity); + parentInterpreter->performanceConstraints, parentInterpreter->curEntity, parentInterpreter); interpreter.memoryModificationLock = Concurrency::ReadLock(enm->memoryModificationMutex); @@ -780,7 +780,7 @@ class Interpreter Interpreter interpreter(parentInterpreter->evaluableNodeManager, rand_seed, parentInterpreter->writeListeners, parentInterpreter->printListener, - parentInterpreter->performanceConstraints, parentInterpreter->curEntity); + parentInterpreter->performanceConstraints, parentInterpreter->curEntity, parentInterpreter); interpreter.memoryModificationLock = Concurrency::ReadLock(enm->memoryModificationMutex); diff --git a/src/Amalgam/interpreter/InterpreterDebugger.cpp b/src/Amalgam/interpreter/InterpreterDebugger.cpp index d85eaf98..0054fe73 100644 --- a/src/Amalgam/interpreter/InterpreterDebugger.cpp +++ b/src/Amalgam/interpreter/InterpreterDebugger.cpp @@ -534,8 +534,11 @@ EvaluableNodeReference Interpreter::InterpretNode_DEBUG(EvaluableNode *en, bool else if(command == "eval") { SetDebuggingState(false); - EvaluableNode *to_eval = Parser::Parse(input, evaluableNodeManager); - EvaluableNodeReference result = InterpretNodeForImmediateUse(to_eval); + auto [node, warnings, char_with_error] = Parser::Parse(input, evaluableNodeManager); + for(auto &w : warnings) + std::cerr << w << std::endl; + + EvaluableNodeReference result = InterpretNodeForImmediateUse(node); std::cout << Parser::Unparse(result, evaluableNodeManager, true, true, true) << std::endl; SetDebuggingState(true); } diff --git a/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp b/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp index c5b037d6..fe8d22fe 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp @@ -191,7 +191,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_SYSTEM(EvaluableNode *en, { auto [public_key, secret_key] = GenerateSignatureKeyPair(); EvaluableNode *list = evaluableNodeManager->AllocListNodeWithOrderedChildNodes(ENT_STRING, 2); - auto &list_ocn = list->GetOrderedChildNodes(); + auto &list_ocn = list->GetOrderedChildNodesReference(); list_ocn[0]->SetStringValue(public_key); list_ocn[1]->SetStringValue(secret_key); @@ -202,7 +202,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_SYSTEM(EvaluableNode *en, { auto [public_key, secret_key] = GenerateEncryptionKeyPair(); EvaluableNode *list = evaluableNodeManager->AllocListNodeWithOrderedChildNodes(ENT_STRING, 2); - auto &list_ocn = list->GetOrderedChildNodes(); + auto &list_ocn = list->GetOrderedChildNodesReference(); list_ocn[0]->SetStringValue(public_key); list_ocn[1]->SetStringValue(secret_key); @@ -295,12 +295,38 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_PARSE(EvaluableNode *en, b auto &ocn = en->GetOrderedChildNodes(); if(ocn.size() == 0) return EvaluableNodeReference::Null(); + + bool transactional_parse = false; + if(ocn.size() > 1) + transactional_parse = InterpretNodeIntoBoolValue(ocn[1]); + + bool return_warnings = false; + if(ocn.size() > 2) + return_warnings = InterpretNodeIntoBoolValue(ocn[2]); + //get the string to parse auto [valid_string, to_parse] = InterpretNodeIntoStringValue(ocn[0]); if(!valid_string) return EvaluableNodeReference::Null(); - return Parser::Parse(to_parse, evaluableNodeManager); + auto [node, warnings, char_with_error] = Parser::Parse(to_parse, evaluableNodeManager, transactional_parse); + + if(!return_warnings) + return node; + + EvaluableNodeReference retval(evaluableNodeManager->AllocNode(ENT_LIST), true); + retval->ReserveOrderedChildNodes(2); + retval->AppendOrderedChildNode(node); + + EvaluableNodeReference warning_list( + evaluableNodeManager->AllocListNodeWithOrderedChildNodes(ENT_STRING, warnings.size()), true); + retval->AppendOrderedChildNode(warning_list); + + auto &list_ocn = warning_list->GetOrderedChildNodesReference(); + for(size_t i = 0; i < warnings.size(); i++) + list_ocn[i]->SetStringValue(warnings[i]); + + return retval; } EvaluableNodeReference Interpreter::InterpretNode_ENT_UNPARSE(EvaluableNode *en, bool immediate_result) @@ -529,7 +555,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL_SANDBOXED(EvaluableNo PopulatePerformanceCounters(perf_constraints_ptr, nullptr); Interpreter sandbox(evaluableNodeManager, randomStream.CreateOtherStreamViaRand(), - writeListeners, printListener, perf_constraints_ptr); + writeListeners, printListener, perf_constraints_ptr, nullptr, this); #ifdef MULTITHREAD_SUPPORT //everything at this point is referenced on stacks; allow the sandbox to trigger a garbage collect without this interpreter blocking @@ -1918,8 +1944,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_WEIGHTED_RAND(EvaluableNod //want to generate multiple values, so return a list EvaluableNodeReference retval( evaluableNodeManager->AllocListNodeWithOrderedChildNodes(ENT_STRING, number_to_generate), true); - - auto &retval_ocn = retval->GetOrderedChildNodes(); + auto &retval_ocn = retval->GetOrderedChildNodesReference(); //make a copy of all of the probabilities so they can be removed one at a time EvaluableNode::AssocType assoc(param->GetMappedChildNodesReference()); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp b/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp index 3fabe1a4..03850c04 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp @@ -931,7 +931,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_GET_LABELS(EvaluableNode * //make list of labels EvaluableNodeReference result(evaluableNodeManager->AllocListNodeWithOrderedChildNodes(ENT_STRING, num_labels), true); - auto &result_ocn = result->GetOrderedChildNodes(); + auto &result_ocn = result->GetOrderedChildNodesReference(); //because labels can be stored in different ways, it is just easiest to iterate // rather than to get a reference to each string id @@ -1173,7 +1173,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_SET_VALUE(EvaluableNode *e //get the new value auto value_node = InterpretNode(ocn[1]); source->CopyValueFrom(value_node); - source.UpdatePropertiesBasedOnAttachedNode(value_node, true); + source.UpdatePropertiesBasedOnAttachedNode(value_node); return source; } diff --git a/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp b/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp index 408a7534..6b49c866 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp @@ -102,8 +102,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CONTAINED_ENTITIES_and_COM //new list containing the contained entity ids to return EvaluableNodeReference result( evaluableNodeManager->AllocListNodeWithOrderedChildNodes(ENT_STRING, contained_entities.size()), true); - - auto &result_ocn = result->GetOrderedChildNodes(); + auto &result_ocn = result->GetOrderedChildNodesReference(); //create the string references all at once and hand off string_intern_pool.CreateStringReferences(contained_entities, [](Entity *e) { return e->GetIdStringId(); }); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp b/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp index 2dcdd2a8..29bfce58 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp @@ -600,7 +600,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_RANGE(EvaluableNode *en, b { EvaluableNodeReference range_list(evaluableNodeManager->AllocListNodeWithOrderedChildNodes(ENT_NUMBER, num_nodes), true); - auto &range_list_ocn = range_list->GetOrderedChildNodes(); + auto &range_list_ocn = range_list->GetOrderedChildNodesReference(); for(size_t i = 0; i < num_nodes; i++) range_list_ocn[i]->SetTypeViaNumberValue(i * range_step_size + range_start); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp b/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp index c48bbf94..ff14290e 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp @@ -1006,7 +1006,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_INDICES(EvaluableNode *en, //create all the string references at once for speed (especially multithreading) string_intern_pool.CreateStringReferences(container_mcn, [](auto n) { return n.first; }); - auto &index_list_ocn = index_list->GetOrderedChildNodes(); + auto &index_list_ocn = index_list->GetOrderedChildNodesReference(); size_t index = 0; for(auto &[node_id, _] : container_mcn) index_list_ocn[index++]->SetTypeViaStringIdValueWithReferenceHandoff(node_id); @@ -1016,7 +1016,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_INDICES(EvaluableNode *en, size_t num_ordered_nodes = container->GetOrderedChildNodesReference().size(); index_list.SetReference(evaluableNodeManager->AllocListNodeWithOrderedChildNodes(ENT_NUMBER, num_ordered_nodes)); - auto &index_list_ocn = index_list->GetOrderedChildNodes(); + auto &index_list_ocn = index_list->GetOrderedChildNodesReference(); for(size_t i = 0; i < num_ordered_nodes; i++) index_list_ocn[i]->SetTypeViaNumberValue(static_cast(i)); } diff --git a/src/Amalgam/out.txt b/src/Amalgam/out.txt index e89283eb..af10df0c 100644 --- a/src/Amalgam/out.txt +++ b/src/Amalgam/out.txt @@ -4,9 +4,6 @@ --label-- hello world: 12 and 2 hello world: 12 and 2 ---non-keyword token-- -6 -notakeyword --get_defaults-- { != 0.65 @@ -235,13 +232,39 @@ notakeyword (print "hello") [(null) (null) .infinity -.infinity] -{b 2 a 1 c ["alpha" "beta" "gamma"]} +{c ["alpha" "beta" "gamma"] b 2 a 1} { + c ["alpha" "beta" "gamma"] b 2 a 1 - c ["alpha" "beta" "gamma"] } +(apply "6") + +(apply "notakeyword") + +(seq + (+ 1 2) +) +[ + (seq + (+ 1 2) + (+) + ) + ["Warning: 2 missing closing parenthesis at line 1, column 17"] +] +[ + (seq + (+ 1 2) + ) + ["Warning: 1 missing closing parenthesis at line 1, column 17"] +] +[ + (seq + (+ 1 2) + ) + ["Warning: Invalid opcode \"a\"; transforming to apply opcode using the invalid opcode type at line 1, column 19"] +] --if-- if 1 if 2 @@ -633,14 +656,14 @@ a b 2 c 3 d 4 - e 5 + f 6 } -{c 3 d 4} +{a 1 b 2} { a 1 b 2 - c 3 d 4 + f 6 } { a 1 @@ -684,14 +707,14 @@ c b 2 c 3 d 4 - e 5 + f 6 } -{c 3 d 4} +{a 1 b 2} { a 1 b 2 - c 3 d 4 + f 6 } { a 1 @@ -1042,7 +1065,7 @@ abcdef [1 3] [9 5] --indices-- -["b" "4" "a" "c"] +["4" "c" "b" "a"] [ 0 1 @@ -1054,7 +1077,7 @@ abcdef 7 ] --values-- -["d" 2 1 3] +[3 "d" 2 1] [ "a" 1 @@ -1075,7 +1098,7 @@ abcdef 4 "d" ] -[1 2 3 "d"] +[1 "d" 3 2] [ 1 2 @@ -1286,7 +1309,7 @@ current_index: 2 8 ] accum_string "abcdef" - argv ["C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] + argv ["C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] bar (declare {x 6} (+ x 2) @@ -1299,10 +1322,10 @@ current_index: 2 A {B 2} B 2 } - interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" + interpreter "C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1727097549.386236 + start_time 1727135592.072069 www 1 x 12 zz 10 @@ -1329,7 +1352,7 @@ current_index: 2 8 ] accum_string "abcdef" - argv ["C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] + argv ["C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] bar (declare {x 6} (+ x 2) @@ -1342,10 +1365,10 @@ current_index: 2 A {B 2} B 2 } - interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" + interpreter "C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1727097549.386236 + start_time 1727135592.072069 www 1 x 12 zz 10 @@ -1371,7 +1394,7 @@ current_index: 2 8 ] accum_string "abcdef" - argv ["C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] + argv ["C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] bar (declare {x 6} (+ x 2) @@ -1384,10 +1407,10 @@ current_index: 2 A {B 2} B 2 } - interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" + interpreter "C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1727097549.386236 + start_time 1727135592.072069 www 1 x 12 zz 10 @@ -1527,15 +1550,15 @@ b ["b" @(get (target 2) 0) @(get (target 2) 0) "a"] -infinity test c or d: ["d" "d" "c" "d"] +infinity test c or d: ["c" "c" "d" "c"] infinity test c or d: ["c" "d" @(get (target 2) 1) @(get (target 2) 0)] -{a 34 b 43 c 23} +{a 23 b 61 c 16} {a 30 b 50 c 20} -["3" "1" "2"] +["1" "6" "7"] --get_rand_seed-- 0RÊíõÿ'`¦!šc”lÿ @@ -1631,17 +1654,17 @@ string {a 3 b 4} {c "c"} ] -21: [{"b":4,"a":3},{"d":null,"c":"c"}] +21: [{"b":4,"a":3},{"c":"c","d":null}] 22: [{"a":3,"b":4},{"c":"c","d":null}] 23: e: - a - b - - .inf +c: 3 +d: 4 b: 2 a: 1 -d: 4 -c: 3 24: a: 1 b: 2 @@ -1654,7 +1677,7 @@ e: - .inf 25: {a 1} -current date-time in epoch: 2024-09-23-09.19.09.6988380 +current date-time in epoch: 2024-09-23-19.53.12.1324550 2020-06-07 00:22:59 1391230800 1391230800 @@ -2208,16 +2231,6 @@ decrypted: hello {_ (null)} (replace _ - ["g"] - (lambda - [ - (get - (current_value 1) - 0 - ) - 4 - ] - ) [] (lambda { @@ -2228,6 +2241,16 @@ decrypted: hello ) } ) + ["g"] + (lambda + [ + (get + (current_value 1) + 0 + ) + 4 + ] + ) ) ) (declare @@ -2829,10 +2852,10 @@ MergeEntityChild1 (associate "x" 3 "y" 4) MergeEntityChild2 (associate "p" 3 "q" 4) -_1651806471 -(associate "e" 3 "f" 4) _3130331116 (associate "E" 3 "F" 4) +_1651806471 +(associate "e" 3 "f" 4) --union_entities-- (associate "b" 4 "a" 3 "c" 3) MergeEntityChild1 @@ -2850,17 +2873,6 @@ MergeEntityChild2 "w" 7 ) -_1651806471 -(associate - "e" - 3 - "f" - 4 - "g" - 5 - "h" - 6 -) _3130331116 (associate "E" @@ -2872,21 +2884,21 @@ _3130331116 "H" 6 ) -(parallel - ##p - ["_2325275497" "_2325275497" "_2973704165" "_2973704165"] -) -_2325275497 +_1651806471 (associate - "E" + "e" 3 - "F" + "f" 4 - "G" + "g" 5 - "H" + "h" 6 ) +(parallel + ##p + ["_2325275497" "_2325275497" "_2973704165" "_2973704165"] +) _2973704165 (associate "e" @@ -2898,6 +2910,17 @@ _2973704165 "h" 6 ) +_2325275497 +(associate + "E" + 3 + "F" + 4 + "G" + 5 + "H" + 6 +) --difference_entities-- (declare {_ (null) new_entity (null)} @@ -3226,14 +3249,8 @@ _3532185687 [] (lambda { - E (get - (current_value 1) - "E" - ) - F (get - (current_value 1) - "F" - ) + E 3 + F 4 G 5 H 6 } @@ -3258,16 +3275,7 @@ _3532185687 _ [] (lambda - { - e (get - (current_value 1) - "e" - ) - f (get - (current_value 1) - "f" - ) - } + {e 3 f 4} ) ) ) @@ -3297,6 +3305,10 @@ DiffEntityChild1 root: {x 3 y 4 z 5} contained_entities new_entity: ["DiffEntityChild1" "OnlyIn2" "_3626604918" "_3823131681"] difference between DiffEntity2 and new_entity: +(declare + {_ (null) new_entity (null)} + (clone_entities _ new_entity) +) (declare {_ (null) new_entity (null)} (assign @@ -3308,7 +3320,19 @@ difference between DiffEntity2 and new_entity: (lambda (declare {_ (null)} - (replace _) + (replace + _ + [] + (lambda + { + b (get + (current_value 1) + "b" + ) + c 3 + } + ) + ) ) ) { @@ -3319,7 +3343,29 @@ difference between DiffEntity2 and new_entity: ) ) (create_entities - (append new_entity "_3626604918") + (append new_entity "OnlyIn2") + (call + (lambda + (declare + {_ (null)} + (replace + _ + [] + (lambda + {o 6} + ) + ) + ) + ) + { + _ (retrieve_entity_root + (append _ "OnlyIn2") + ) + } + ) + ) + (create_entities + (append new_entity "_1985995361") (call (lambda (declare @@ -3329,16 +3375,16 @@ difference between DiffEntity2 and new_entity: [] (lambda { - E (null) - F (null) - G (get + E (get (current_value 1) - "G" + "E" ) - H (get + F (get (current_value 1) - "H" + "F" ) + G 5 + H 6 } ) ) @@ -3346,13 +3392,13 @@ difference between DiffEntity2 and new_entity: ) { _ (retrieve_entity_root - (append _ "_3626604918") + (append _ "_1985995361") ) } ) ) (create_entities - (append new_entity "_3823131681") + (append new_entity "_2783372341") (call (lambda (declare @@ -3361,14 +3407,23 @@ difference between DiffEntity2 and new_entity: _ [] (lambda - {e (null) f (null)} + { + e (get + (current_value 1) + "e" + ) + f (get + (current_value 1) + "f" + ) + } ) ) ) ) { _ (retrieve_entity_root - (append _ "_3823131681") + (append _ "_2783372341") ) } ) @@ -3377,12 +3432,14 @@ difference between DiffEntity2 and new_entity: (append _ "DiffEntityChild1") (append new_entity "DiffEntityChild1") ) - (clone_entities - (append _ "OnlyIn2") - (append new_entity "OnlyIn2") - ) new_entity ) +new_entity: DiffContainerReconstructed +new_entity root: {b 4 c 3} +DiffEntityChild1 root: +{x 3 y 4 z 6} +contained_entities new_entity: ["OnlyIn2" "_1985995361" "_2783372341" "DiffEntityChild1"] +difference between DiffContainer and DiffEntity2: (declare {_ (null) new_entity (null)} (assign @@ -3394,19 +3451,7 @@ difference between DiffEntity2 and new_entity: (lambda (declare {_ (null)} - (replace - _ - [] - (lambda - { - b (get - (current_value 1) - "b" - ) - c 3 - } - ) - ) + (replace _) ) ) { @@ -3416,28 +3461,6 @@ difference between DiffEntity2 and new_entity: ) ) ) - (create_entities - (append new_entity "OnlyIn2") - (call - (lambda - (declare - {_ (null)} - (replace - _ - [] - (lambda - {o 6} - ) - ) - ) - ) - { - _ (retrieve_entity_root - (append _ "OnlyIn2") - ) - } - ) - ) (create_entities (append new_entity "_1985995361") (call @@ -3449,10 +3472,16 @@ difference between DiffEntity2 and new_entity: [] (lambda { - E 3 - F 4 - G 5 - H 6 + E (null) + F (null) + G (get + (current_value 1) + "G" + ) + H (get + (current_value 1) + "H" + ) } ) ) @@ -3475,7 +3504,7 @@ difference between DiffEntity2 and new_entity: _ [] (lambda - {e 3 f 4} + {e (null) f (null)} ) ) ) @@ -3487,22 +3516,16 @@ difference between DiffEntity2 and new_entity: } ) ) + (clone_entities + (append _ "OnlyIn2") + (append new_entity "OnlyIn2") + ) (clone_entities (append _ "DiffEntityChild1") (append new_entity "DiffEntityChild1") ) new_entity ) -new_entity: DiffContainerReconstructed -new_entity root: {b 4 c 3} -DiffEntityChild1 root: -{x 3 y 4 z 6} -contained_entities new_entity: ["OnlyIn2" "_1985995361" "_2783372341" "DiffEntityChild1"] -difference between DiffContainer and DiffEntity2: -(declare - {_ (null) new_entity (null)} - (clone_entities _ new_entity) -) --mix_entities-- (associate "b" 4) MergeEntityChild1 @@ -3518,10 +3541,10 @@ MergeEntityChild2 "v" 6 ) -_1651806471 -(associate "e" 3 "f" 4 "h" 6) _3130331116 -(associate "E" 3 "F" 4 "G" 5) +(associate "E" 3 "F" 4 "H" 6) +_1651806471 +(associate "e" 3 "f" 4 "g" 5) --get_entity_comments-- Full test This is a suite of unit tests. @@ -3600,7 +3623,7 @@ deep sets --set_entity_root_permission-- RootTest -1727097549.948708 +1727135592.678275 (true) RootTest @@ -4944,4 +4967,4 @@ concurrent entity writes successful: (true) --clean-up test files-- --total execution time-- -1.4600541591644287 +2.634282112121582