emirhanyildirim34555's picture
Publish reviewed research staging to main
2e4432d verified
Raw History Blame Contribute Delete
5.2 kB
"""Offline CLI. JSON diagnostics on stderr, successful JSON on stdout."""
import argparse,json,sys
from pathlib import Path
from . import __version__
from .api import compile_source,run_source,run_ocir,loads_ocir,dumps_ocir,SCHEMA_SHA256
from .runtime import ICLError,digest
def read(path,max_bytes=4000000):
p=Path(path)
if p.is_symlink() or any(parent.is_symlink() for parent in p.parents):raise ICLError('PATH','symlinks are not accepted')
if not p.is_file():raise ICLError('PATH','regular file required')
if p.stat().st_size>max_bytes:raise ICLError('SOURCE_LIMIT','file size exceeded')
try:return p.read_text(encoding='utf-8')
except (UnicodeError,OSError):raise ICLError('PATH','UTF-8 file unavailable')
def js(text):
try:return json.loads(text,parse_constant=lambda x:(_ for _ in ()).throw(ValueError(x)))
except (ValueError,RecursionError):raise ICLError('JSON','invalid finite JSON argument')
def output(obj):print(json.dumps(obj,ensure_ascii=False,allow_nan=False,sort_keys=True))
def main(argv=None):
parser=argparse.ArgumentParser(prog='icl',description='Invariant Cognition Language finite core, offline v0.1')
parser.add_argument('--version',action='version',version=__version__)
subs=parser.add_subparsers(dest='command',required=True)
for name in ('check','run','compile'):
p=subs.add_parser(name);p.add_argument('source');p.add_argument('--module-map',help='JSON file mapping module names to source text');p.add_argument('--entry',default='main');p.add_argument('--json',action='store_true',help='JSON is also the default output')
p.add_argument('--max-steps',type=int)
if name=='run':
p.add_argument('--args',default='[]',help='JSON argument array');p.add_argument('--engine',choices=['vm','tree'],default='vm');p.add_argument('--policy',help='Separate trusted host policy JSON file');p.add_argument('--trace',action='store_true');p.add_argument('--stop-after',type=int);p.add_argument('--checkpoint-out')
if name=='compile':p.add_argument('-o','--output',required=True)
p=subs.add_parser('inspect');p.add_argument('ocir');p.add_argument('--json',action='store_true')
p=subs.add_parser('replay');p.add_argument('ocir');p.add_argument('checkpoint');p.add_argument('--args',default='[]');p.add_argument('--policy');p.add_argument('--json',action='store_true');p.add_argument('--trace',action='store_true')
p=subs.add_parser('test');p.add_argument('--json',action='store_true')
a=parser.parse_args(argv)
try:
if a.command in ('check','run','compile'):
source=read(a.source,1000000);modules=js(read(a.module_map)) if a.module_map else None;limits={'max_steps':a.max_steps} if a.max_steps is not None else None
ocir=compile_source(source,modules,limits,a.entry)
if a.command=='check':output({'status':'PASS','schema_sha256':SCHEMA_SHA256,'program_id':ocir['program_id'],'required_effects':ocir['required_effects']});return 0
if a.command=='compile':
Path(a.output).write_text(dumps_ocir(ocir)+'\n',encoding='utf-8');output({'status':'PASS','output':a.output,'sha256':ocir['integrity_sha256']});return 0
policy=js(read(a.policy)) if a.policy else None
r=run_ocir(ocir,js(a.args),a.engine,policy=policy,stop_after=a.stop_after)
if a.checkpoint_out and r.get('checkpoint'):Path(a.checkpoint_out).write_text(json.dumps(r['checkpoint'],sort_keys=True),encoding='utf-8')
elif a.command=='inspect':
o=loads_ocir(read(a.ocir));output({'schema_version':o['schema_version'],'schema_sha256':o['schema_sha256'],'program_id':o['program_id'],'entrypoint':o['entrypoint'],'functions':[f['name'] for f in o['typed_blocks']],'required_effects':o['required_effects'],'limits':o['limits'],'integrity':'PASS'});return 0
elif a.command=='replay':
o=loads_ocir(read(a.ocir));r=run_ocir(o,js(a.args),policy=js(read(a.policy)) if a.policy else None,checkpoint=js(read(a.checkpoint)))
else:
fixtures=[('arithmetic','module t; fn main() -> Int64 { return 7 * 6; }',42),('branch','module t; fn main() -> Int64 { if true { return 1; } else { return 0; } }',1),('function','module t; fn f(x:Int64)->Int64{return x*x;} fn main()->Int64{return f(9);}',81)]
results=[]
for name,s,v in fixtures:
vals=[run_source(s,engine=e)['value'] for e in ('tree','vm')];results.append({'name':name,'passed':vals==[v,v]})
passed=all(x['passed'] for x in results);output({'status':'PASS' if passed else 'FAIL','tests':results});return 0 if passed else 1
if not a.trace and 'trace' in r:r['trace_sha256']=digest(r['trace']);r['trace_events']=len(r.pop('trace'))
output({'status':'PAUSED' if r.get('checkpoint') else 'PASS',**r});return 0
except ICLError as e:
print(json.dumps({'status':'ERROR','diagnostic':e.as_dict()},ensure_ascii=False),file=sys.stderr);return 2
except (OSError,UnicodeError) as e:
print(json.dumps({'status':'ERROR','diagnostic':{'code':'IO','message':str(e)}}),file=sys.stderr);return 3
if __name__=='__main__':raise SystemExit(main())