Add support for leaflist

Change-Id: Idcb529f85240a32e84d82934c81fcf0c3451f94e
diff --git a/src/ast_commands.cpp b/src/ast_commands.cpp
index 3af3ff0..c349e8c 100644
--- a/src/ast_commands.cpp
+++ b/src/ast_commands.cpp
@@ -12,6 +12,11 @@
     return this->m_path == b.m_path && this->m_data == b.m_data;
 }
 
+bool get_::operator==(const get_& b) const
+{
+    return this->m_path == b.m_path;
+}
+
 bool cd_::operator==(const cd_& b) const
 {
     return this->m_path == b.m_path;
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 33715a4..b9cf883 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -210,6 +210,18 @@
     }
 };
 
+struct leafListElementPath_class {
+    template <typename T, typename Iterator, typename Context>
+    void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
+    {
+        auto& parserContext = x3::get<parser_context_tag>(context);
+        if (ast.m_nodes.empty() || ast.m_nodes.back().m_suffix.type() != typeid(leafListElement_)) {
+            parserContext.m_errorMsg = "This is not a leaf list element.";
+            _pass(context) = false;
+        }
+    }
+};
+
 struct space_separator_class {
     template <typename T, typename Iterator, typename Context>
     void on_success(Iterator const&, Iterator const&, T&, Context const& context)
diff --git a/src/ast_path.cpp b/src/ast_path.cpp
index 0aa839c..b9b9b5a 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -26,6 +26,23 @@
 {
 }
 
+bool leafListElement_::operator==(const leafListElement_& b) const
+{
+    return this->m_name == b.m_name && this->m_value == b.m_value;
+}
+
+leafList_::leafList_() = default;
+
+leafList_::leafList_(const std::string& name)
+    : m_name(name)
+{
+}
+
+bool leafList_::operator==(const leafList_& b) const
+{
+    return this->m_name == b.m_name;
+}
+
 bool module_::operator==(const module_& b) const
 {
     return this->m_name == b.m_name;
@@ -142,6 +159,10 @@
         res << "]";
         return res.str();
     }
+    std::string operator()(const leafListElement_& node) const
+    {
+        return node.m_name + "[.=" + escapeListKeyString(leafDataToString(node.m_value)) + "]";
+    }
     std::string operator()(const nodeup_&) const
     {
         return "..";
@@ -202,6 +223,11 @@
         return list_{listElement.m_name};
     }
 
+    auto operator()(const leafListElement_& leafListElement) const
+    {
+        return leafList_{leafListElement.m_name};
+    }
+
     template <typename T>
     auto operator()(const T& suffix) const
     {
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index 966f771..f768461 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -46,6 +46,22 @@
     std::string m_name;
 };
 
+struct leafList_ {
+    leafList_();
+    leafList_(const std::string& name);
+
+    bool operator==(const leafList_& b) const;
+
+    std::string m_name;
+};
+
+struct leafListElement_ {
+    bool operator==(const leafListElement_& b) const;
+
+    std::string m_name;
+    leaf_data_ m_value;
+};
+
 struct listElement_ {
     listElement_() {}
     listElement_(const std::string& listName, const std::map<std::string, leaf_data_>& keys);
@@ -67,7 +83,7 @@
 
 struct schemaNode_ {
     boost::optional<module_> m_prefix;
-    boost::variant<container_, list_, nodeup_, leaf_> m_suffix;
+    boost::variant<container_, list_, nodeup_, leaf_, leafList_> m_suffix;
 
     schemaNode_();
     schemaNode_(decltype(m_suffix) node);
@@ -77,7 +93,7 @@
 
 struct dataNode_ {
     boost::optional<module_> m_prefix;
-    boost::variant<container_, listElement_, nodeup_, leaf_, list_> m_suffix;
+    boost::variant<container_, listElement_, nodeup_, leaf_, leafListElement_, leafList_, list_> m_suffix;
 
     dataNode_();
     dataNode_(decltype(m_suffix) node);
@@ -120,6 +136,7 @@
 
 BOOST_FUSION_ADAPT_STRUCT(container_, m_name)
 BOOST_FUSION_ADAPT_STRUCT(listElement_, m_name, m_keys)
+BOOST_FUSION_ADAPT_STRUCT(leafListElement_, m_name, m_value)
 BOOST_FUSION_ADAPT_STRUCT(module_, m_name)
 BOOST_FUSION_ADAPT_STRUCT(dataNode_, m_prefix, m_suffix)
 BOOST_FUSION_ADAPT_STRUCT(schemaNode_, m_prefix, m_suffix)
diff --git a/src/ast_values.cpp b/src/ast_values.cpp
index a381c1a..c3f2ce7 100644
--- a/src/ast_values.cpp
+++ b/src/ast_values.cpp
@@ -99,6 +99,8 @@
         return "(presence container)";
     case SpecialValue::List:
         return "(list)";
+    case SpecialValue::LeafList:
+        return "(leaflist)";
     }
 
     __builtin_unreachable();
diff --git a/src/ast_values.hpp b/src/ast_values.hpp
index 9ec8389..c1d8f63 100644
--- a/src/ast_values.hpp
+++ b/src/ast_values.hpp
@@ -50,6 +50,7 @@
 
 enum class SpecialValue {
     List,
+    LeafList,
     Container,
     PresenceContainer
 };
diff --git a/src/datastore_access.hpp b/src/datastore_access.hpp
index e70d9c9..6412037 100644
--- a/src/datastore_access.hpp
+++ b/src/datastore_access.hpp
@@ -48,6 +48,8 @@
     virtual void deletePresenceContainer(const std::string& path) = 0;
     virtual void createListInstance(const std::string& path) = 0;
     virtual void deleteListInstance(const std::string& path) = 0;
+    virtual void createLeafListInstance(const std::string& path) = 0;
+    virtual void deleteLeafListInstance(const std::string& path) = 0;
     virtual Tree executeRpc(const std::string& path, const Tree& input) = 0;
 
     virtual std::shared_ptr<Schema> schema() = 0;
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 1b4b49d..02d6666 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -53,10 +53,10 @@
     cd_::name >> space_separator > dataPath;
 
 auto const create_def =
-    create_::name >> space_separator > (presenceContainerPath | listInstancePath);
+    create_::name >> space_separator > (presenceContainerPath | listInstancePath | leafListElementPath);
 
 auto const delete_rule_def =
-    delete_::name >> space_separator > (presenceContainerPath | listInstancePath);
+    delete_::name >> space_separator > (presenceContainerPath | listInstancePath | leafListElementPath);
 
 auto const get_def =
     get_::name >> -(space_separator >> ((dataPathListEnd | dataPath) | (module >> "*")));
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index eb0fd66..909d863 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -6,6 +6,7 @@
  *
 */
 
+#include <boost/algorithm/string/predicate.hpp>
 #include <boost/mpl/for_each.hpp>
 #include <iostream>
 #include <sstream>
@@ -41,8 +42,19 @@
 void Interpreter::operator()(const get_& get) const
 {
     auto items = m_datastore.getItems(absolutePathFromCommand(get));
-    for (auto it : items) {
-        std::cout << it.first << " = " << leafDataToString(it.second) << std::endl;
+    for (auto it = items.begin(); it != items.end(); it++) {
+        auto [path, value] = *it;
+        if (value.type() == typeid(special_) && boost::get<special_>(value).m_value == SpecialValue::LeafList) {
+            auto leafListPrefix = path;
+            std::cout << path << " = " << leafDataToString(value) << std::endl;
+            ++it;
+            while (boost::starts_with(it->first, leafListPrefix)) {
+                std::cout << stripLeafListValueFromPath(it->first) << " = " << leafDataToString(it->second) << std::endl;
+                ++it;
+            }
+        } else {
+            std::cout << path << " = " << leafDataToString(value) << std::endl;
+        }
     }
 }
 
@@ -55,6 +67,8 @@
 {
     if (create.m_path.m_nodes.back().m_suffix.type() == typeid(listElement_))
         m_datastore.createListInstance(absolutePathFromCommand(create));
+    else if (create.m_path.m_nodes.back().m_suffix.type() == typeid(leafListElement_))
+        m_datastore.createLeafListInstance(absolutePathFromCommand(create));
     else
         m_datastore.createPresenceContainer(absolutePathFromCommand(create));
 }
@@ -63,6 +77,8 @@
 {
     if (delet.m_path.m_nodes.back().m_suffix.type() == typeid(container_))
         m_datastore.deletePresenceContainer(absolutePathFromCommand(delet));
+    else if (delet.m_path.m_nodes.back().m_suffix.type() == typeid(leafListElement_))
+        m_datastore.deleteLeafListInstance(absolutePathFromCommand(delet));
     else
         m_datastore.deleteListInstance(absolutePathFromCommand(delet));
 }
diff --git a/src/netconf_access.cpp b/src/netconf_access.cpp
index 1340332..6fd3f69 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -38,9 +38,15 @@
         if (it->schema()->nodetype() == LYS_LIST) {
             res.emplace(stripXPathPrefix(it->path()), special_{SpecialValue::List});
         }
-        if (it->schema()->nodetype() == LYS_LEAF) {
+        if (it->schema()->nodetype() == LYS_LEAF || it->schema()->nodetype() == LYS_LEAFLIST) {
+            using namespace std::string_literals;
             libyang::Data_Node_Leaf_List leaf(it);
-            res.emplace(stripXPathPrefix(it->path()), leafValueFromValue(leaf.value(), leaf.leaf_type()->base()));
+            auto value = leafValueFromValue(leaf.value(), leaf.leaf_type()->base());
+            if (it->schema()->nodetype() == LYS_LEAFLIST) {
+                std::string strippedLeafListValue = stripLeafListValueFromPath(it->path());
+                res.emplace(stripXPathPrefix(strippedLeafListValue), special_{SpecialValue::LeafList});
+            }
+            res.emplace(stripXPathPrefix(it->path()), value);
         }
     }
 }
@@ -115,6 +121,19 @@
     doEditFromDataNode(node);
 }
 
+void NetconfAccess::createLeafListInstance(const std::string& path)
+{
+    auto node = m_schema->dataNodeFromPath(path);
+    doEditFromDataNode(node);
+}
+void NetconfAccess::deleteLeafListInstance(const std::string& path)
+{
+    auto node = m_schema->dataNodeFromPath(path);
+    auto list = *(node->find_path(path.c_str())->data().begin());
+    list->insert_attr(m_schema->getYangModule("ietf-netconf"), "operation", "delete");
+    doEditFromDataNode(node);
+}
+
 void NetconfAccess::doEditFromDataNode(std::shared_ptr<libyang::Data_Node> dataNode)
 {
     auto data = dataNode->print_mem(LYD_XML, 0);
diff --git a/src/netconf_access.hpp b/src/netconf_access.hpp
index 107fcd3..964fa54 100644
--- a/src/netconf_access.hpp
+++ b/src/netconf_access.hpp
@@ -39,6 +39,8 @@
     void deletePresenceContainer(const std::string& path) override;
     void createListInstance(const std::string& path) override;
     void deleteListInstance(const std::string& path) override;
+    void createLeafListInstance(const std::string& path) override;
+    void deleteLeafListInstance(const std::string& path) override;
     void commitChanges() override;
     void discardChanges() override;
     Tree executeRpc(const std::string& path, const Tree& input) override;
diff --git a/src/path_parser.hpp b/src/path_parser.hpp
index 15655d7..ecd3bd7 100644
--- a/src/path_parser.hpp
+++ b/src/path_parser.hpp
@@ -17,6 +17,7 @@
 x3::rule<writable_leaf_path_class, dataPath_> const writableLeafPath = "writableLeafPath";
 x3::rule<presenceContainerPath_class, dataPath_> const presenceContainerPath = "presenceContainerPath";
 x3::rule<listInstancePath_class, dataPath_> const listInstancePath = "listInstancePath";
+x3::rule<leafListElementPath_class, dataPath_> const leafListElementPath = "leafListElementPath";
 x3::rule<initializePath_class, x3::unused_type> const initializePath = "initializePath";
 x3::rule<trailingSlash_class, TrailingSlash> const trailingSlash = "trailingSlash";
 x3::rule<absoluteStart_class, Scope> const absoluteStart = "absoluteStart";
@@ -26,6 +27,7 @@
 x3::rule<createKeySuggestions_class, x3::unused_type> const createKeySuggestions = "createKeySuggestions";
 x3::rule<createValueSuggestions_class, x3::unused_type> const createValueSuggestions = "createValueSuggestions";
 x3::rule<suggestKeysEnd_class, x3::unused_type> const suggestKeysEnd = "suggestKeysEnd";
+x3::rule<class leafListValue_class, leaf_data_> const leafListValue = "leafListValue";
 
 enum class NodeParserMode {
     CompleteDataNode,
@@ -56,6 +58,7 @@
     using type = dataNode_;
 };
 
+
 template <NodeParserMode PARSER_MODE>
 struct NodeParser : x3::parser<NodeParser<PARSER_MODE>> {
     using attribute_type = typename ModeToAttribute<PARSER_MODE>::type;
@@ -113,9 +116,16 @@
                     }
                     parserContext.m_suggestions.emplace(Completion{parseString, "[", Completion::WhenToAdd::IfFullMatch});
                     break;
+                case yang::NodeTypes::LeafList:
+                    if constexpr (std::is_same<attribute_type, schemaNode_>()) {
+                        out.m_suffix = leafList_{child.second};
+                    } else {
+                        out.m_suffix = leafListElement_{child.second, {}};
+                    }
+                    parserContext.m_suggestions.emplace(Completion{parseString, "[", Completion::WhenToAdd::IfFullMatch});
+                    break;
                 case yang::NodeTypes::Action:
                 case yang::NodeTypes::AnyXml:
-                case yang::NodeTypes::LeafList:
                 case yang::NodeTypes::Notification:
                 case yang::NodeTypes::Rpc:
                     continue;
@@ -172,6 +182,25 @@
                         }
                     }
                 }
+
+                if (attr.m_suffix.type() == typeid(leafListElement_)) {
+                    parserContext.m_tmpListKeyLeafPath.m_location = parserContext.currentSchemaPath();
+                    ModuleNodePair node{attr.m_prefix.flat_map([](const auto& it) {
+                                            return boost::optional<std::string>{it.m_name};
+                                        }),
+                                        boost::get<leafListElement_>(attr.m_suffix).m_name};
+                    parserContext.m_tmpListKeyLeafPath.m_node = node;
+                    res = leafListValue.parse(begin, end, ctx, rctx, boost::get<leafListElement_>(attr.m_suffix).m_value);
+
+                    if (!res) {
+                        if constexpr (PARSER_MODE == NodeParserMode::IncompleteDataNode) {
+                            res = true;
+                            attr.m_suffix = leafList_{boost::get<leafListElement_>(attr.m_suffix).m_name};
+                        } else {
+                            begin = saveIter;
+                        }
+                    }
+                }
             }
 
             if (res) {
@@ -189,7 +218,7 @@
 
 using schemaNode = NodeParser<NodeParserMode::SchemaNode>;
 using dataNode = NodeParser<NodeParserMode::CompleteDataNode>;
-using dataNodeAllowList = NodeParser<NodeParserMode::IncompleteDataNode>;
+using incompleteDataNode = NodeParser<NodeParserMode::IncompleteDataNode>;
 using pathCompletions = NodeParser<NodeParserMode::CompletionsOnly>;
 
 using AnyPath = boost::variant<schemaPath_, dataPath_>;
@@ -245,7 +274,7 @@
         if constexpr (PARSER_MODE == PathParserMode::DataPathListEnd || PARSER_MODE == PathParserMode::AnyPath) {
             if (!res || !pathEnd.parse(begin, end, ctx, rctx, x3::unused)) {
                 dataNode_ attrNodeList;
-                res = dataNodeAllowList{m_filterFunction}.parse(begin, end, ctx, rctx, attrNodeList);
+                res = incompleteDataNode{m_filterFunction}.parse(begin, end, ctx, rctx, attrNodeList);
                 if (res) {
                     attrData.m_nodes.push_back(attrNodeList);
                     // If the trailing slash matches, no more nodes are parsed.
@@ -295,6 +324,22 @@
 #pragma GCC diagnostic ignored "-Woverloaded-shift-op-parentheses"
 #endif
 
+struct SuggestLeafListEnd : x3::parser<SuggestLeafListEnd> {
+    using attribute_type = x3::unused_type;
+    template <typename It, typename Ctx, typename RCtx, typename Attr>
+    bool parse(It& begin, It, Ctx const& ctx, RCtx&, Attr&) const
+    {
+        auto& parserContext = x3::get<parser_context_tag>(ctx);
+        parserContext.m_completionIterator = begin;
+        parserContext.m_suggestions = {Completion{"]"}};
+
+        return true;
+    }
+} const suggestLeafListEnd;
+
+auto const leafListValue_def =
+    '[' >> leaf_data >> suggestLeafListEnd >> ']';
+
 auto const rest =
     x3::omit[x3::no_skip[+(x3::char_ - '/' - space_separator)]];
 
@@ -344,6 +389,9 @@
 auto const listInstancePath_def =
     dataPath;
 
+auto const leafListElementPath_def =
+    dataPath;
+
 // A "nothing" parser, which is used to indicate we tried to parse a path
 auto const initializePath_def =
     x3::eps;
@@ -360,9 +408,11 @@
 BOOST_SPIRIT_DEFINE(writableLeafPath)
 BOOST_SPIRIT_DEFINE(presenceContainerPath)
 BOOST_SPIRIT_DEFINE(listInstancePath)
+BOOST_SPIRIT_DEFINE(leafListElementPath)
 BOOST_SPIRIT_DEFINE(initializePath)
 BOOST_SPIRIT_DEFINE(createKeySuggestions)
 BOOST_SPIRIT_DEFINE(createValueSuggestions)
 BOOST_SPIRIT_DEFINE(suggestKeysEnd)
+BOOST_SPIRIT_DEFINE(leafListValue)
 BOOST_SPIRIT_DEFINE(absoluteStart)
 BOOST_SPIRIT_DEFINE(trailingSlash)
diff --git a/src/schema.cpp b/src/schema.cpp
index 2cfcbf2..e092569 100644
--- a/src/schema.cpp
+++ b/src/schema.cpp
@@ -46,3 +46,12 @@
         return false;
     }
 }
+
+bool Schema::isLeafList(const std::string& path) const
+{
+    try {
+        return nodeType(path) == yang::NodeTypes::LeafList;
+    } catch (InvalidNodeException&) {
+        return false;
+    }
+}
diff --git a/src/schema.hpp b/src/schema.hpp
index 0a9b2ff..995d921 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -58,6 +58,7 @@
     bool isLeaf(const schemaPath_& location, const ModuleNodePair& node) const;
     bool isList(const schemaPath_& location, const ModuleNodePair& node) const;
     bool isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const;
+    bool isLeafList(const std::string& path) const;
     virtual yang::NodeTypes nodeType(const std::string& path) const = 0;
     virtual yang::NodeTypes nodeType(const schemaPath_& location, const ModuleNodePair& node) const = 0;
     virtual bool isModule(const std::string& name) const = 0;
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index 738ce49..7102e9c 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -86,6 +86,13 @@
     m_nodes.emplace(key, std::unordered_map<std::string, NodeInfo>());
 }
 
+void StaticSchema::addLeafList(const std::string& location, const std::string& name, const yang::LeafDataType& type)
+{
+    m_nodes.at(location).emplace(name, NodeInfo{yang::leaflist{yang::TypeInfo{type, std::nullopt}}, yang::AccessType::Writable});
+    std::string key = joinPaths(location, name);
+    m_nodes.emplace(key, std::unordered_map<std::string, NodeInfo>());
+}
+
 void StaticSchema::addModule(const std::string& name)
 {
     m_modules.emplace(name);
@@ -120,7 +127,16 @@
 yang::TypeInfo StaticSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::string locationString = pathToSchemaString(location, Prefixes::Always);
-    return boost::get<yang::leaf>(children(locationString).at(fullNodeName(location, node)).m_nodeType).m_type;
+    auto nodeType = children(locationString).at(fullNodeName(location, node)).m_nodeType;
+    if (nodeType.type() == typeid(yang::leaf)) {
+        return boost::get<yang::leaf>(nodeType).m_type;
+    }
+
+    if (nodeType.type() == typeid(yang::leaflist)) {
+        return boost::get<yang::leaflist>(nodeType).m_type;
+    }
+
+    throw std::logic_error("StaticSchema::leafType: Path is not a leaf or a leaflist");
 }
 
 yang::TypeInfo StaticSchema::leafType(const std::string& path) const
@@ -200,7 +216,11 @@
             return yang::NodeTypes::Leaf;
         }
 
-        throw std::runtime_error{"YangSchema::nodeType: unsupported type"};
+        if (targetNode.m_nodeType.type() == typeid(yang::leaflist)) {
+            return yang::NodeTypes::LeafList;
+        }
+
+        throw std::runtime_error{"StaticSchema::nodeType: unsupported type"};
 
     } catch (std::out_of_range&) {
         throw InvalidNodeException();
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index b94dfa3..3acefc0 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -30,6 +30,10 @@
     yang::TypeInfo m_type;
 };
 
+struct leaflist {
+    yang::TypeInfo m_type;
+};
+
 struct module {
 };
 
@@ -39,7 +43,7 @@
 };
 }
 
-using NodeType = boost::variant<yang::container, yang::list, yang::leaf, yang::module>;
+using NodeType = boost::variant<yang::container, yang::list, yang::leaf, yang::leaflist, yang::module>;
 
 struct NodeInfo {
     NodeType m_nodeType;
@@ -77,6 +81,7 @@
     std::set<identityRef_> validIdentities(std::string_view module, std::string_view value);
     void addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence = yang::ContainerTraits::None);
     void addLeaf(const std::string& location, const std::string& name, const yang::LeafDataType& type, const yang::AccessType accessType = yang::AccessType::Writable);
+    void addLeafList(const std::string& location, const std::string& name, const yang::LeafDataType& type);
     void addList(const std::string& location, const std::string& name, const std::set<std::string>& keys);
     void addModule(const std::string& name);
     void addIdentity(const std::optional<identityRef_>& base, const identityRef_& name);
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index 5b01d3e..4aaa156 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -191,11 +191,17 @@
     using namespace std::string_literals;
     Tree res;
 
-    auto fillMap = [&res](auto items) {
+    auto fillMap = [this, &res](auto items) {
         if (!items)
             return;
         for (unsigned int i = 0; i < items->val_cnt(); i++) {
-            res.emplace(items->val(i)->xpath(), leafValueFromVal(items->val(i)));
+            auto value = leafValueFromVal(items->val(i));
+            if (m_schema->isLeafList(items->val(i)->xpath())) {
+                res.emplace(items->val(i)->xpath(), special_{SpecialValue::LeafList});
+                res.emplace(items->val(i)->xpath() + "[.="s + escapeListKeyString(leafDataToString(value)) + "]", value);
+            } else {
+                res.emplace(items->val(i)->xpath(), value);
+            }
         }
     };
 
@@ -242,6 +248,24 @@
     }
 }
 
+void SysrepoAccess::createLeafListInstance(const std::string& path)
+{
+    try {
+        m_session->set_item(path.c_str());
+    } catch (sysrepo::sysrepo_exception& ex) {
+        reportErrors();
+    }
+}
+
+void SysrepoAccess::deleteLeafListInstance(const std::string& path)
+{
+    try {
+        m_session->delete_item(path.c_str());
+    } catch (sysrepo::sysrepo_exception& ex) {
+        reportErrors();
+    }
+}
+
 void SysrepoAccess::createListInstance(const std::string& path)
 {
     try {
diff --git a/src/sysrepo_access.hpp b/src/sysrepo_access.hpp
index 1d72525..26d6c80 100644
--- a/src/sysrepo_access.hpp
+++ b/src/sysrepo_access.hpp
@@ -34,6 +34,8 @@
     void deletePresenceContainer(const std::string& path) override;
     void createListInstance(const std::string& path) override;
     void deleteListInstance(const std::string& path) override;
+    void createLeafListInstance(const std::string& path) override;
+    void deleteLeafListInstance(const std::string& path) override;
     Tree executeRpc(const std::string& path, const Tree& input) override;
 
     std::shared_ptr<Schema> schema() override;
diff --git a/src/utils.cpp b/src/utils.cpp
index 3543787..941fa4e 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -228,3 +228,10 @@
 {
     return boost::apply_visitor(getSchemaPathVisitor(), path);
 }
+
+std::string stripLeafListValueFromPath(const std::string& path)
+{
+    auto res = path;
+    res.erase(res.find_last_of('['));
+    return res;
+}
diff --git a/src/utils.hpp b/src/utils.hpp
index 781170d..ecbf919 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -25,3 +25,4 @@
 std::string fullNodeName(const dataPath_& location, const ModuleNodePair& pair);
 std::string leafDataToString(const leaf_data_ value);
 schemaPath_ anyPathToSchemaPath(const boost::variant<dataPath_, schemaPath_, module_>& path);
+std::string stripLeafListValueFromPath(const std::string& path);
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 132c25f..48b3724 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -201,10 +201,11 @@
 }
 }
 
+template <typename NodeType>
 yang::TypeInfo YangSchema::impl_leafType(const libyang::S_Schema_Node& node) const
 {
     using namespace std::string_literals;
-    auto leaf = std::make_shared<libyang::Schema_Node_Leaf>(node);
+    auto leaf = std::make_shared<NodeType>(node);
     auto leafUnits = leaf->units();
     std::function<yang::TypeInfo(std::shared_ptr<libyang::Type>)> resolveType;
     resolveType = [this, &resolveType, leaf, leafUnits] (std::shared_ptr<libyang::Type> type) -> yang::TypeInfo {
@@ -293,12 +294,28 @@
 
 yang::TypeInfo YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
 {
-    return impl_leafType(getSchemaNode(location, node));
+    auto lyNode = getSchemaNode(location, node);
+    switch (lyNode->nodetype()) {
+    case LYS_LEAF:
+        return impl_leafType<libyang::Schema_Node_Leaf>(lyNode);
+    case LYS_LEAFLIST:
+        return impl_leafType<libyang::Schema_Node_Leaflist>(lyNode);
+    default:
+        throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
+    }
 }
 
 yang::TypeInfo YangSchema::leafType(const std::string& path) const
 {
-    return impl_leafType(getSchemaNode(path));
+    auto lyNode = getSchemaNode(path);
+    switch (lyNode->nodetype()) {
+    case LYS_LEAF:
+        return impl_leafType<libyang::Schema_Node_Leaf>(lyNode);
+    case LYS_LEAFLIST:
+        return impl_leafType<libyang::Schema_Node_Leaflist>(lyNode);
+    default:
+        throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
+    }
 }
 
 std::optional<std::string> YangSchema::leafTypeName(const std::string& path) const
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index a4c0b90..5b2db04 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -70,6 +70,7 @@
     std::shared_ptr<libyang::Module> getYangModule(const std::string& name);
 
 private:
+    template <typename NodeType>
     yang::TypeInfo impl_leafType(const std::shared_ptr<libyang::Schema_Node>& node) const;
     std::set<std::string> modules() const;