docs: clarify Claro v1 status and validation
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Claro checker/linter (gentle)."""
|
||||
import sys, pathlib
|
||||
|
||||
OPEN = {"IF":"ENDIF", "DO":"DONE", "REPEAT":"UNTIL", "TEACH":"LEARNED", "TRY":"ENDTRY"}
|
||||
KNOWN = set(list(OPEN.keys()) + list(OPEN.values()) + ["ELSE","CATCH",
|
||||
"SAY","ASK","SET","CALL","RETURN","WAIT","HELP",
|
||||
"IMPORT","EXPORT",
|
||||
"ADD","GET","PUT","COUNT","HAS","KEYS",
|
||||
"READ","WRITE","APPEND","EXISTS","DELETE","FILE",
|
||||
"PARSE","MAKE","JSON",
|
||||
"WITH","TO","AS","INTO","AT","KEY","TIMES","STOP"
|
||||
])
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python tools/check.py <file.claro>")
|
||||
return 2
|
||||
p = pathlib.Path(sys.argv[1])
|
||||
lines = p.read_text(encoding="utf-8", errors="replace").splitlines()
|
||||
stack = []
|
||||
bad = 0
|
||||
for i, line in enumerate(lines, start=1):
|
||||
if "\t" in line:
|
||||
print(f"Line {i}: contains a TAB. Use spaces.")
|
||||
bad += 1
|
||||
stripped = line.strip()
|
||||
if not stripped or stripped.startswith("#"):
|
||||
continue
|
||||
head = stripped.split()[0].upper()
|
||||
if head not in KNOWN:
|
||||
print(f"Line {i}: warning: unknown keyword '{head}'.")
|
||||
if head in OPEN:
|
||||
stack.append((head, i))
|
||||
elif head in OPEN.values():
|
||||
if not stack:
|
||||
print(f"Line {i}: '{head}' without opener.")
|
||||
bad += 1
|
||||
else:
|
||||
op, oi = stack.pop()
|
||||
exp = OPEN[op]
|
||||
if head != exp:
|
||||
print(f"Line {i}: '{head}' closes '{op}' from line {oi}, expected '{exp}'.")
|
||||
bad += 1
|
||||
if stack:
|
||||
for op, oi in stack:
|
||||
print(f"Line {oi}: '{op}' not closed (expected {OPEN[op]}).")
|
||||
bad += len(stack)
|
||||
if bad:
|
||||
print(f"\nFound {bad} issue(s).")
|
||||
return 1
|
||||
print("No problems found.")
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Claro formatter (simple, deterministic)."""
|
||||
import sys, pathlib
|
||||
|
||||
OPEN = {"IF", "DO", "REPEAT", "TEACH", "TRY"}
|
||||
MID = {"ELSE", "CATCH"}
|
||||
CLOSE= {"ENDIF", "DONE", "UNTIL", "LEARNED", "ENDTRY"}
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python tools/fmt.py <file.claro>")
|
||||
return 2
|
||||
p = pathlib.Path(sys.argv[1])
|
||||
lines = p.read_text(encoding="utf-8").splitlines()
|
||||
out = []
|
||||
indent = 0
|
||||
for raw in lines:
|
||||
line = raw.rstrip()
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
out.append("")
|
||||
continue
|
||||
head = stripped.split()[0].upper()
|
||||
if head in CLOSE:
|
||||
indent = max(0, indent-1)
|
||||
if head in MID:
|
||||
indent = max(0, indent-1)
|
||||
out.append((" "*indent) + stripped)
|
||||
if head in OPEN:
|
||||
indent += 1
|
||||
if head in MID:
|
||||
indent += 1
|
||||
p.write_text("\n".join(out) + "\n", encoding="utf-8")
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compatibility wrapper around the built-in Claro RC0 test runner."""
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
CLARO = ROOT / ("claro.exe" if os.name == "nt" else "claro")
|
||||
|
||||
if not CLARO.exists():
|
||||
print("Build first: gcc -std=c99 src/claro.c -O0 -o " + ("claro.exe" if os.name == "nt" else "claro") + " -lm")
|
||||
raise SystemExit(2)
|
||||
|
||||
raise SystemExit(subprocess.call([str(CLARO), "test"], cwd=str(ROOT)))
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
EXE = ROOT / "claro.exe"
|
||||
DOC = ROOT / "docs" / "CONCURRENCY.md"
|
||||
|
||||
|
||||
def fail(message):
|
||||
raise SystemExit(message)
|
||||
|
||||
|
||||
def run(cmd):
|
||||
result = subprocess.run(cmd, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
return result.returncode, result.stdout
|
||||
|
||||
|
||||
def main():
|
||||
if not EXE.exists():
|
||||
fail(f"Missing Claro executable: {EXE}")
|
||||
|
||||
if not DOC.exists():
|
||||
fail("Missing docs/CONCURRENCY.md")
|
||||
text = DOC.read_text(encoding="utf-8")
|
||||
required = ["START TASK", "YIELD", "WAIT TASK", "TASK STATUS", "cooperative", "deterministic", "no native threads"]
|
||||
for phrase in required:
|
||||
if phrase.lower() not in text.lower():
|
||||
fail(f"CONCURRENCY.md missing required phrase: {phrase}")
|
||||
|
||||
rc, out = run([str(EXE), "check", "tests/40_tasks_status.claro"])
|
||||
if rc != 0:
|
||||
fail(out)
|
||||
|
||||
rc, out = run([str(EXE), "tests/40_tasks_status.claro"])
|
||||
if rc != 0:
|
||||
fail(out)
|
||||
if "DONE" not in out:
|
||||
fail(f"TASK STATUS should report DONE after a cooperative task runs. Output was:\n{out}")
|
||||
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
bad = Path(td) / "missing_endtask.claro"
|
||||
bad.write_text("START TASK sample\n SAY \"oops\"\n", encoding="utf-8")
|
||||
rc, out = run([str(EXE), "check", str(bad)])
|
||||
if rc == 0:
|
||||
fail("claro check should fail when START TASK is missing ENDTASK/END")
|
||||
if "START block opened here needs ENDTASK" not in out:
|
||||
fail(f"Missing ENDTASK diagnostic was not clear enough:\n{out}")
|
||||
|
||||
print("Concurrency foundation validation OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
|
||||
|
||||
|
||||
def fail(message):
|
||||
raise SystemExit(message)
|
||||
|
||||
|
||||
def main():
|
||||
if not EXE.exists():
|
||||
fail(f"Missing Claro executable: {EXE}")
|
||||
result = subprocess.run([str(EXE), "ide"], cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
fail(result.stdout)
|
||||
try:
|
||||
data = json.loads(result.stdout)
|
||||
except json.JSONDecodeError as exc:
|
||||
fail(f"claro ide did not print valid JSON: {exc}\n{result.stdout}")
|
||||
|
||||
required_top_level = ["language", "version", "file_extensions", "keywords", "types", "commands", "snippets", "diagnostics"]
|
||||
for key in required_top_level:
|
||||
if key not in data:
|
||||
fail(f"Missing top-level IDE metadata field: {key}")
|
||||
|
||||
if ".claro" not in data["file_extensions"]:
|
||||
fail("IDE metadata must advertise the .claro file extension")
|
||||
if "LIST OF TEXT" not in data["types"]:
|
||||
fail("IDE metadata must include typed container types")
|
||||
if not any(cmd.get("name") == "typecheck" for cmd in data["commands"]):
|
||||
fail("IDE metadata must include the typecheck command")
|
||||
if not any(snip.get("name") == "typed_list" for snip in data["snippets"]):
|
||||
fail("IDE metadata must include a typed_list snippet")
|
||||
if not any(diag.get("source") == "claro typecheck" for diag in data["diagnostics"]):
|
||||
fail("IDE metadata must describe claro typecheck diagnostics")
|
||||
|
||||
print("IDE metadata validation OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
HELPER = ROOT / "support" / "lsp" / "claro_lsp_stub.py"
|
||||
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
|
||||
|
||||
|
||||
def fail(message):
|
||||
raise SystemExit(message)
|
||||
|
||||
|
||||
def run_helper(*args):
|
||||
result = subprocess.run([sys.executable, str(HELPER), *args], cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
fail(result.stdout)
|
||||
return result.stdout
|
||||
|
||||
|
||||
def main():
|
||||
if not HELPER.exists():
|
||||
fail(f"Missing LSP helper: {HELPER}")
|
||||
if not EXE.exists():
|
||||
fail(f"Missing Claro executable: {EXE}")
|
||||
|
||||
metadata = json.loads(run_helper("metadata"))
|
||||
if metadata.get("language") != "Claro":
|
||||
fail("metadata must describe Claro")
|
||||
if ".claro" not in metadata.get("file_extensions", []):
|
||||
fail("metadata must include .claro")
|
||||
if not any(cmd.get("name") == "typecheck" for cmd in metadata.get("commands", [])):
|
||||
fail("metadata must include typecheck command")
|
||||
|
||||
completions = json.loads(run_helper("completions"))
|
||||
if "SAY" not in completions.get("keywords", []):
|
||||
fail("completions must include SAY")
|
||||
if "LIST OF TEXT" not in completions.get("types", []):
|
||||
fail("completions must include typed containers")
|
||||
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
bad = Path(td) / "bad_lsp.claro"
|
||||
bad.write_text("SET score AS NUMBER TO 1\nSET score TO \"oops\"\n", encoding="utf-8")
|
||||
diagnostics = json.loads(run_helper("diagnostics", str(bad)))
|
||||
if not diagnostics:
|
||||
fail("diagnostics must report type errors")
|
||||
first = diagnostics[0]
|
||||
for key in ["source", "file", "line", "severity", "message"]:
|
||||
if key not in first:
|
||||
fail(f"diagnostic is missing {key}")
|
||||
if first["source"] != "claro typecheck":
|
||||
fail("type mismatch should come from claro typecheck")
|
||||
if first["line"] != 2:
|
||||
fail(f"expected type diagnostic on line 2, got {first['line']}")
|
||||
if "NUMBER" not in first["message"] or "TEXT" not in first["message"]:
|
||||
fail(f"diagnostic should preserve type mismatch message: {first['message']}")
|
||||
|
||||
print("LSP helper validation OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
|
||||
EXPECTED_VERSION = "Claro v1.18.26"
|
||||
|
||||
|
||||
def fail(message):
|
||||
raise SystemExit(message)
|
||||
|
||||
|
||||
def run(cmd, cwd):
|
||||
result = subprocess.run(cmd, cwd=cwd, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
return result.returncode, result.stdout
|
||||
|
||||
|
||||
def read(path):
|
||||
return path.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def main():
|
||||
if not EXE.exists():
|
||||
fail(f"Missing executable: {EXE}")
|
||||
|
||||
rc, out = run([str(EXE), "--version"], ROOT)
|
||||
if rc != 0 or EXPECTED_VERSION not in out:
|
||||
fail(f"Expected {EXPECTED_VERSION}, got:\n{out}")
|
||||
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
work = Path(td)
|
||||
(work / "main.claro").write_text('SAY "Package security demo"\n', encoding="utf-8")
|
||||
|
||||
rc, out = run([str(EXE), "package", "init"], work)
|
||||
if rc != 0:
|
||||
fail(out)
|
||||
project = read(work / "claro.project")
|
||||
for phrase in ["manifest-version: 1", "name: ClaroProject", "main: main.claro", "version: v1.18.26", "packages:"]:
|
||||
if phrase not in project:
|
||||
fail(f"claro.project missing {phrase!r}:\n{project}")
|
||||
|
||||
rc, out = run([str(EXE), "package", "add", "math-tools"], work)
|
||||
if rc != 0:
|
||||
fail(out)
|
||||
manifest_path = work / "packages" / "math-tools" / "claro.package"
|
||||
manifest = read(manifest_path)
|
||||
for phrase in ["manifest-version: 1", "name: math-tools", "version: 1", "source: local", "checksum:"]:
|
||||
if phrase not in manifest:
|
||||
fail(f"claro.package missing {phrase!r}:\n{manifest}")
|
||||
checksum_line = next((line for line in manifest.splitlines() if line.startswith("checksum:")), "")
|
||||
checksum = checksum_line.split(":", 1)[1].strip()
|
||||
if len(checksum) < 8 or checksum == "TODO":
|
||||
fail(f"Package checksum should be a stable non-placeholder value, got {checksum!r}")
|
||||
|
||||
lock = read(work / "claro.lock")
|
||||
for phrase in ["lock-version: 1", "version: v1.18.26", "package: math-tools", "checksum:"]:
|
||||
if phrase not in lock:
|
||||
fail(f"claro.lock missing {phrase!r}:\n{lock}")
|
||||
|
||||
rc, out = run([str(EXE), "package", "doctor"], work)
|
||||
if rc != 0:
|
||||
fail(out)
|
||||
for phrase in ["OK package manifest: math-tools", "OK package checksum: math-tools", "Package/project files look ready."]:
|
||||
if phrase not in out:
|
||||
fail(f"package doctor output missing {phrase!r}:\n{out}")
|
||||
|
||||
rc, out = run([str(EXE), "package", "add", "../bad"], work)
|
||||
if rc == 0:
|
||||
fail("Unsafe package names with path separators must be rejected")
|
||||
if "Package names may use only" not in out:
|
||||
fail(f"Unsafe package diagnostic was unclear:\n{out}")
|
||||
|
||||
print("Package security validation OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
exe = root / ("claro.exe" if sys.platform.startswith("win") else "claro")
|
||||
|
||||
def run(args, expect=0, input_text=None):
|
||||
p = subprocess.run(args, cwd=root, input=input_text, text=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if p.returncode != expect:
|
||||
print("FAILED:", " ".join(map(str,args)))
|
||||
print(p.stdout)
|
||||
print(p.stderr)
|
||||
sys.exit(1)
|
||||
return p.stdout + p.stderr
|
||||
|
||||
run([str(exe), "--version"])
|
||||
run([str(exe), "test"])
|
||||
run([str(exe), "check", "lessons/01_hello.claro"])
|
||||
run([str(exe), "check", "lessons/08_functions.claro"])
|
||||
run([str(exe), "check", "tests/checker_fixtures/bad_unknown_command.claro"], expect=1)
|
||||
run([str(exe), "check", "tests/checker_fixtures/bad_typo_command.claro"], expect=1)
|
||||
print("RC3 validation passed")
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
EXE = os.path.join(ROOT, 'claro.exe' if os.name == 'nt' else 'claro')
|
||||
SRC = os.path.join(ROOT, 'src', 'claro.c')
|
||||
|
||||
cmd = ['gcc', '-std=c99', SRC, '-O0', '-o', EXE, '-lm']
|
||||
print('+', ' '.join(cmd))
|
||||
subprocess.check_call(cmd, cwd=ROOT)
|
||||
|
||||
checks = [
|
||||
[EXE, '--version'],
|
||||
[EXE, 'test'],
|
||||
[EXE, 'check', 'tests/25_simple_syntax.claro'],
|
||||
[EXE, 'check', 'tests/26_end_alias.claro'],
|
||||
[EXE, 'check', 'tests/28_text_list_helpers.claro'],
|
||||
[EXE, 'tests/29_arguments_demo.claro', 'apple', 'banana'],
|
||||
]
|
||||
|
||||
for c in checks:
|
||||
print('+', ' '.join(c))
|
||||
subprocess.check_call(c, cwd=ROOT)
|
||||
|
||||
print('RC6 validation completed.')
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate Claro v1.09.26 RC9 from the package root."""
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
|
||||
|
||||
|
||||
def run(cmd):
|
||||
print("+", " ".join(str(c) for c in cmd))
|
||||
return subprocess.run(cmd, cwd=ROOT).returncode
|
||||
|
||||
|
||||
def main():
|
||||
if run(["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]):
|
||||
return 1
|
||||
checks = [
|
||||
[str(EXE), "--version"],
|
||||
[str(EXE), "test"],
|
||||
[str(EXE), "doctor"],
|
||||
[str(EXE), "validate"],
|
||||
[str(EXE), "examples/simple_functions.claro"],
|
||||
[str(EXE), "examples/text_polish.claro"],
|
||||
]
|
||||
for cmd in checks:
|
||||
if run(cmd):
|
||||
return 1
|
||||
print("Claro v1.09.26 RC9 validation passed.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
|
||||
|
||||
EXPECTED = {
|
||||
"tests/typecheck_bad.claro": [
|
||||
"tests/typecheck_bad.claro:2: Type mismatch for score: it was first set as NUMBER, but this value looks like TEXT.",
|
||||
"tests/typecheck_bad.claro:3: Type mismatch for name: expected TEXT, but this value looks like NUMBER.",
|
||||
"tests/typecheck_bad.claro:4: Type check failed: expected TEXT, but score looks like NUMBER.",
|
||||
],
|
||||
"tests/typecheck_container_bad.claro": [
|
||||
"tests/typecheck_container_bad.claro:3: Type mismatch for list names: expected TEXT item, but this value looks like NUMBER.",
|
||||
"tests/typecheck_container_bad.claro:6: Type mismatch for map scores: expected NUMBER value, but this value looks like TEXT.",
|
||||
],
|
||||
}
|
||||
|
||||
STALE_PHRASES = [
|
||||
"was first set as NUMBER, but this looks like TEXT",
|
||||
"name needs TEXT, but this looks like NUMBER",
|
||||
"Type check says this should be TEXT",
|
||||
"ADD to names needs TEXT items",
|
||||
"PUT into scores needs NUMBER values",
|
||||
]
|
||||
|
||||
|
||||
def run_typecheck(path):
|
||||
result = subprocess.run(
|
||||
[str(EXE), "typecheck", path],
|
||||
cwd=ROOT,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
print("$", EXE.name, "typecheck", path)
|
||||
print(result.stdout, end="")
|
||||
if result.returncode == 0:
|
||||
raise SystemExit(f"Expected {path} to fail typecheck")
|
||||
lines = [line.strip() for line in result.stdout.splitlines() if line.strip()]
|
||||
expected = EXPECTED[path]
|
||||
if lines != expected:
|
||||
raise SystemExit(
|
||||
"Unexpected diagnostics for " + path + "\nExpected:\n"
|
||||
+ "\n".join(expected) + "\nGot:\n" + "\n".join(lines)
|
||||
)
|
||||
for stale in STALE_PHRASES:
|
||||
if stale in result.stdout:
|
||||
raise SystemExit(f"Found stale diagnostic wording: {stale}")
|
||||
return result.stdout
|
||||
|
||||
|
||||
def main():
|
||||
if not EXE.exists():
|
||||
raise SystemExit(f"Missing Claro executable: {EXE}")
|
||||
|
||||
for path in EXPECTED:
|
||||
run_typecheck(path)
|
||||
|
||||
print("Typecheck diagnostics validation complete")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate Claro v1.10.26 from the package root."""
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
|
||||
|
||||
|
||||
def run(cmd):
|
||||
print("+", " ".join(str(c) for c in cmd))
|
||||
return subprocess.run(cmd, cwd=ROOT).returncode
|
||||
|
||||
|
||||
def main():
|
||||
if run(["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]):
|
||||
return 1
|
||||
checks = [
|
||||
[str(EXE), "--version"],
|
||||
[str(EXE), "test"],
|
||||
[str(EXE), "doctor"],
|
||||
[str(EXE), "validate"],
|
||||
[str(EXE), "examples/simple_functions.claro"],
|
||||
[str(EXE), "examples/text_polish.claro"],
|
||||
[str(EXE), "examples/types_and_objects.claro"],
|
||||
[str(EXE), "examples/tasks_and_graphics.claro"],
|
||||
]
|
||||
for cmd in checks:
|
||||
if run(cmd):
|
||||
return 1
|
||||
print("Claro v1.10.26 validation passed.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate Claro v1.14.26 from the package root."""
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
EXE = os.path.join(ROOT, 'claro_validate')
|
||||
SRC = os.path.join(ROOT, 'src', 'claro.c')
|
||||
|
||||
def run(cmd, **kwargs):
|
||||
print('+', ' '.join(cmd))
|
||||
return subprocess.run(cmd, cwd=ROOT, text=True, **kwargs)
|
||||
|
||||
if run(['gcc', '-std=c99', SRC, '-O0', '-o', EXE, '-lm']).returncode:
|
||||
sys.exit(1)
|
||||
if run([EXE, '--version']).returncode:
|
||||
sys.exit(1)
|
||||
if run([EXE, 'test']).returncode:
|
||||
sys.exit(1)
|
||||
if run([EXE, 'typecheck', 'tests/typecheck_good.claro']).returncode:
|
||||
sys.exit(1)
|
||||
bad = run([EXE, 'typecheck', 'tests/typecheck_bad.claro'])
|
||||
if bad.returncode == 0:
|
||||
print('Expected typecheck_bad.claro to fail')
|
||||
sys.exit(1)
|
||||
if run([EXE, 'validate']).returncode:
|
||||
sys.exit(1)
|
||||
print('Claro v1.14.26 validation passed.')
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
import os, subprocess, sys
|
||||
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
os.chdir(ROOT)
|
||||
|
||||
exe = './claro.exe' if os.name == 'nt' else './claro'
|
||||
cmds = [
|
||||
['gcc', '-std=c99', 'src/claro.c', '-O0', '-o', exe, '-lm'],
|
||||
[exe, '--version'],
|
||||
[exe, 'test'],
|
||||
[exe, 'validate'],
|
||||
[exe, 'check', 'examples/objects_classes.claro'],
|
||||
]
|
||||
for cmd in cmds:
|
||||
print('+', ' '.join(cmd))
|
||||
rc = subprocess.call(cmd)
|
||||
if rc != 0:
|
||||
sys.exit(rc)
|
||||
print('Claro v1.15.26 validation passed.')
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
|
||||
|
||||
def run(cmd, cwd=ROOT, input_text=None):
|
||||
result = subprocess.run(cmd, cwd=cwd, input=input_text, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
print("$", " ".join(map(str, cmd)))
|
||||
print(result.stdout, end="")
|
||||
if result.returncode != 0:
|
||||
raise SystemExit(result.returncode)
|
||||
return result.stdout
|
||||
|
||||
if not EXE.exists():
|
||||
run(["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"])
|
||||
|
||||
run([str(EXE), "--version"])
|
||||
run([str(EXE), "test"])
|
||||
run([str(EXE), "validate"])
|
||||
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
work = Path(td)
|
||||
shutil.copy2(EXE, work / EXE.name)
|
||||
local = work / EXE.name
|
||||
run([str(local), "new", "DemoProject"], cwd=work)
|
||||
proj = work / "DemoProject"
|
||||
run([str(local), "package", "doctor"], cwd=proj)
|
||||
run([str(local), "package", "add", "text"], cwd=proj)
|
||||
run([str(local), "package", "add", "text"], cwd=proj)
|
||||
listing = run([str(local), "package", "list"], cwd=proj)
|
||||
if listing.count("text") != 1:
|
||||
raise SystemExit("Package was duplicated in listing")
|
||||
bad = subprocess.run([str(local), "package", "add", "../bad"], cwd=proj, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
print("$", local, "package add ../bad")
|
||||
print(bad.stdout, end="")
|
||||
if bad.returncode == 0:
|
||||
raise SystemExit("Unsafe package name was accepted")
|
||||
run([str(local), "package", "remove", "text"], cwd=proj)
|
||||
run([str(local), "run"], cwd=proj)
|
||||
|
||||
print("Validation passed. Claro v1.18.26 networking reliability is ready for use.")
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro")
|
||||
SRC = ROOT / "src" / "claro.c"
|
||||
ZIG_CC = Path.home() / "AppData" / "Local" / "hermes" / "tools" / "zig-x86_64-windows-0.14.1" / "zig.exe"
|
||||
|
||||
|
||||
def run(cmd):
|
||||
print("$", " ".join(str(c) for c in cmd))
|
||||
result = subprocess.run(cmd, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
print(result.stdout, end="")
|
||||
if result.returncode != 0:
|
||||
raise SystemExit(result.returncode)
|
||||
return result.stdout
|
||||
|
||||
|
||||
def compiler_cmd():
|
||||
env_cc = os.environ.get("CLARO_CC")
|
||||
if env_cc:
|
||||
parts = env_cc.split()
|
||||
return parts + ["-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]
|
||||
if ZIG_CC.exists():
|
||||
return [str(ZIG_CC), "cc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]
|
||||
return ["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]
|
||||
|
||||
|
||||
def build():
|
||||
if not SRC.exists():
|
||||
raise SystemExit("Missing src/claro.c")
|
||||
run(compiler_cmd())
|
||||
|
||||
|
||||
def main():
|
||||
build()
|
||||
out = run([str(EXE), "--version"])
|
||||
if "Claro v1.18.26" not in out:
|
||||
raise SystemExit("Wrong version string")
|
||||
run([str(EXE), "test"])
|
||||
run([str(EXE), "validate"])
|
||||
run([sys.executable, "tools/validate_ide_metadata.py"])
|
||||
run([sys.executable, "tools/validate_lsp_helper.py"])
|
||||
run([sys.executable, "tools/validate_concurrency.py"])
|
||||
run([sys.executable, "tools/validate_package_security.py"])
|
||||
run([sys.executable, "tools/validate_typecheck_diagnostics.py"])
|
||||
run([sys.executable, "tools/validate_version_convention.py"])
|
||||
run([str(EXE), "examples/networking.claro"])
|
||||
print("v1.18.26 validation complete")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
EXE = ROOT / "claro.exe"
|
||||
EXPECTED = "Claro v1.18.26"
|
||||
|
||||
|
||||
def fail(message):
|
||||
raise SystemExit(message)
|
||||
|
||||
|
||||
def run(cmd):
|
||||
return subprocess.run(cmd, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def main():
|
||||
if not EXE.exists():
|
||||
fail(f"Missing executable: {EXE}")
|
||||
out = run([str(EXE), "--version"]).stdout.strip()
|
||||
if out != EXPECTED:
|
||||
fail(f"Expected version {EXPECTED!r}, got {out!r}")
|
||||
ide = run([str(EXE), "ide"])
|
||||
if ide.returncode != 0:
|
||||
fail(ide.stdout)
|
||||
metadata = json.loads(ide.stdout)
|
||||
if metadata.get("version") != "v1.18.26":
|
||||
fail(f"Expected IDE metadata version 'v1.18.26', got {metadata.get('version')!r}")
|
||||
validator = ROOT / "tools" / "validate_v1_17.py"
|
||||
text = validator.read_text(encoding="utf-8")
|
||||
if "Claro v1.18.26" not in text:
|
||||
fail("tools/validate_v1_17.py must validate Claro v1.18.26")
|
||||
print("Version convention validation OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user