diff --git a/src/Amalgam/Opcodes.h b/src/Amalgam/Opcodes.h index 15f9419e..67b49c03 100644 --- a/src/Amalgam/Opcodes.h +++ b/src/Amalgam/Opcodes.h @@ -298,7 +298,7 @@ constexpr size_t NUM_ENT_OPCODES = ENT_NOT_A_BUILT_IN_TYPE; constexpr size_t NUM_VALID_ENT_OPCODES = ENT_DEALLOCATED; -//Different arrangements of ordered parameters +//different arrangements of ordered parameters enum OrderedChildNodeType { OCNT_UNORDERED, @@ -309,7 +309,7 @@ enum OrderedChildNodeType OCNT_POSITION }; -//Returns the type of structure that the ordered child nodes have for a given type +//returns the type of structure that the ordered child nodes have for a given t constexpr OrderedChildNodeType GetInstructionOrderedChildNodeType(EvaluableNodeType t) { switch(t) @@ -429,67 +429,77 @@ constexpr OrderedChildNodeType GetInstructionOrderedChildNodeType(EvaluableNodeT } } -//Returns true if the instruction uses an associative array as parameters. If false, then a regular kind of list +//returns true if the instruction uses an associative array as parameters. If false, then a regular kind of list constexpr bool DoesInstructionUseAssocParameters(EvaluableNodeType t) { return GetInstructionOrderedChildNodeType(t) == OCNT_PAIRED; } -//Returns true if the type is an immediate value +//returns true if t is an immediate value constexpr bool IsEvaluableNodeTypeImmediate(EvaluableNodeType t) { return (t == ENT_NUMBER || t == ENT_STRING || t == ENT_SYMBOL); } -//Returns true if the type uses string data +//returns true if t uses string data constexpr bool DoesEvaluableNodeTypeUseStringData(EvaluableNodeType t) { return (t == ENT_STRING || t == ENT_SYMBOL); } -//Returns true if the type uses number data +//returns true if t uses number data constexpr bool DoesEvaluableNodeTypeUseNumberData(EvaluableNodeType t) { return (t == ENT_NUMBER); } -//Returns true if the type uses association data +//returns true if t uses association data constexpr bool DoesEvaluableNodeTypeUseAssocData(EvaluableNodeType t) { return (t == ENT_ASSOC); } -//Returns true if the type uses ordered data (doesn't use any other type) +//returns true if t uses ordered data (doesn't use any other t) constexpr bool DoesEvaluableNodeTypeUseOrderedData(EvaluableNodeType t) { return (!IsEvaluableNodeTypeImmediate(t) && !DoesEvaluableNodeTypeUseAssocData(t)); } -//returns true if the type is a query -constexpr bool IsEvaluableNodeTypeQuery(EvaluableNodeType type) +//returns true if t creates a scope on the stack +constexpr bool DoesEvaluableNodeTypeCreateScope(EvaluableNodeType t) { - return (type == ENT_QUERY_SELECT || type == ENT_QUERY_IN_ENTITY_LIST || type == ENT_QUERY_NOT_IN_ENTITY_LIST || type == ENT_QUERY_COUNT - || type == ENT_QUERY_SAMPLE || type == ENT_QUERY_WEIGHTED_SAMPLE || type == ENT_QUERY_EXISTS || type == ENT_QUERY_NOT_EXISTS - || type == ENT_QUERY_EQUALS || type == ENT_QUERY_NOT_EQUALS - || type == ENT_QUERY_BETWEEN || type == ENT_QUERY_NOT_BETWEEN || type == ENT_QUERY_AMONG || type == ENT_QUERY_NOT_AMONG - || type == ENT_QUERY_MAX || type == ENT_QUERY_MIN || type == ENT_QUERY_SUM || type == ENT_QUERY_MODE - || type == ENT_QUERY_QUANTILE || type == ENT_QUERY_GENERALIZED_MEAN - || type == ENT_QUERY_MIN_DIFFERENCE || type == ENT_QUERY_MAX_DIFFERENCE || type == ENT_QUERY_VALUE_MASSES - || type == ENT_QUERY_LESS_OR_EQUAL_TO || type == ENT_QUERY_GREATER_OR_EQUAL_TO - || type == ENT_QUERY_WITHIN_GENERALIZED_DISTANCE || type == ENT_QUERY_NEAREST_GENERALIZED_DISTANCE - || type == ENT_COMPUTE_ENTITY_CONVICTIONS || type == ENT_COMPUTE_ENTITY_GROUP_KL_DIVERGENCE - || type == ENT_COMPUTE_ENTITY_DISTANCE_CONTRIBUTIONS || type == ENT_COMPUTE_ENTITY_KL_DIVERGENCES + return (t == ENT_CALL || t == ENT_CALL_SANDBOXED || t == ENT_WHILE || t == ENT_LET || t == ENT_REPLACE + || t == ENT_RANGE || t == ENT_REWRITE || t == ENT_MAP || t == ENT_FILTER || t == ENT_WEAVE + || t == ENT_REDUCE || t == ENT_SORT || t == ENT_ASSOCIATE || t == ENT_ZIP || t == ENT_LIST + || t == ENT_ASSOC || t == ENT_CALL_ENTITY || t == ENT_CALL_ENTITY_GET_CHANGES || t == ENT_CALL_CONTAINER ); } -//returns true if the type could potentially be idempotent -constexpr bool IsEvaluableNodeTypePotentiallyIdempotent(EvaluableNodeType type) +//returns true if t is a query +constexpr bool IsEvaluableNodeTypeQuery(EvaluableNodeType t) { - return (type == ENT_NUMBER || type == ENT_STRING - || type == ENT_TRUE || type == ENT_FALSE - || type == ENT_NULL || type == ENT_LIST || type == ENT_ASSOC - || type == ENT_CONCLUDE || type == ENT_RETURN - || IsEvaluableNodeTypeQuery(type)); + return (t == ENT_QUERY_SELECT || t == ENT_QUERY_IN_ENTITY_LIST || t == ENT_QUERY_NOT_IN_ENTITY_LIST || t == ENT_QUERY_COUNT + || t == ENT_QUERY_SAMPLE || t == ENT_QUERY_WEIGHTED_SAMPLE || t == ENT_QUERY_EXISTS || t == ENT_QUERY_NOT_EXISTS + || t == ENT_QUERY_EQUALS || t == ENT_QUERY_NOT_EQUALS + || t == ENT_QUERY_BETWEEN || t == ENT_QUERY_NOT_BETWEEN || t == ENT_QUERY_AMONG || t == ENT_QUERY_NOT_AMONG + || t == ENT_QUERY_MAX || t == ENT_QUERY_MIN || t == ENT_QUERY_SUM || t == ENT_QUERY_MODE + || t == ENT_QUERY_QUANTILE || t == ENT_QUERY_GENERALIZED_MEAN + || t == ENT_QUERY_MIN_DIFFERENCE || t == ENT_QUERY_MAX_DIFFERENCE || t == ENT_QUERY_VALUE_MASSES + || t == ENT_QUERY_LESS_OR_EQUAL_TO || t == ENT_QUERY_GREATER_OR_EQUAL_TO + || t == ENT_QUERY_WITHIN_GENERALIZED_DISTANCE || t == ENT_QUERY_NEAREST_GENERALIZED_DISTANCE + || t == ENT_COMPUTE_ENTITY_CONVICTIONS || t == ENT_COMPUTE_ENTITY_GROUP_KL_DIVERGENCE + || t == ENT_COMPUTE_ENTITY_DISTANCE_CONTRIBUTIONS || t == ENT_COMPUTE_ENTITY_KL_DIVERGENCES + ); +} + +//returns true if t could potentially be idempotent +constexpr bool IsEvaluableNodeTypePotentiallyIdempotent(EvaluableNodeType t) +{ + return (t == ENT_NUMBER || t == ENT_STRING + || t == ENT_TRUE || t == ENT_FALSE + || t == ENT_NULL || t == ENT_LIST || t == ENT_ASSOC + || t == ENT_CONCLUDE || t == ENT_RETURN + || IsEvaluableNodeTypeQuery(t)); } constexpr bool IsEvaluableNodeTypeValid(EvaluableNodeType t) @@ -624,7 +634,7 @@ constexpr EvaluableNodeType GetEvaluableNodeTypeFromStringId(StringInternPool::S return static_cast(type_index); } -//returns a string of the enumerated type specified +//returns a string of the type specified // if get_non_keywords is true, then it will return types that are not necessarily keywords, like number inline std::string GetStringFromEvaluableNodeType(EvaluableNodeType t, bool get_non_keywords = false) { diff --git a/src/Amalgam/Parser.cpp b/src/Amalgam/Parser.cpp index 17fcda27..58c4b86b 100644 --- a/src/Amalgam/Parser.cpp +++ b/src/Amalgam/Parser.cpp @@ -115,50 +115,50 @@ std::string Parser::Unparse(EvaluableNode *tree, EvaluableNodeManager *enm, return upd.result; } -EvaluableNode *Parser::GetCodeForPathFromAToB(UnparseData &upd, EvaluableNode *a, EvaluableNode *b) +EvaluableNode *Parser::GetCodeForPathToSharedNodeFromParentAToParentB(UnparseData &upd, + EvaluableNode *shared_node, EvaluableNode *a_parent, EvaluableNode *b_parent) { - if(a == nullptr || b == nullptr) + if(shared_node == nullptr || a_parent == nullptr || b_parent == nullptr) return nullptr; - //climb back from current tree to top level parent; get ancestor (for comparison with b's) and how far back it is - EvaluableNode *a_ancestor = a; - EvaluableNode *a_ancestor_parent = upd.parentNodes[a_ancestor]; - int64_t a_ancestor_depth = 0; - EvaluableNode::ReferenceSetType nodes_visited; - while(a_ancestor_parent != nullptr - && a_ancestor != b //stop if it's the target - && nodes_visited.insert(a_ancestor_parent).second == true) //make sure not visited yet - { - //climb back up one level - a_ancestor_depth++; - a_ancestor = a_ancestor_parent; - a_ancestor_parent = upd.parentNodes[a_ancestor]; - } - - //find way from b (as previously defined) back to ancestor - EvaluableNode *b_ancestor = b; - EvaluableNode *b_ancestor_parent = upd.parentNodes[b_ancestor]; - nodes_visited.clear(); + //find all parent nodes of a to find collision with parent node of b, along with depth counts + EvaluableNode::ReferenceCountType a_parent_nodes; + size_t a_ancestor_depth = 1; + while(a_parent != nullptr && a_parent_nodes.emplace(a_parent, a_ancestor_depth++).second == true) + a_parent = upd.parentNodes[a_parent]; + + //restart at a depth of 1 in case something goes wrong + a_ancestor_depth = 1; + //keep track of nodes visited to make sure there's no cycle + EvaluableNode::ReferenceSetType b_nodes_visited; + //ids to traverse along the path std::vector b_path_nodes; - while(b_ancestor_parent != nullptr - && b_ancestor != a_ancestor //stop if it's the target - && nodes_visited.insert(b_ancestor_parent).second == true) //make sure not visited yet + //the current node from path b + EvaluableNode *b = shared_node; + while(b_nodes_visited.insert(b_parent).second == true) //make sure not visited yet { + //stop if found common parent node + if(auto a_entry = a_parent_nodes.find(b); a_entry != end(a_parent_nodes)) + { + a_ancestor_depth = a_entry->second; + break; + } - EvaluableNode *lookup = upd.enm->AllocNode(ENT_GET); - lookup->AppendOrderedChildNode(nullptr); //placeholder for the object to use when assembling chain later + //could not find a common ancestor, so error out + if(b == nullptr) + return nullptr; //each kind of child nodes - if(b_ancestor_parent->IsAssociativeArray()) + if(b_parent->IsAssociativeArray()) { StringInternPool::StringID key_id = StringInternPool::NOT_A_STRING_ID; - auto &bap_mcn = b_ancestor_parent->GetMappedChildNodesReference(); + auto &bp_mcn = b_parent->GetMappedChildNodesReference(); if(!upd.sortKeys) { //look up which key corresponds to the value - for(auto &[s_id, s] : b_ancestor_parent->GetMappedChildNodesReference()) + for(auto &[s_id, s] : bp_mcn) { - if(s == b_ancestor) + if(s == b) { key_id = s_id; break; @@ -168,16 +168,16 @@ EvaluableNode *Parser::GetCodeForPathFromAToB(UnparseData &upd, EvaluableNode *a else //sortKeys { std::vector key_sids; - key_sids.reserve(bap_mcn.size()); - for(auto &[k_id, _] : bap_mcn) + key_sids.reserve(bp_mcn.size()); + for(auto &[k_id, _] : bp_mcn) key_sids.push_back(k_id); std::sort(begin(key_sids), end(key_sids), StringIDNaturalCompareSort); for(auto &key_sid : key_sids) { - auto k = bap_mcn.find(key_sid); - if(k->second == b_ancestor) + auto k = bp_mcn.find(key_sid); + if(k->second == b) { key_id = k->first; break; @@ -185,46 +185,42 @@ EvaluableNode *Parser::GetCodeForPathFromAToB(UnparseData &upd, EvaluableNode *a } } - lookup->AppendOrderedChildNode(upd.enm->AllocNode(ENT_STRING, key_id)); + b_path_nodes.insert(begin(b_path_nodes), upd.enm->AllocNode(ENT_STRING, key_id)); } - else if(b_ancestor_parent->IsOrderedArray()) + else if(b_parent->IsOrderedArray()) { - auto &bap_ocn = b_ancestor_parent->GetOrderedChildNodesReference(); - const auto &found = std::find(begin(bap_ocn), end(bap_ocn), b_ancestor); - auto index = std::distance(begin(bap_ocn), found); - lookup->AppendOrderedChildNode(upd.enm->AllocNode(static_cast(index))); + auto &bp_ocn = b_parent->GetOrderedChildNodesReference(); + const auto &found = std::find(begin(bp_ocn), end(bp_ocn), b); + auto index = std::distance(begin(bp_ocn), found); + b_path_nodes.insert(begin(b_path_nodes), upd.enm->AllocNode(static_cast(index))); } else //didn't work... odd/error condition { - delete lookup; return nullptr; } - b_path_nodes.push_back(lookup); - b_ancestor = b_ancestor_parent; - b_ancestor_parent = upd.parentNodes[b_ancestor]; + b = b_parent; + b_parent = upd.parentNodes[b]; } - //make sure common ancestor is the same (otherwise return null) - if(a_ancestor != b_ancestor) - return nullptr; - //build code to get the reference - EvaluableNode *refpath = upd.enm->AllocNode(ENT_TARGET); - refpath->AppendOrderedChildNode(upd.enm->AllocNode(static_cast(a_ancestor_depth))); - - //combine together - while(b_path_nodes.size() > 0) - { - //pull off the end of b - EvaluableNode *next = b_path_nodes.back(); - b_path_nodes.pop_back(); + EvaluableNode *target = upd.enm->AllocNode(ENT_TARGET); + //need to include the get (below) in the depth, so add 1 + target->AppendOrderedChildNode(upd.enm->AllocNode(static_cast(a_ancestor_depth + 1))); + + EvaluableNode *indices = nullptr; + if(b_path_nodes.size() == 0) + return target; + else if(b_path_nodes.size() == 1) + indices = b_path_nodes[0]; + else + indices = upd.enm->AllocNode(b_path_nodes, false, true); - next->GetOrderedChildNodes()[0] = refpath; - refpath = next; - } + EvaluableNode *get = upd.enm->AllocNode(ENT_GET); + get->AppendOrderedChildNode(target); + get->AppendOrderedChildNode(indices); - return refpath; + return get; } void Parser::SkipWhitespaceAndAccumulateAttributes(EvaluableNode *target) @@ -445,7 +441,11 @@ EvaluableNode *Parser::GetNextToken(EvaluableNode *parent_node, EvaluableNode *n { pos++; numOpenParenthesis++; - SkipWhitespaceAndAccumulateAttributes(new_token); + + //only accumulate attributes for opcodes -- attributes for [ and { must be before the character + if(cur_char == '(') + SkipWhitespaceAndAccumulateAttributes(new_token); + if(pos >= code->size()) { FreeNode(new_token); @@ -811,14 +811,15 @@ void Parser::Unparse(UnparseData &upd, EvaluableNode *tree, EvaluableNode *paren if(!upd.cycleFree && tree != nullptr) { //keep track of what was visited - auto [_, inserted] = upd.parentNodes.insert(std::make_pair(tree, parent)); + auto [dest_node_with_parent, inserted] = upd.parentNodes.emplace(tree, parent); //if code already referenced, then print path to it if(!inserted) { upd.preevaluationNeeded = true; - EvaluableNode *code_to_print = GetCodeForPathFromAToB(upd, parent, tree); + EvaluableNode *code_to_print = GetCodeForPathToSharedNodeFromParentAToParentB(upd, + tree, parent, dest_node_with_parent->second); //unparse the path using a new set of parentNodes as to not pollute the one currently being unparsed EvaluableNode::ReferenceAssocType references; std::swap(upd.parentNodes, references); @@ -1049,6 +1050,31 @@ void Parser::Unparse(UnparseData &upd, EvaluableNode *tree, EvaluableNode *paren } } +//given a node, traverses the node via index and returns that child, nullptr if invalid +static EvaluableNode *GetNodeRelativeToIndex(EvaluableNode *node, EvaluableNode *index_node) +{ + if(node == nullptr) + return nullptr; + + //if it's an assoc, then treat the index as a string + if(node->IsAssociativeArray()) + { + StringInternPool::StringID index_sid = EvaluableNode::ToStringIDIfExists(index_node); + EvaluableNode **found = node->GetMappedChildNode(index_sid); + if(found != nullptr) + return *found; + return nullptr; + } + + //otherwise treat the index as a number for a list + size_t index = static_cast(EvaluableNode::ToNumber(index_node)); + if(index < node->GetOrderedChildNodes().size()) + return node->GetOrderedChildNodesReference()[index]; + + //didn't find anything + return nullptr; +} + EvaluableNode *Parser::GetNodeFromRelativeCodePath(EvaluableNode *path) { if(path == nullptr) @@ -1070,32 +1096,47 @@ EvaluableNode *Parser::GetNodeFromRelativeCodePath(EvaluableNode *path) if(index_node == nullptr) return nullptr; - //if it's an assoc, then treat the index as a string - if(result->GetMappedChildNodes().size() > 0) + if(index_node->IsOrderedArray()) { - StringInternPool::StringID index_sid = EvaluableNode::ToStringIDIfExists(index_node); - EvaluableNode **found = result->GetMappedChildNode(index_sid); - if(found != nullptr) - return *found; - return nullptr; + //travers the nodes over each index to find the location + auto &index_ocn = index_node->GetOrderedChildNodesReference(); + for(auto &index_node_element : index_ocn) + { + result = GetNodeRelativeToIndex(result, index_node_element); + if(result == nullptr) + break; + } + + return result; + } + else //immediate + { + return GetNodeRelativeToIndex(result, index_node); } - //otherwise treat the index as a number for a list - size_t index = static_cast(EvaluableNode::ToNumber(index_node)); - if(result->GetOrderedChildNodes().size() > index) - return result->GetOrderedChildNodes()[index]; + return nullptr; } case ENT_TARGET: { //first parameter is the number of steps to crawl up in the parent tree - size_t steps_up = 1; + size_t target_steps_up = 1; if(path->GetOrderedChildNodes().size() > 0) - steps_up = static_cast(EvaluableNode::ToNumber(path->GetOrderedChildNodes()[0])); + { + double step_value = EvaluableNode::ToNumber(path->GetOrderedChildNodes()[0]); + + //zero is not allowed here because that means it would attempt to replace itself with itself + //within the data -- in actual runtime, 0 is allowed for target because other things can point to it, + //but not during parsing + if(step_value >= 1) + target_steps_up = static_cast(step_value); + else + return nullptr; + } //crawl up parse tree EvaluableNode *result = path; - for(size_t i = 0; i < steps_up && result != nullptr; i++) + for(size_t i = 0; i < target_steps_up && result != nullptr; i++) { auto found = parentNodes.find(result); if(found != end(parentNodes)) @@ -1103,6 +1144,8 @@ EvaluableNode *Parser::GetNodeFromRelativeCodePath(EvaluableNode *path) else result = nullptr; } + + return result; } default: @@ -1120,47 +1163,39 @@ void Parser::PreevaluateNodes() continue; auto node_type = n->GetType(); - if(node_type == ENT_GET || node_type == ENT_TARGET) - { - EvaluableNode *target = GetNodeFromRelativeCodePath(n); + if(node_type != ENT_GET && node_type != ENT_TARGET) + continue; - //transform the target to a location relative to the target's parent - EvaluableNode *parent = nullptr; - if(target == nullptr) - continue; - parent = parentNodes[target]; - if(parent == nullptr) - continue; + EvaluableNode *target = GetNodeFromRelativeCodePath(n); - //copy reference of target to the parent's index of the target - if(parent->IsAssociativeArray()) + //find the node's parent in order to set it to target + EvaluableNode *parent = nullptr; + parent = parentNodes[n]; + if(parent == nullptr) + continue; + + //copy reference of target to the parent's index of the target + if(parent->IsAssociativeArray()) + { + for(auto &[_, cn] : parent->GetMappedChildNodesReference()) { - for(auto &[_, cn] : parent->GetMappedChildNodesReference()) + if(cn == n) { - if(cn == n) - { - cn = target; - break; - } + cn = target; + break; } } - else if(parent->IsOrderedArray()) + } + else if(parent->IsOrderedArray()) + { + for(auto &cn : parent->GetOrderedChildNodesReference()) { - for(auto &cn : parent->GetOrderedChildNodesReference()) + if(cn == n) { - if(cn == n) - { - cn = target; - break; - } + cn = target; + break; } } - - //mark both the originals' parents and the new parents as both cyclic - EvaluableNode::SetParentEvaluableNodesCycleChecks(parentNodes[n], parentNodes); - EvaluableNode::SetParentEvaluableNodesCycleChecks(parent, parentNodes); - - continue; } } } diff --git a/src/Amalgam/Parser.h b/src/Amalgam/Parser.h index 9cdc5432..a546ab23 100644 --- a/src/Amalgam/Parser.h +++ b/src/Amalgam/Parser.h @@ -144,7 +144,8 @@ class Parser }; //Returns code that will get from location a to b. - static EvaluableNode *GetCodeForPathFromAToB(UnparseData &upd, EvaluableNode *a, EvaluableNode *b); + static EvaluableNode *GetCodeForPathToSharedNodeFromParentAToParentB(UnparseData &upd, + EvaluableNode *shared_node, EvaluableNode *a_parent, EvaluableNode *b_parent); //Skips whitespace and accumulates any attributes (e.g., labels, comments) on to target void SkipWhitespaceAndAccumulateAttributes(EvaluableNode *target); @@ -188,7 +189,7 @@ class Parser // if need_initial_indent is true, then it will perform an indentation before generating the first code, // otherwise, will assume the indentation is already where it should be static void Unparse(UnparseData &upd, EvaluableNode *tree, EvaluableNode *parent, bool expanded_whitespace, size_t indentation_depth, bool need_initial_indent); - + //given a path starting at path's parent, parses the path and returns the target location EvaluableNode *GetNodeFromRelativeCodePath(EvaluableNode *path); diff --git a/src/Amalgam/amlg_code/full_test.amlg b/src/Amalgam/amlg_code/full_test.amlg index a860bf4c..5b9f91a3 100644 --- a/src/Amalgam/amlg_code/full_test.amlg +++ b/src/Amalgam/amlg_code/full_test.amlg @@ -3453,11 +3453,108 @@ (list 1) (lambda (get (target) 0)))) (print - (list (associate "a" 1) - @(get (target 0) 0) 1 + (list (assoc "a" 1) + @(get (target 2) 0) 1 ) ) + (print (get + (list + {"a" 3} + @(get (target 2) [0 "a"]) + ) + 1) + "\n") + + (print + (list + {"a" 3} + @(get (target 2) [0 "a"]) + ) + "\n") + + (print + (list + {"a" 3} + ;@(get (target 2) [0 "a"]) + (get (target) [0 "a"]) + ) + "\n") + + (print "entity cyclic test:\n") + (create_entities "test" + (set_labels + (list + {"a" 3} + (get (target) [0 "a"]) + ) + ["label_target"] + ) + ) + + (print (flatten_entity "test")) + + (print + (get + (retrieve_from_entity "test" "label_target") + (list 0 "a") + ) + "\n") + + (call (flatten_entity "test") {new_entity "test2"}) + + (print (flatten_entity "test2")) + + (declare (assoc + date "date" + id_list [ "id" ] + )) + + + (print "cyclic lookup test:\n") + (declare (assoc + id "id" + )) + (declare (assoc + data_assoc + { + "original" { + auto_derive_on_train { + series_id_features id + } + } + + "pointer" [[ + id + ]] + } + )) + (print data_assoc) + + (print "cyclic lookup test 2:\n") + (declare (assoc + mymap + { + ".f1_rate_1" { + auto_derive_on_train { + ordered_by_features ["date"] + } + + } + ".f3_rate_1" { + + auto_derive_on_train { + ordered_by_features [ @(get (target 5) [".f1_rate_1" "auto_derive_on_train" "ordered_by_features" 0])] + + } + + } + } + + )) + + (print mymap) + (print "--null equality tests--\n") (print "(= (null) (null)): " (= (null) (null)) "\n") diff --git a/src/Amalgam/amlg_code/test.amlg b/src/Amalgam/amlg_code/test.amlg index 053148ab..8ac9e7ac 100644 --- a/src/Amalgam/amlg_code/test.amlg +++ b/src/Amalgam/amlg_code/test.amlg @@ -1,23 +1,39 @@ (seq - (print (call_sandboxed (lambda (+ y 4)) (assoc y 3)) "\n") - - (print (call_sandboxed (lambda (+ (+ y 4) 4)) (assoc y 3) 5) "\n") - (print (call_sandboxed (lambda (+ (+ y 4) 4)) (assoc y 3) 1) "\n") - - (print (call_sandboxed (lambda (+ (+ y 4) 4)) (assoc y 3) (null) 50) "\n") - (print (call_sandboxed (lambda (+ (+ y 4) 4)) (assoc y 3) (null) 1) "\n") - - (print (call_sandboxed (lambda (+ (+ y 4) 4)) (assoc y 3) (null) (null) 3) "\n") - (print (call_sandboxed (lambda (+ (+ y 4) 4)) (assoc y 3) (null) (null) 1) "\n") - -(print (call_sandboxed - (lambda - (call_sandboxed - (+ (+ y 4) 4) - (assoc y y) - (null) (null) 50 - ) - ) - (assoc y 3) - (null) (null) 2) "\n") + + (print (get + (list + {"a" 3} + ;@(get (get (target 0) 0) "a")) + @(get (target 2) [0 "a"]) + ;(get (target 0) [0 "a"]) + ) + 1) + "\n") + + + ;(create_entities "test" + ; (lambda + ; ##label_target + ; (list + ; {"a" 1} + ; @(get (get (target 2) 0) "a")) + ; ) + ; ) + ;) + + ;(print (flatten_entity "test")) + + ;(print + ; ;(get + ; (retrieve_from_entity "test" "label_target) + ; ;(list 0 "a") + ; ;) + ;"\n") + + ;(store_entity "testsave.amlg" "test") + + ;(call (flatten_entity "test") {new_entity "test2"}) + + ;(load_entity "testsave.amlg" "test2") + ;(print (flatten_entity "test2")) ) \ No newline at end of file diff --git a/src/Amalgam/evaluablenode/EvaluableNode.h b/src/Amalgam/evaluablenode/EvaluableNode.h index 27d19fa9..bfdced6e 100644 --- a/src/Amalgam/evaluablenode/EvaluableNode.h +++ b/src/Amalgam/evaluablenode/EvaluableNode.h @@ -569,24 +569,6 @@ class EvaluableNode attributes.individualAttribs.isIdempotent = is_idempotent; } - //marks n and all its parent nodes as needing a cycle check - //nodes_to_parent_nodes is a lookup, for each node the lookup is its parent - static inline void SetParentEvaluableNodesCycleChecks(EvaluableNode *n, ReferenceAssocType &nodes_to_parent_nodes) - { - //mark until/unless have found a cycle - while(n != nullptr && !n->GetNeedCycleCheck()) - { - n->SetNeedCycleCheck(true); - - //attempt to find parent - auto found_parent = nodes_to_parent_nodes.find(n); - if(found_parent == end(nodes_to_parent_nodes)) - return; - - n = found_parent->second; - } - } - //returns whether this node has been marked as known to be currently in use constexpr bool GetKnownToBeInUse() { diff --git a/src/Amalgam/evaluablenode/EvaluableNodeManagement.h b/src/Amalgam/evaluablenode/EvaluableNodeManagement.h index 06bdd70c..2e4d63f9 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeManagement.h +++ b/src/Amalgam/evaluablenode/EvaluableNodeManagement.h @@ -375,11 +375,11 @@ class EvaluableNodeManager return n; } - inline EvaluableNode *AllocListNode(std::vector *child_nodes, + inline EvaluableNode *AllocNode(std::vector &child_nodes, bool need_cycle_check = true, bool is_idempotent = false) { EvaluableNode *n = AllocNode(ENT_LIST); - n->SetOrderedChildNodes(*child_nodes, need_cycle_check, is_idempotent); + n->SetOrderedChildNodes(child_nodes, need_cycle_check, is_idempotent); return n; } diff --git a/src/Amalgam/interpreter/Interpreter.h b/src/Amalgam/interpreter/Interpreter.h index c6f2a542..73ddea79 100644 --- a/src/Amalgam/interpreter/Interpreter.h +++ b/src/Amalgam/interpreter/Interpreter.h @@ -647,14 +647,14 @@ class Interpreter interpreter->memoryModificationLock = Concurrency::ReadLock(enm->memoryModificationMutex); //build new construction stack - EvaluableNode *construction_stack = enm->AllocListNode(parentInterpreter->constructionStackNodes); + EvaluableNode *construction_stack = enm->AllocNode(*parentInterpreter->constructionStackNodes); std::vector csiau(parentInterpreter->constructionStackIndicesAndUniqueness); interpreter->PushNewConstructionContextToStack(construction_stack->GetOrderedChildNodes(), csiau, target_origin, target, current_index, current_value, EvaluableNodeReference::Null()); auto result_ref = interpreter->ExecuteNode(node_to_execute, - enm->AllocListNode(parentInterpreter->callStackNodes), - enm->AllocListNode(parentInterpreter->interpreterNodeStackNodes), + enm->AllocNode(*parentInterpreter->callStackNodes), + enm->AllocNode(*parentInterpreter->interpreterNodeStackNodes), construction_stack, &csiau, GetCallStackMutex()); @@ -704,9 +704,9 @@ class Interpreter std::vector csiau(parentInterpreter->constructionStackIndicesAndUniqueness); auto result_ref = interpreter->ExecuteNode(node_to_execute, - enm->AllocListNode(parentInterpreter->callStackNodes), - enm->AllocListNode(parentInterpreter->interpreterNodeStackNodes), - enm->AllocListNode(parentInterpreter->constructionStackNodes), + enm->AllocNode(*parentInterpreter->callStackNodes), + enm->AllocNode(*parentInterpreter->interpreterNodeStackNodes), + enm->AllocNode(*parentInterpreter->constructionStackNodes), &csiau, GetCallStackMutex(), immediate_results); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp b/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp index c7f1770e..fdf662c1 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp @@ -1269,7 +1269,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_TARGET(EvaluableNode *en, double value = InterpretNodeIntoNumberValue(ocn[0]); if(value >= 0) depth = static_cast(value); - else + else if(!FastIsNaN(value)) //null/nan should leave depth as 0, any negative value is an error return EvaluableNodeReference::Null(); } diff --git a/src/Amalgam/out.txt b/src/Amalgam/out.txt index c5b2d046..b18ca1f8 100644 --- a/src/Amalgam/out.txt +++ b/src/Amalgam/out.txt @@ -883,7 +883,7 @@ abcdef [2 (null) 4 (null) 6 (null)] -["a" 2 @(get (target 0) 0) 4 @(get (target 0) 0) 6] +["a" 2 @(get (target 2) 0) 4 @(get (target 2) 0) 6] [ 1 @@ -1172,15 +1172,15 @@ abcdef { a 3 b @(get - (target 0) + (target 2) "a" ) c @(get - (target 0) + (target 2) "a" ) d @(get - (target 0) + (target 2) "a" ) } @@ -1200,11 +1200,11 @@ abcdef a 2 b 1 c @(get - (target 0) + (target 2) "b" ) d @(get - (target 0) + (target 2) "b" ) } @@ -1272,7 +1272,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) @@ -1285,10 +1285,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 1722180028.817478 + start_time 1722543041.25241 www 1 x 12 zz 10 @@ -1315,7 +1315,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) @@ -1328,10 +1328,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 1722180028.817478 + start_time 1722543041.25241 www 1 x 12 zz 10 @@ -1357,7 +1357,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) @@ -1370,10 +1370,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 1722180028.817478 + start_time 1722543041.25241 www 1 x 12 zz 10 @@ -1509,19 +1509,19 @@ b ["b" "a" "b" "a"] a -["a" "b" @(get (target 0) 0) @(get (target 0) 1)] +["a" "b" @(get (target 2) 0) @(get (target 2) 1)] -["b" @(get (target 0) 0) @(get (target 0) 0) @(get (target 0) 0)] +["b" @(get (target 2) 0) @(get (target 2) 0) @(get (target 2) 0)] infinity test c or d: ["d" "d" "d" "c"] -infinity test c or d: ["d" @(get (target 0) 0) "c" @(get (target 0) 2)] +infinity test c or d: ["d" @(get (target 2) 0) "c" @(get (target 2) 2)] {a 25 b 47 c 28} {a 24 b 52 c 24} -["3" "2" "5"] +["9" "2" "3"] --get_rand_seed-- N9˝UOaVT z @@ -1639,7 +1639,7 @@ e: - .inf 25: {a 1} -current date-time in epoch: 2024-07-28-11.20.29.0661580 +current date-time in epoch: 2024-08-01-16.10.41.3242310 2020-06-07 00:22:59 1391230800 1391230800 @@ -1672,7 +1672,7 @@ domingo, jun. 07, 2020 "hello world: " (* @(get - (target 2) + (target 4) "label-number-22" ) 4 @@ -1681,11 +1681,8 @@ domingo, jun. 07, 2020 (* 1 2) ) label23 @(get - (get - (target 0) - "label21" - ) - 2 + (target 2) + ["label21" 2] ) } { @@ -1694,14 +1691,11 @@ domingo, jun. 07, 2020 #labelB (true) ) labelB @(get - (get - (target 0) - "labelA" - ) - 0 + (target 2) + ["labelA" 0] ) labelQ @(get - (target 0) + (target 2) "labelA" ) } @@ -1711,14 +1705,11 @@ domingo, jun. 07, 2020 #labelB (true) ) labelB @(get - (get - (target 0) - "labelA" - ) - 0 + (target 2) + ["labelA" 0] ) labelQ @(get - (target 0) + (target 2) "labelA" ) } @@ -2779,10 +2770,10 @@ MergeEntityChild1 (associate "x" 3 "y" 4) MergeEntityChild2 (associate "p" 3 "q" 4) -_2169689611 -(associate "e" 3 "f" 4) _2280722175 (associate "E" 3 "F" 4) +_2169689611 +(associate "e" 3 "f" 4) --union_entities-- (associate "b" 4 "a" 3 "c" 3) MergeEntityChild1 @@ -2800,17 +2791,6 @@ MergeEntityChild2 "w" 7 ) -_2169689611 -(associate - "e" - 3 - "f" - 4 - "g" - 5 - "h" - 6 -) _2280722175 (associate "E" @@ -2822,11 +2802,7 @@ _2280722175 "H" 6 ) -(parallel - ##p - ["_3990396532" "_3990396532" "_3330773578" "_3330773578"] -) -_3330773578 +_2169689611 (associate "e" 3 @@ -2837,6 +2813,10 @@ _3330773578 "h" 6 ) +(parallel + ##p + ["_3990396532" "_3990396532" "_3330773578" "_3330773578"] +) _3990396532 (associate "E" @@ -2848,6 +2828,17 @@ _3990396532 "H" 6 ) +_3330773578 +(associate + "e" + 3 + "f" + 4 + "g" + 5 + "h" + 6 +) --difference_entities-- (declare {_ (null) new_entity (null)} @@ -3176,8 +3167,14 @@ _432807187 [] (lambda { - E 3 - F 4 + E (get + (current_value 1) + "E" + ) + F (get + (current_value 1) + "F" + ) G 5 H 6 } @@ -3202,7 +3199,16 @@ _432807187 _ [] (lambda - {e 3 f 4} + { + e (get + (current_value 1) + "e" + ) + f (get + (current_value 1) + "f" + ) + } ) ) ) @@ -3232,10 +3238,6 @@ DiffEntityChild1 root: {x 3 y 4 z 5} contained_entities new_entity: ["DiffEntityChild1" "OnlyIn2" "_350925375" "_2742810479"] difference between DiffEntity2 and new_entity: -(declare - {_ (null) new_entity (null)} - (clone_entities _ new_entity) -) (declare {_ (null) new_entity (null)} (assign @@ -3247,19 +3249,7 @@ difference between DiffEntity2 and new_entity: (lambda (declare {_ (null)} - (replace - _ - [] - (lambda - { - b (get - (current_value 1) - "b" - ) - c 3 - } - ) - ) + (replace _) ) ) { @@ -3270,29 +3260,7 @@ 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 "_3631850880") + (append new_entity "_350925375") (call (lambda (declare @@ -3302,16 +3270,16 @@ difference between DiffEntity2 and new_entity: [] (lambda { - E (get + E (null) + F (null) + G (get (current_value 1) - "E" + "G" ) - F (get + H (get (current_value 1) - "F" + "H" ) - G 5 - H 6 } ) ) @@ -3319,13 +3287,13 @@ difference between DiffEntity2 and new_entity: ) { _ (retrieve_entity_root - (append _ "_3631850880") + (append _ "_350925375") ) } ) ) (create_entities - (append new_entity "_1938178219") + (append new_entity "_2742810479") (call (lambda (declare @@ -3334,23 +3302,14 @@ difference between DiffEntity2 and new_entity: _ [] (lambda - { - e (get - (current_value 1) - "e" - ) - f (get - (current_value 1) - "f" - ) - } + {e (null) f (null)} ) ) ) ) { _ (retrieve_entity_root - (append _ "_1938178219") + (append _ "_2742810479") ) } ) @@ -3359,14 +3318,12 @@ 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" "_3631850880" "_1938178219" "DiffEntityChild1"] -difference between DiffContainer and DiffEntity2: (declare {_ (null) new_entity (null)} (assign @@ -3378,7 +3335,19 @@ difference between DiffContainer and DiffEntity2: (lambda (declare {_ (null)} - (replace _) + (replace + _ + [] + (lambda + { + b (get + (current_value 1) + "b" + ) + c 3 + } + ) + ) ) ) { @@ -3388,6 +3357,28 @@ difference between DiffContainer and DiffEntity2: ) ) ) + (create_entities + (append new_entity "OnlyIn2") + (call + (lambda + (declare + {_ (null)} + (replace + _ + [] + (lambda + {o 6} + ) + ) + ) + ) + { + _ (retrieve_entity_root + (append _ "OnlyIn2") + ) + } + ) + ) (create_entities (append new_entity "_3631850880") (call @@ -3399,16 +3390,10 @@ difference between DiffContainer and DiffEntity2: [] (lambda { - E (null) - F (null) - G (get - (current_value 1) - "G" - ) - H (get - (current_value 1) - "H" - ) + E 3 + F 4 + G 5 + H 6 } ) ) @@ -3431,7 +3416,7 @@ difference between DiffContainer and DiffEntity2: _ [] (lambda - {e (null) f (null)} + {e 3 f 4} ) ) ) @@ -3443,16 +3428,22 @@ difference between DiffContainer and DiffEntity2: } ) ) - (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" "_3631850880" "_1938178219" "DiffEntityChild1"] +difference between DiffContainer and DiffEntity2: +(declare + {_ (null) new_entity (null)} + (clone_entities _ new_entity) +) --mix_entities-- (associate "b" 4 "a" 3 "c" 3) MergeEntityChild1 @@ -3468,10 +3459,10 @@ MergeEntityChild2 "w" 7 ) -_2169689611 -(associate "e" 3 "f" 4 "h" 6) _2280722175 -(associate "E" 3 "F" 4 "G" 5) +(associate "E" 3 "F" 4 "H" 6) +_2169689611 +(associate "e" 3 "f" 4 "g" 5) --get_entity_comments-- Full test This is a suite of unit tests. @@ -3549,7 +3540,7 @@ deep sets --set_entity_root_permission-- RootTest -1722180029.283405 +1722543041.603805 (true) RootTest @@ -4520,7 +4511,7 @@ a [ {a 1} @(get - (target 0) + (target 2) 0 ) 1 @@ -4528,11 +4519,110 @@ a [ {a 1} @(get - (target 0) + (target 2) 0 ) 1 ] +3 +[ + {a 3} + @(get + (target 2) + [0 "a"] + ) +] + +[ + {a 3} + @(get + (target 2) + [0 "a"] + ) +] + +entity cyclic test: +(declare + {create_new_entity (true) new_entity (null)} + (let + { + _ (lambda + ##label_target + [ + {a 3} + @(get + (target 2) + [0 "a"] + ) + ] + ) + } + (if + create_new_entity + (assign + "new_entity" + (first + (create_entities new_entity _) + ) + ) + (assign_entity_roots new_entity _) + ) + ) + (set_entity_rand_seed new_entity "b\tݒ>kER") + new_entity +) +3 +(declare + {create_new_entity (true) new_entity (null)} + (let + { + _ (lambda + ##label_target + [ + {a 3} + @(get + (target 2) + [0 "a"] + ) + ] + ) + } + (if + create_new_entity + (assign + "new_entity" + (first + (create_entities new_entity _) + ) + ) + (assign_entity_roots new_entity _) + ) + ) + (set_entity_rand_seed new_entity "b\tݒ>kER") + new_entity +) +cyclic lookup test: +{ + original { + auto_derive_on_train {series_id_features "id"} + } + pointer [ + [@(get (target 4) ["original" "auto_derive_on_train" "series_id_features"])] + ] +} +cyclic lookup test 2: +{ + ".f1_rate_1" { + auto_derive_on_train { + ordered_by_features ["date"] + } + } + ".f3_rate_1" { + auto_derive_on_train { + ordered_by_features [@(get (target 5) [".f1_rate_1" "auto_derive_on_train" "ordered_by_features" 0])] + } + } +} --null equality tests-- (= (null) (null)): (true) @@ -4550,7 +4640,7 @@ a #duped ["b"] @(get - (target 0) + (target 2) 1 ) ) @@ -4792,4 +4882,4 @@ concurrent entity writes successful: (true) --clean-up test files-- --total execution time-- -1.9058191776275635 +2.8818700313568115