diff --git a/examples/specs/imp-sum-spec.k b/examples/specs/imp-sum-spec.k
index 389005b..4c44ac2 100644
--- a/examples/specs/imp-sum-spec.k
+++ b/examples/specs/imp-sum-spec.k
@@ -42,4 +42,4 @@ module IMP-SUM-SPEC
requires N >=Int 0
andBool M >=Int 0
-endmodule
\ No newline at end of file
+endmodule
diff --git a/kimp/src/kimp/__main__.py b/kimp/src/kimp/__main__.py
index c43b598..9b83b36 100644
--- a/kimp/src/kimp/__main__.py
+++ b/kimp/src/kimp/__main__.py
@@ -4,14 +4,11 @@
import os
from argparse import ArgumentParser
from pathlib import Path
-from tempfile import NamedTemporaryFile
from typing import TYPE_CHECKING, Any, Final
from pyk.cli.utils import dir_path, file_path
-from pyk.ktool.kprint import KAstOutput
-from pyk.ktool.krun import KRunOutput
-from .kimp import KIMP
+from .kimp import KImp
if TYPE_CHECKING:
from argparse import Namespace
@@ -66,37 +63,19 @@ def main() -> None:
def exec_run(
- input_file: str,
- definition_dir: str,
- input_term: str | None = None,
- output: str = 'none',
- ignore_return_code: bool = False,
+ input_file: Path,
+ env_list: list[list[tuple[str, int]]] | None,
+ definition_dir: Path | None,
depth: int | None = None,
**kwargs: Any,
) -> None:
- krun_output = KRunOutput[output.upper()]
-
- definition_dir_path = find_target('llvm') if definition_dir is None else Path(definition_dir)
- kimp = KIMP(definition_dir_path, definition_dir_path)
-
- try:
- with NamedTemporaryFile(mode='w') as f:
- temp_file = Path(f.name)
- if input_term is not None:
- temp_file.write_text(input_term)
- else:
- temp_file.write_text(Path(input_file).read_text())
- proc_res = kimp.run_program(temp_file, output=krun_output, depth=depth)
- if output != KAstOutput.NONE:
- print(proc_res.stdout)
- except RuntimeError as err:
- if ignore_return_code:
- msg, stdout, stderr = err.args
- print(stdout)
- print(stderr)
- print(msg)
- else:
- raise
+ definition_dir = find_target('llvm') if definition_dir is None else definition_dir
+ kimp = KImp(definition_dir, definition_dir)
+ pgm = input_file.read_text()
+ env = {var: val for assign in env_list for var, val in assign} if env_list else {}
+ pattern = kimp.pattern(pgm=pgm, env=env)
+ output = kimp.run(pattern, depth=depth)
+ print(kimp.pretty(output, color=True))
def exec_prove(
@@ -114,7 +93,7 @@ def exec_prove(
definition_dir = str(find_target('haskell'))
k_src_dir = str(find_target('source') / 'imp-semantics')
- kimp = KIMP(definition_dir, definition_dir)
+ kimp = KImp(definition_dir, definition_dir)
try:
kimp.prove(
@@ -128,7 +107,6 @@ def exec_prove(
)
except ValueError as err:
_LOGGER.critical(err.args)
- # exit(1)
raise
except RuntimeError as err:
if ignore_return_code:
@@ -147,7 +125,7 @@ def exec_show(
**kwargs: Any,
) -> None:
definition_dir = str(find_target('haskell'))
- kimp = KIMP(definition_dir, definition_dir)
+ kimp = KImp(definition_dir, definition_dir)
kimp.show_kcfg(spec_module, claim_id)
@@ -158,7 +136,7 @@ def exec_view(
**kwargs: Any,
) -> None:
definition_dir = str(find_target('haskell'))
- kimp = KIMP(definition_dir, definition_dir)
+ kimp = KImp(definition_dir, definition_dir)
kimp.view_kcfg(spec_module, claim_id)
@@ -170,7 +148,6 @@ def create_argument_parser() -> ArgumentParser:
shared_args.add_argument(
'--definition',
dest='definition_dir',
- nargs='?',
type=dir_path,
help='Path to compiled K definition to use.',
)
@@ -198,61 +175,41 @@ def create_argument_parser() -> ArgumentParser:
explore_args = ArgumentParser(add_help=False)
explore_args.add_argument(
'--reinit',
- dest='reinit',
default=False,
action='store_true',
help='Reinitialize proof even if it already exists.',
)
explore_args.add_argument(
'--max-depth',
- dest='max_depth',
default=100,
type=int,
help='Max depth of execution',
)
explore_args.add_argument(
'--max-iterations',
- dest='max_iterations',
default=1000,
type=int,
help='Store every Nth state in the CFG for inspection.',
)
- parser = ArgumentParser(prog='kimp', description='KIMP command line tool')
+ parser = ArgumentParser(prog='kimp', description='KImp command line tool')
command_parser = parser.add_subparsers(dest='command', required=True, help='Command to execute')
# Run
+ def env(s: str) -> list[tuple[str, int]]:
+ return [(var.strip(), int(val)) for var, val in (assign.split('=') for assign in s.split(','))]
+
run_subparser = command_parser.add_parser('run', help='Run an IMP program', parents=[shared_args])
- input_group = run_subparser.add_mutually_exclusive_group(required=True)
- input_group.add_argument(
- '--input-file',
- type=file_path,
- help='Path to .imp file',
- )
- input_group.add_argument(
- '--input-term',
- dest='input_term',
- type=str,
- help='Program to run, as a literal string',
- )
+ run_subparser.add_argument('input_file', metavar='INPUT_FILE', type=file_path, help='Path to .imp file')
run_subparser.add_argument(
- '--output',
- dest='output',
- type=str,
- default='pretty',
- help='Output mode',
- choices=['pretty', 'program', 'json', 'kore', 'kast', 'none'],
- required=False,
- )
- run_subparser.add_argument(
- '--ignore-return-code',
- action='store_true',
- default=False,
- help='Ignore return code of krun, alwasys return 0 (use for debugging only)',
+ '--env',
+ dest='env_list',
+ action='append',
+ type=env,
+ help='Assigments of initial values in form x=0,y=1,...',
)
run_subparser.add_argument(
'--depth',
- dest='depth',
type=int,
help='Execute at most DEPTH rewrite steps',
)
diff --git a/kimp/src/kimp/kdist/imp-semantics/statements.k b/kimp/src/kimp/kdist/imp-semantics/statements.k
index f498362..529c94b 100644
--- a/kimp/src/kimp/kdist/imp-semantics/statements.k
+++ b/kimp/src/kimp/kdist/imp-semantics/statements.k
@@ -19,17 +19,18 @@ endmodule
module STATEMENTS-RULES
imports STATEMENTS-SYNTAX
- rule if ( true ) E1 else _ => E1
- rule if ( false ) _ else E2 => E2
+ rule [if-true]: if ( true ) E1 else _ => E1
+ rule [if-false]: if ( false ) _ else E2 => E2
- rule if ( C ) E => if ( C ) E else {}
+ rule [if-else]: if ( C ) E => if ( C ) E else {}
rule [while]:
while ( C ) E
- => if ( C ) {
- E
- while ( C ) E
- }
+ =>
+ if ( C ) {
+ E
+ while ( C ) E
+ }
rule [block]: { E } => E ~> {}
@@ -40,22 +41,21 @@ module STATEMENTS-RULES
endmodule
module STATEMENTS
-
imports EXPRESSIONS
imports VARIABLES-SYNTAX
imports STATEMENTS-RULES
configuration
- $PGM:Stmt // changed!
- .Map
+ $PGM:Stmt // changed!
+ $ENV:Map
// changed configuration, need to repeat the variable rule
- rule
+ rule [var]:
X:Id => V ...
X |-> V ...
// assignment rule accesses configuration
- rule
+ rule [assign]:
X = V:Value ; => .K ...
E => E [ X <- V ]
endmodule
diff --git a/kimp/src/kimp/kimp.py b/kimp/src/kimp/kimp.py
index 4109305..460643d 100644
--- a/kimp/src/kimp/kimp.py
+++ b/kimp/src/kimp/kimp.py
@@ -2,17 +2,16 @@
from pyk.kcfg.show import NodePrinter
-__all__ = ['KIMP']
+__all__ = ['KImp']
import logging
from contextlib import contextmanager
from dataclasses import dataclass
from functools import cached_property
from pathlib import Path
-from tempfile import NamedTemporaryFile
from typing import TYPE_CHECKING, final
-from pyk.cli.utils import check_dir_path, check_file_path
+from pyk.cli.utils import check_dir_path
from pyk.cterm.symbolic import CTermSymbolic
from pyk.kast.formatter import Formatter
from pyk.kast.inner import KApply, KLabel, KSequence, KVariable
@@ -23,21 +22,20 @@
from pyk.kore.rpc import KoreClient, kore_server
from pyk.ktool.claim_loader import ClaimLoader
from pyk.ktool.kprove import KProve
-from pyk.ktool.krun import KRun, KRunOutput, _krun
from pyk.proof.reachability import APRProof, APRProver
from pyk.proof.show import APRProofShow
from pyk.proof.tui import APRProofViewer
from pyk.utils import single
if TYPE_CHECKING:
- from collections.abc import Iterable, Iterator
- from subprocess import CompletedProcess
+ from collections.abc import Iterable, Iterator, Mapping
from typing import Final
from pyk.cterm.cterm import CTerm
from pyk.kast.outer import KDefinition
from pyk.kcfg.kcfg import KCFG, KCFGExtendResult
from pyk.kore.rpc import FallbackReason
+ from pyk.kore.syntax import Pattern
from pyk.ktool.kprint import KPrint
from pyk.utils import BugReport
@@ -87,7 +85,7 @@ def is_mergeable(self, c1: CTerm, c2: CTerm) -> bool:
@final
@dataclass(frozen=True)
-class KIMP:
+class KImp:
llvm_dir: Path
haskell_dir: Path
proof_dir: Path
@@ -106,10 +104,6 @@ def __init__(self, llvm_dir: str | Path, haskell_dir: str | Path):
object.__setattr__(self, 'haskell_dir', haskell_dir)
object.__setattr__(self, 'proof_dir', proof_dir)
- @cached_property
- def parser(self) -> Path:
- return self.llvm_dir / 'parser_PGM'
-
@cached_property
def definition(self) -> KDefinition:
return read_kast_definition(self.llvm_dir / 'compiled.json')
@@ -122,45 +116,51 @@ def format(self) -> Formatter:
def kprove(self) -> KProve:
return KProve(definition_dir=self.haskell_dir, use_directory=self.proof_dir)
- @cached_property
- def krun(self) -> KRun:
- krun = KRun(definition_dir=self.llvm_dir)
- return krun
-
- def run_program(
+ def run(
self,
- program_file: str | Path,
+ pattern: Pattern,
*,
- output: KRunOutput = KRunOutput.NONE,
- check: bool = True,
- temp_file: str | Path | None = None,
- depth: int | None,
- ) -> CompletedProcess:
- def run(program_file: Path) -> CompletedProcess:
- return _krun(
- input_file=program_file,
- definition_dir=self.llvm_dir,
- output=output,
- check=check,
- depth=depth,
- pipe_stderr=True,
- pmap={'PGM': str(self.parser)},
+ depth: int | None = None,
+ ) -> Pattern:
+ from pyk.ktool.krun import llvm_interpret
+
+ return llvm_interpret(definition_dir=self.llvm_dir, pattern=pattern, depth=depth)
+
+ def pattern(self, *, pgm: str, env: Mapping[str, int]) -> Pattern:
+ from pyk.kore.prelude import ID, INT, SORT_K_ITEM, inj, map_pattern, top_cell_initializer
+ from pyk.kore.syntax import DV, SortApp, String
+
+ pgm_pattern = self.parse(pgm)
+ env_pattern = map_pattern(
+ *(
+ (
+ inj(ID, SORT_K_ITEM, DV(ID, String(var))),
+ inj(INT, SORT_K_ITEM, DV(INT, String(str(val)))),
+ )
+ for var, val in env.items()
)
+ )
+ return top_cell_initializer(
+ {
+ '$PGM': inj(SortApp('SortStmt'), SORT_K_ITEM, pgm_pattern),
+ '$ENV': inj(SortApp('SortMap'), SORT_K_ITEM, env_pattern),
+ }
+ )
+
+ def parse(self, pgm: str) -> Pattern:
+ from pyk.kore.parser import KoreParser
+ from pyk.utils import run_process_2
- def preprocess_and_run(program_file: Path, temp_file: Path) -> CompletedProcess:
- temp_file.write_text(program_file.read_text())
- return run(temp_file)
+ parser = self.llvm_dir / 'parser_PGM'
+ args = [str(parser), '/dev/stdin']
- program_file = Path(program_file)
- check_file_path(program_file)
+ kore_text = run_process_2(args, input=pgm).stdout
+ return KoreParser(kore_text).pattern()
- if temp_file is None:
- with NamedTemporaryFile(mode='w') as f:
- temp_file = Path(f.name)
- return preprocess_and_run(program_file, temp_file)
+ def pretty(self, pattern: Pattern, color: bool | None = None) -> str:
+ from pyk.kore.tools import kore_print
- temp_file = Path(temp_file)
- return preprocess_and_run(program_file, temp_file)
+ return kore_print(pattern, definition_dir=self.llvm_dir, color=bool(color))
def prove(
self,
@@ -222,7 +222,7 @@ def view_kcfg(
claim_id: str,
) -> None:
proof = APRProof.read_proof_data(proof_dir=self.proof_dir, id=f'{spec_module}.{claim_id}')
- kcfg_viewer = APRProofViewer(proof, self.kprove, node_printer=KIMPNodePrinter(kimp=self))
+ kcfg_viewer = APRProofViewer(proof, self.kprove, node_printer=ImpNodePrinter(kimp=self))
kcfg_viewer.run()
def show_kcfg(
@@ -231,7 +231,7 @@ def show_kcfg(
claim_id: str,
) -> None:
proof = APRProof.read_proof_data(proof_dir=self.proof_dir, id=f'{spec_module}.{claim_id}')
- proof_show = APRProofShow(self.kprove, node_printer=KIMPNodePrinter(kimp=self))
+ proof_show = APRProofShow(self.kprove, node_printer=ImpNodePrinter(kimp=self))
res_lines = proof_show.show(
proof,
)
@@ -303,10 +303,10 @@ def legacy_explore(
)
-class KIMPNodePrinter(NodePrinter):
- kimp: KIMP
+class ImpNodePrinter(NodePrinter):
+ kimp: KImp
- def __init__(self, kimp: KIMP):
+ def __init__(self, kimp: KImp):
NodePrinter.__init__(self, kimp.kprove)
self.kimp = kimp
diff --git a/package/smoke-test.sh b/package/smoke-test.sh
index 6be4246..69a4ca1 100755
--- a/package/smoke-test.sh
+++ b/package/smoke-test.sh
@@ -4,7 +4,7 @@ set -euxo pipefail
kimp --help
-kimp run --verbose --input-file examples/sumto10.imp
+kimp run --verbose examples/sumto10.imp --env 'x=0,y=1' --env z=2
kimp prove --verbose examples/specs/imp-sum-spec.k IMP-SUM-SPEC sum-spec