How to show the definition of a ML definition in Isabelle/HOL? - isabelle

I am trying to follow the Isabelle 2020 documentation implementation.pdf (pp. 55). But I do not understand some ML things such as \<^binding> (in terms of what it does; it seems to be a new language outside of SML).
ML ‹
val airspeed_velocity =
Attrib.setup_config_real \<^binding>‹airspeed_velocity› (K 0.0)
›
Following the question How to show the defintion of functions in Isabelle, I did Ctrl+Click on the word "binding" in the jEdit/Isabelle PIDE, which leads me to the following definition in ml_antiquotation.ml:
value_embedded (Binding.make ("binding", ⌂))
(Scan.lift (Parse.input Args.embedded) >> (ML_Syntax.make_binding o Input.source_content)) #>
However, going-to-definition seems to stop working here. I tried to get to further definitions of items such as Binding.make and Scan.lift to understand how those were defined. But Ctrl+Click on them no longer works.
I read in this SO answer that the Isabelle PIDE can serve as an ML editor.
My question is:
Is there way to let jEdit/Isabelle PIDE jump to definition of items in the ML files after the first jump from a .thy file, or in an arbitrary SML file?

Related

How can I map frama-c CLI code to the original c statement? And how can I find the documentation of the api of the frama-c?

I'm trying to get the program dependence graph (PDG) using frama-c at the original code's statement level. However, 'pdg' plug-in in frama-c prints the PDG at the parsed code's node level.
Since frama-c-gui can highlight the original statement corresponds to the node in the parsed code, I'm pretty sure that there is a mapping between the node in parsed code and the original code's statement. How can I get this mapping? Just the line number at the original code is fine, too.
Frama-C's GUI presents two views of the code:
The CIL code (C Intermediate Language), often called normalized source code, which corresponds to a pretty-printing of Frama's AST, in the top center panel;
And the original source code, on the top right panel.
I'm assuming that by parsed code you are talking about the CIL (normalized) code.
Every element in Frama-C's AST contains a location, which is a pair of positions: the first and last coordinates (line, row, column) in the original code which correspond to that element (minus a few exceptions, such as generated elements, macro expansions, etc). Most AST elements have ways to retrieve that location.
In the case of PDG nodes, you can get the associated statements (if any) and then print their location, as in the code below (run with frama-c -pdg -load-module print_pdg.ml <file>):
(* print_pdg.ml *)
let () = Db.Main.extend (fun () ->
Globals.Functions.iter (fun kf ->
let pdg = !Db.Pdg.get kf in
!Db.Pdg.iter_nodes (fun n ->
match PdgTypes.Node.stmt n with
| None -> ()
| Some st ->
Format.printf "%a: %a#."
Printer.pp_location (Cil_datatype.Stmt.loc st) Printer.pp_stmt st
) pdg
)
)
Note that my example script will print each statement multiple times, if there are multiple PDG nodes associated to the same statement.
By default, Printer.pp_location only prints the file name and line of the starting character, but you can make a custom pretty-printer to include the column as well, or the coordinates of the last character.
API and Plug-in Documentation (from question in comment)
Some Frama-C plug-ins (Eva, WP, E-ACSL, etc.) have their own manuals, which are available in the Frama-C download page.
There is no specific manual for the Pdg plug-in, but some Ocamldoc-generated HTML pages can be obtained from the Frama-C API archive.
However, what most Frama-C plug-in developers prefer is to use the OCaml Merlin plug-in in their favorite editor (emacs, vim, etc) to navigate the code and read the source comments (in the .mli files, for instance).
On Emacs, for instance, C-c C-l on a module/variable name jumps to its definition, and C-c C-a alternates between .ml and .mli files (implementation - documentation). Combined with auto-completion for module/function discovery, this provides a form of interactive documentation that many OCaml developers are comfortable with.

Find the instantiation of a class for a type

In Isabelle/HOL, how do I find where a given type was instantiated for a given class? For the sake of this post for example, where real was instantiated as a conditionally_complete_linorder. To justify the question: I might want to know this for inspiration for a similar instantiation, for showing it to someone(s), for Isabelle/HOL practice reading, for curiosity, and so on. My process at the moment:
First, check it actually is: type instantiation real :: conditionally_complete_linorder begin end and see if I get the error message "No parameters and no pending instance proof obligations in instantiation."
Next, ideally before where I'd need to know how i.e. whether it was direct, or implicit via classes C_1[, C_2, C_3, etc]. Then, I would need to find where those instantiations are, either an explicit instantiation real :: conditionally_complete_linorder or the implicit ones for the C_i (same process for either case ofc). I don't know how to find out how, so I have to check for an explicit instantiation, then all possible implicit instantiations.
For explicit, I can do a grep -Ern ~/.local/src/Isabelle2019 -e 'instantiation real :: conditionally_complete_linorder' (and hope the whitespace isn't weird, or do a more robust search :)). Repeat for AFP location. Alternatively, to stay within the jEdit window:
I can find where the class itself was defined by typing term "x::'a::conditionally_complete_linorder" then Ctrl-clicking the class name, and then check if real is directly instantiated in that file with Ctrl-f.
I could then check if it's instantiated where the type real is defined by typing term "x::real" and Ctrl-clicking real, then Ctrl-f for conditionally_complete_linorder in that file.
(If it is in either place it'll be whichever is further down in the import hierarchy, but I find just going through those two steps simpler.) However, if neither two places turn it up then either, for whatever reason, it is explicitly instantiated somewhere else or is implicitly instantiated. For that reason grep is more robust.
If explicit turns nothing up then I check implicit. Looking at the class_deps graph I can see that conditionally_complete_linorder can follow from either complete_linorder or linear_continuum. I can then continue the search by seeing if real is instantiated as either of them (disregarding any I happen to know real can't be instantiated as). I can also check to see if it's instantiated as both conditioanlly_complete_lattice and linorder, which is what I can see conditionally_complete_linorder is a simple (no additional assumptions) combination of*. Repeat for all of these classes recursively until the instantiations are found. In this case, I can see that linear_continuum_topology implies linear_continuum, so kill two birds with one stone with grep -Ern ~/.local/src/Isabelle2019 -e "instantiation.*real" | grep continuum and find /path/to/.local/src/Isabelle2019/src/HOL/Real.thy:897:instantiation real :: linear_continuum.
This process is quite tedious. Less but still quite tedious** would be to get the class_deps graph up and Ctrl-f for "instantiation real" in Real.thy and look for instantiations of: the original class, the superclasses of it, or the classes which imply it. Then in the files each those classes are defined search for "instantiation real". Do this recursively till done. In this case I would have found what I needed in Real.thy.
Is there an easier way? Hope I just missed something obvious.
* I can't Ctrl-click in Conditionally_Complete_Lattices.thy to jump to linorder directly, I guess because of something to do with it being pre-built, so I have to do the term "x::'a::linorder" thing again.
** And also less robust, as it is minus grep-ing which can turn up weirder instantiation locations, then again I'm not sure if this ever comes up in practice.
Thanks
You can import the theory in the code listing below and then use the command find_instantiations. I will leave the code without further explanation, but please feel free to ask further questions in the comments if you need further details or suspect that something is not quite right.
section ‹Auxiliary commands›
theory aux_cmd
imports Complex_Main
keywords "find_instantiations" :: thy_decl
begin
subsection ‹Commands›
ML ‹
fun find_instantiations ctxt c =
let
val {classes, ...} = ctxt |> Proof_Context.tsig_of |> Type.rep_tsig;
val algebra = classes |> #2
val arities = algebra |> Sorts.arities_of;
in
Symtab.lookup arities c
|> the
|> map #1
|> Sorts.minimize_sort algebra
end
fun find_instantiations_cmd tc st =
let
val ctxt = Toplevel.context_of st;
val _ = tc
|> Syntax.parse_typ ctxt
|> dest_Type
|> fst
|> find_instantiations ctxt
|> map Pretty.str
|> Pretty.writeln_chunks
in () end
val q = Outer_Syntax.command
\<^command_keyword>‹find_instantiations›
"find all instantiations of a given type constructor"
(Parse.type_const >> (fn tc => Toplevel.keep (find_instantiations_cmd tc)));
›
subsection ‹Examples›
find_instantiations filter
find_instantiations nat
find_instantiations real
end
Remarks
I would be happy to provide amendments if you find any problems with it, but do expect a reasonable delay in further replies.
The command finds both explicit and implicit instantiations, i.e. it also finds the ones that were achieved by means other than the use of the commands instance or instantiation, e.g. inside an ML function.
Unfortunately, the command does not give you the location of the file where the instantiation was performed - this is something that would be more difficult to achieve, especially, given that instantiations can also be performed programmatically. Nevertheless, given a list of all instantiations, I believe, it is nearly always easy to use the in-built search functionality on the imported theories to narrow down the exact place where the instantiation was performed.

How can I pass a ML value as an argument to an outer syntax command?

I define an outer syntax command, imake to write some code to a file and do some other things. The intended usage is as follows:
theory Scratch
imports Complex_Main "~/Is0/IsS"
begin
imake ‹myfile›
end
The above example will write some contents to the file myfile. myfile should be a path relative to the location of the Scratch theory.
ML ‹val this_path = File.platform_path(Resources.master_directory #{theory})
I would like to be able to use the value this_path in specifying myfile. The imake command is defined in the import ~/Is0/IsS and currently looks as follows:
ML‹(*imake*)
val _ = Outer_Syntax.improper_command #{command_spec "imake"} ""
(Parse.text >>
(fn path => Toplevel.keep
(fn _ => Gc.imake path)))›
The argument is pased using Parse.text, but I need feed it the path based on the ML value this_path, which is defined later (in the Scratch theory). I searched around a lot, trying to figure out how to use something like Parse.const, but I won't be able to figure anything out any time soon.
So: It's important that I use, in some way, Resources.master_directory #{theory} in Scratch.thy, so that imake gets the folder Scratch is in, which will come from the use of #{theory} in Scratch.
If I'm belaboring the last point, it's because in the past, I wasted a lot of time getting the wrong folder, because I didn't understand how to use the command above correctly.
How can I achieve this?
Your minimal examples uses Resource.master_directory with the parameter #{theory} to define your path. #{theory} refers (statically) to the theory at the point where you write down the antiquotation. This is mostly for interactive use, when you explore stuff. For code which is used in other places, you must use the dynamically passed context and extract the theory from it.
The function Toplevel.keep you use takes a function Toplevel.state -> unit as an argument. The Toplevel.state contains a context (see chapter 1 of the Isabelle Implementation Manual), which again contains the current theory; with Toplevel.theory_of you can extract the theory from the state. For example, you could use
Toplevel.keep (fn state => writeln
(File.platform_path (Resources.master_directory (Toplevel.theory_of state))))
to define a command that prints the master_directory for your current theory.
Except in simple cases, it is very likely that you do not only need the theory, but the whole context (which you can get with Toplevel.context_of).
Use setup from preceding (parts of the) theory
In the previous section, I assumed that you always want to use the master directory. For the case where the path should be configurable, Isabelle knows the concept of configuration options.
In your case, you would need to define an configuration option before you declare your imake command
ML ‹
val imake_path = Attrib.setup_config_string #{binding imake_path}
(K path)
› (* declares an option imake_path with the value `path` as default value *)
Then, the imake command can refer to this attribute to retrieve the path via Config.get:
Toplevel.keep (fn state =>
let val path = Config.get (Toplevel.context_of state) imake_path
in ... end)
The value of imake_path can then be set in Isar (only as a string):
declare [[imake_path="/tmp"]]
or in ML, via Config.map (for updating proof contexts) or Config.map_global (for updating theories). Note that you need to feed the updated context back to the system. Isar has the command setup (takes an ML expression of type theory -> theory) for that:
setup ‹Config.map_global imake_path (K "/tmp")›
Configuration options are described in detail in the Isar Implementation Manual, section 1.1.5.
Note: This mechanism does not allow you to automatically set imake_path to the master directory for each new theory. You need to set it manually, e.g. by adding
setup ‹
Config.map imake_path
(K (File.platform_path (Resources.master_directory #{theory})))
›
at the beginning of each theory.
The more general mechanism behind configuration options is context data. For details, see section 1.1 and in particular section 1.1.4 of the Isabelle Implementation Manual). This mechanism is used in a lot of places in Isabelle; the simpset, the configuration of the simplifier, is one example for this.

Do I get 3-address code in Frama-c

I just started developing a frama-c plugin that is doing some kind of alias analysis. I'm using the Dataflow.Backwards analysis and now I have to go through the different assignment statements and collect some stuff about the lvalues.
Does frama-c provide me with 3-address code? Do I have some guarantees about the shape of the lvalue (or any memory access)? I mean, sth like in soot or wala that there is at most one field access, s.t., for a->b->c, there would be a temp variable like tmp=a->b; tmp->c;? I checked the manuals, but I couldn't find anything related to this.
No, there is no such normalization in Frama-C. If you really need it, you can first use a visitor in order to normalize the code so that it suits the requirements of your plug-in. It'd go like that:
class normalize prj: Visitor.frama_c_visitor =
object
inherit Visitor.frama_c_copy prj
method vinstr i =
match i with
| Set (lv,e) -> ...
....
end
let analyze () = ...
let run () =
let my_prj = File.create_project_from_visitor "my_project" (fun prj -> new normalize prj) in
Project.on my_prj analyze ()
The following module from Cil does probably what you want:
http://www.cs.berkeley.edu/~necula/cil/ext.html#toc26. Be aware that the type of the resulting AST is the standard Cil one. You won't be getting any help from the OCaml compiler as to which constructs can be present in the simplified AST, and which ones cannot.
Note also that this module has not been ported to Frama-C so far. You will need some minor adaptation to make it work within Frama-C.

Matlab: Attempt to reference field of non-structure array

I am using the Kernel Density Estimator toolbox form http://www.ics.uci.edu/~ihler/code/kde.html . But I am getting the following error when I try to execute the demo files -
>> demo_kde_3
KDE Example #3 : Product sampling methods (single, anecdotal run)
Attempt to reference field of non-structure array.
Error in double (line 10)
if (npd.N > 0) d = 1; % return 1 if the density exists
Error in repmat (line 49)
nelems = prod(double(siz));
Error in kde (line 39)
if (size(ks,1) == 1) ks = repmat(ks,[size(points,1),1]); end;
Error in demo_kde_3 (line 8)
p = kde([.1,.45,.55,.8],.05); % create a mixture of 4 gaussians for
testing
Can anyone suggest what might be wrong? I am new to Matlab and having a hard time to figure out the problem.
Thank You,
Try changing your current directory away from the #kde folder; you may have to add the #kde folder to your path when you do this. For example run:
cd('c:\');
addpath('full\path\to\the\folder\#kde');
You may also need to add
addpath('full\path\to\the\folder\#kde\examples');
Then see if it works.
It looks like function repmat (a mathworks function) is picking up the #kde class's version of the double function, causing an error. Usually, only objects of the class #kde can invoke that functions which are in the #kde folder.
I rarely use the #folder form of class definitions, so I'm not completely sure of the semantics; I'm curious if this has any effect on the error.
In general, I would not recommend using the #folder class format for any development that you do. The mathworks overhauled their OO paradigm a few versions ago to a much more familiar (and useful) format. Use help classdef to see more. This #kde code seems to predate this upgrade.
MATLAB gives you the code line where the error occurs. As double and repmat belong to MATLAB, the bug probably is in kde.m line 39. Open that file in MATLAB debugger, set a breakpoint on that line (so the execution stops immediately before the execution of that specific line), and then when the code is stopped there, check the situation. Try the entire code line in console (copy-paste or type it, do not single-step, as causing an uncatched error while single-stepping ends the execution of code in debugger), it should give you an error (but doesn't stop execution). Then try pieces of the code of that code line, what works as it should and what not, eg. does the result of size(points, 1) make any sense.
However, debugging unfamiliar code is not an easy task, especially if you're a beginner in MATLAB. But if you learn and understand the essential datatypes of MATLAB (arrays, cell arrays and structs) and the different ways they can be addressed, and apply that knowledge to the situation on the line 39 of kde.m, hopefully you can fix the bug.
Repmat calls double and expects the built-in double to be called.
However I would guess that this is not part of that code:
if (npd.N > 0) d = 1; % return 1 if the density exists
So if all is correct this means that the buil-tin function double has been overloaded, and that this is the reason why the code crashes.
EDIT:
I see that #Pursuit has already addressed the issue but I will leave my answer in place as it describes the method of detection a bit more.

Resources