mirror of
https://github.com/khodges42/nightShift.git
synced 2026-06-14 10:08:37 +00:00
What changed:
- Added file_writer stage support in nightshift/config.py:93, nightshift/agents.py:446, and nightshift/pipeline.py:374.
- Added file-block parsing and deterministic patch generation in nightshift/patches.py:65.
- file_writer agents now return complete file blocks:
```file:relative/path.py
<complete file content>
```
NightShift reads current files and generates the unified diff itself.
- Existing patch_validator, patch_normalizer, and patch_apply still run after that.
- Switched tiny-lisp-nightshift/nightshift.yaml:51 to type: file_writer.
- Updated tiny-lisp-nightshift/agents/implementer.md:1 to request file blocks instead of diffs.
- Updated README, tutorial, config reference, and design doc.
Also added tests for parsing file blocks, generating multi-file diffs, validating those diffs, and running a full file_writer -> normalize -> validate pipeline.
Verification: python -m unittest discover -v passes, 101 tests.
81 lines
2.3 KiB
Diff
81 lines
2.3 KiB
Diff
diff --git a/lisp.py b/lisp.py
|
|
new file mode 100644
|
|
index 0000000..f2a5c3d
|
|
--- /dev/null
|
|
+++ b/lisp.py
|
|
@@ -0,0 +1,78 @@
|
|
+import re
|
|
+
|
|
+class LispParser:
|
|
+ def __init__(self, expression):
|
|
+ self.expression = expression
|
|
+ self.tokens = self.tokenize()
|
|
+ self.index = 0
|
|
+
|
|
+ def tokenize(self):
|
|
+ token_pattern = r'\s*([()])|(\d+)|([a-zA-Z_][a-zA-Z0-9_]*)'
|
|
+ tokens = re.findall(token_pattern, self.expression)
|
|
+ return [token for group in tokens for token in group if token]
|
|
+
|
|
+ def parse(self):
|
|
+ result = []
|
|
+ while self.index < len(self.tokens):
|
|
+ token = self.tokens[self.index]
|
|
+ if token == '(':
|
|
+ self.index += 1
|
|
+ result.append(self.parse())
|
|
+ elif token == ')':
|
|
+ return result
|
|
+ else:
|
|
+ result.append(self.parse_atom(token))
|
|
+ self.index += 1
|
|
+ if len(result) == 1:
|
|
+ return result[0]
|
|
+ return result
|
|
+
|
|
+ def parse_atom(self, token):
|
|
+ try:
|
|
+ return int(token)
|
|
+ except ValueError:
|
|
+ return token
|
|
+
|
|
+def parse_lisp(expression):
|
|
+ parser = LispParser(expression)
|
|
+ return parser.parse()
|
|
+
|
|
+if __name__ == "__main__":
|
|
+ expression = "(1 2 (3 4) a b)"
|
|
+ print(parse_lisp(expression))
|
|
diff --git a/tests/test_lisp.py b/tests/test_lisp.py
|
|
new file mode 100644
|
|
index 0000000..b5c7d8e
|
|
--- /dev/null
|
|
+++ b/tests/test_lisp.py
|
|
@@ -0,0 +1,39 @@
|
|
+import unittest
|
|
+from lisp import parse_lisp
|
|
+
|
|
+class TestLispParser(unittest.TestCase):
|
|
+ def test_parse_numbers(self):
|
|
+ self.assertEqual(parse_lisp("123"), 123)
|
|
+ self.assertEqual(parse_lisp("(1 2 3)"), [1, 2, 3])
|
|
+
|
|
+ def test_parse_symbols(self):
|
|
+ self.assertEqual(parse_lisp("a"), "a")
|
|
+ self.assertEqual(parse_lisp("(a b c)"), ["a", "b", "c"])
|
|
+
|
|
+ def test_parse_nested_lists(self):
|
|
+ self.assertEqual(parse_lisp("(1 (2 3) 4)"), [1, [2, 3], 4])
|
|
+ self.assertEqual(parse_lisp("((a b) (c d))"), [["a", "b"], ["c", "d"]])
|
|
+
|
|
+ def test_unbalanced_parentheses(self):
|
|
+ with self.assertRaises(IndexError):
|
|
+ parse_lisp("(")
|
|
+ with self.assertRaises(IndexError):
|
|
+ parse_lisp(")")
|
|
+ with self.assertRaises(IndexError):
|
|
+ parse_lisp("(1 2 (3 4)")
|
|
+
|
|
+if __name__ == "__main__":
|
|
+ unittest.main()
|