dts: dtlib: Refactor to get rid of _is_parsing flag
The public DT.get_node() function was used during parsing to look up
paths in references like &{/foo/bar}, along with an ugly
'DT._is_parsing' flag to adapt its behavior (to not mention aliases in
error messages).
Split out common node lookup code needed during parsing and by
get_node() instead, and stop using get_node() during parsing. This
allows '_is_parsing' to be removed and untangles things a bit.
Piggyback some other small reference-related cleanups, and fix an issue
with the filename/linenr being given twice in some error messages.
This commit also removes the index of path components from error
messages, but just the string is probably good enough.
Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
diff --git a/scripts/dts/dtlib.py b/scripts/dts/dtlib.py
index 69abdf0..d13c16b 100644
--- a/scripts/dts/dtlib.py
+++ b/scripts/dts/dtlib.py
@@ -103,8 +103,6 @@
self._lineno = 1
- self._is_parsing = True
-
self._parse_dt()
self._register_phandles()
@@ -113,8 +111,6 @@
self._remove_unreferenced()
self._register_labels()
- self._is_parsing = False
-
def get_node(self, path):
"""
Returns the Node instance for the node with path or alias 'path' (a
@@ -142,37 +138,15 @@
dt.get_node("bar-alias/baz") returns the 'baz' node.
"""
if path.startswith("/"):
- cur = self.root
- component_i = 0
- rest = path
- else:
- # Strip the first component from 'path' and store it in 'alias'.
- # Use a separate 'rest' variable rather than directly modifying
- # 'path' so that all of 'path' still shows up in error messages.
- alias, _, rest = path.partition("/")
- if alias not in self.alias2node:
- raise DTError("node path does not start with '/'"
- if self._is_parsing else
- "no alias '{}' found -- did you forget the "
- "leading '/' in the node path?".format(alias))
- cur = self.alias2node[alias]
- component_i = 1
+ return _root_and_path_to_node(self.root, path, path)
- for component in rest.split("/"):
- # Collapse multiple / in a row, and allow a / at the end
- if not component:
- continue
+ # Path does not start with '/'. First component must be an alias.
+ alias, _, rest = path.partition("/")
+ if alias not in self.alias2node:
+ raise DTError("no alias '{}' found -- did you forget the "
+ "leading '/' in the node path?".format(alias))
- component_i += 1
-
- if component not in cur.nodes:
- raise DTError("component {} ({}) in path {} does not exist"
- .format(component_i, repr(component),
- repr(path)))
-
- cur = cur.nodes[component]
-
- return cur
+ return _root_and_path_to_node(self.alias2node[alias], rest, path)
def has_node(self, path):
"""
@@ -266,17 +240,11 @@
_append_no_dup(node.labels, label)
elif tok.id is _T_DEL_NODE:
- try:
- self._del_node(self._next_ref2node())
- except DTError as e:
- self._parse_error(e)
+ self._del_node(self._next_ref2node())
self._expect_token(";")
elif tok.id is _T_OMIT_IF_NO_REF:
- try:
- self._next_ref2node()._omit_if_no_ref = True
- except DTError as e:
- self._parse_error(e)
+ self._next_ref2node()._omit_if_no_ref = True
self._expect_token(";")
elif tok.id is _T_EOF:
@@ -924,21 +892,33 @@
def _next_ref2node(self):
# Checks that the next token is a label/path reference and returns the
- # Node it points to
+ # Node it points to. Only used during parsing, so uses _parse_error()
+ # on errors to save some code in callers.
label = self._next_token()
if label.id is not _T_REF:
- self._parse_error("expected label reference (&foo) or path")
- return self._ref2node(label.val)
+ self._parse_error(
+ "expected label (&foo) or path (&{/foo/bar}) reference")
+ try:
+ return self._ref2node(label.val)
+ except DTError as e:
+ self._parse_error(e)
def _ref2node(self, s):
# Returns the Node the label/path reference 's' points to
if s[0] == "{":
+ # Path reference (&{/foo/bar})
+ path = s[1:-1]
+ if not path.startswith("/"):
+ raise DTError("node path '{}' does not start with '/'"
+ .format(path))
# Will raise DTError if the path doesn't exist
- return self.get_node(s[1:-1])
+ return _root_and_path_to_node(self.root, path, path)
- # node2label hasn't been filled in yet, and using it would get messy
+ # Label reference (&foo).
+
+ # label2node hasn't been filled in yet, and using it would get messy
# when nodes are deleted
for node in self.node_iter():
if s in node.labels:
@@ -1838,6 +1818,26 @@
.decode("utf-8", "backslashreplace")
+def _root_and_path_to_node(cur, path, fullpath):
+ # Returns the node pointed at by 'path', relative to the Node 'cur'. For
+ # example, if 'cur' has path /foo/bar, and 'path' is "baz/qaz", then the
+ # node with path /foo/bar/baz/qaz is returned. 'fullpath' is the path as
+ # given in the .dts file, for error messages.
+
+ for component in path.split("/"):
+ # Collapse multiple / in a row, and allow a / at the end
+ if not component:
+ continue
+
+ if component not in cur.nodes:
+ raise DTError("component '{}' in path '{}' does not exist"
+ .format(component, fullpath))
+
+ cur = cur.nodes[component]
+
+ return cur
+
+
_escape_table = str.maketrans({
"\\": "\\\\",
'"': '\\"',
diff --git a/scripts/dts/testdtlib.py b/scripts/dts/testdtlib.py
index 029b865..777e1fd 100755
--- a/scripts/dts/testdtlib.py
+++ b/scripts/dts/testdtlib.py
@@ -386,7 +386,7 @@
&{foo} {
};
""",
-".tmp.dts:6 (column 1): parse error: node path does not start with '/'")
+".tmp.dts:6 (column 1): parse error: node path 'foo' does not start with '/'")
verify_error("""
/dts-v1/;
@@ -397,7 +397,7 @@
&{/foo} {
};
""",
-".tmp.dts:6 (column 1): parse error: component 1 ('foo') in path '/foo' does not exist")
+".tmp.dts:6 (column 1): parse error: component 'foo' in path '/foo' does not exist")
#
# Test property labels
@@ -586,7 +586,7 @@
};
};
""",
-"/sub: component 2 ('missing') in path '/sub/missing' does not exist")
+"/sub: component 'missing' in path '/sub/missing' does not exist")
#
# Test phandles
@@ -901,6 +901,13 @@
""",
".tmp.dts:6 (column 15): parse error: undefined node label 'missing'")
+ verify_error("""
+/dts-v1/;
+
+/delete-node/ {
+""",
+".tmp.dts:3 (column 15): parse error: expected label (&foo) or path (&{/foo/bar}) reference")
+
#
# Test /include/ (which is handled in the lexer)
#
@@ -1113,6 +1120,13 @@
""",
".tmp.dts:6 (column 18): parse error: undefined node label 'missing'")
+ verify_error("""
+/dts-v1/;
+
+/omit-if-no-ref/ {
+""",
+".tmp.dts:3 (column 18): parse error: expected label (&foo) or path (&{/foo/bar}) reference")
+
#
# Test expressions
#
@@ -1321,8 +1335,8 @@
verify_path_error("", "no alias '' found -- did you forget the leading '/' in the node path?")
verify_path_error("missing", "no alias 'missing' found -- did you forget the leading '/' in the node path?")
- verify_path_error("/missing", "component 1 ('missing') in path '/missing' does not exist")
- verify_path_error("/foo/missing", "component 2 ('missing') in path '/foo/missing' does not exist")
+ verify_path_error("/missing", "component 'missing' in path '/missing' does not exist")
+ verify_path_error("/foo/missing", "component 'missing' in path '/foo/missing' does not exist")
verify_path_exists("/")
verify_path_exists("/foo")
@@ -1378,7 +1392,7 @@
verify_path_is("alias4/node5", "node5")
verify_path_error("alias4/node5/node6",
- "component 3 ('node6') in path 'alias4/node5/node6' does not exist")
+ "component 'node6' in path 'alias4/node5/node6' does not exist")
verify_error("""
/dts-v1/;