aboutsummaryrefslogtreecommitdiffstats
path: root/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_danger.xml
blob: ad2d03a5e1b5ebe94fc315aa9f9b6dca83109e41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">  
 <item android:state_focused="true"><shape>
        <solid android:color="@color/white" />
        <stroke android:width="2dp" android:color="@color/bbutton_danger" />
        <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
    </shape></item>  
    
<item><shape android:shape="rectangle">
        <solid android:color="@color/white" />
        <stroke android:width="1dp" android:color="@color/bbutton_danger" />
        <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
    </shape></item>
    
    

</selector>
href='#n290'>290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
--  Debugging during synthesis.
--  Copyright (C) 2019 Tristan Gingold
--
--  This file is part of GHDL.
--
--  This program is free software: you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation, either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with this program.  If not, see <gnu.org/licenses>.

with Files_Map;
with Tables;
with Simple_IO; use Simple_IO;
with Name_Table;
with Str_Table;

with Grt.Types; use Grt.Types;
with Grt.Readline;

with Vhdl.Errors;
with Vhdl.Nodes_Walk; use Vhdl.Nodes_Walk;
with Vhdl.Parse;

with Elab.Vhdl_Context.Debug; use Elab.Vhdl_Context.Debug;
with Elab.Vhdl_Debug; use Elab.Vhdl_Debug;

package body Elab.Debugger is
   Current_Instance : Synth_Instance_Acc;
   Current_Loc : Node;

   type Debug_Reason is
     (
      Reason_Init,
      Reason_Break,
      Reason_Time,
      Reason_Error
     );

   function Debug_Current_Instance return Synth_Instance_Acc is
   begin
      return Current_Instance;
   end Debug_Current_Instance;

   package Breakpoints is new Tables
     (Table_Index_Type => Natural,
      Table_Component_Type => Node,
      Table_Low_Bound => 1,
      Table_Initial => 16);

   function Is_Breakpoint_Hit return Boolean is
   begin
      for I in Breakpoints.First .. Breakpoints.Last loop
         if Breakpoints.Table (I) = Current_Loc then
            return True;
         end if;
      end loop;
      return False;
   end Is_Breakpoint_Hit;

   --  Current execution state, or reason to stop execution (set by the
   --  last debugger command).
   type Exec_State_Type is
     (--  Execution should continue until a breakpoint is reached or assertion
      --  failure.
      Exec_Run,

      --  Execution will stop at the next statement.
      Exec_Single_Step,

      --  Execution will stop at the next simple statement in the same frame.
      Exec_Next,

      --  Execution will stop at the next statement in the same frame.  In
      --  case of compound statement, stop after the compound statement.
      Exec_Next_Stmt);

   Exec_State : Exec_State_Type := Exec_Run;

   --  Current frame for next.
   Exec_Instance : Synth_Instance_Acc;

   --  Current statement for next_stmt.
   Exec_Statement : Node;

   function Is_Within_Statement (Stmt : Node; Cur : Node) return Boolean
   is
      Parent : Node;
   begin
      Parent := Cur;
      loop
         if Parent = Stmt then
            return True;
         end if;
         case Get_Kind (Parent) is
            when Iir_Kinds_Sequential_Statement =>
               Parent := Get_Parent (Parent);
            when others =>
               return False;
         end case;
      end loop;
   end Is_Within_Statement;

   Prompt_Debug : constant String := "debug> " & ASCII.NUL;
   Prompt_Error : constant String := "error> " & ASCII.NUL;
   Prompt_Init  : constant String := "init> " & ASCII.NUL;
   --  Prompt_Elab  : constant String := "elab> " & ASCII.NUL;

   procedure Disp_Iir_Location (N : Node) is
   begin
      if N = Null_Iir then
         Put_Err ("??:??:??");
      else
         Put_Err (Vhdl.Errors.Disp_Location (N));
      end if;
      Put_Err (": ");
   end Disp_Iir_Location;

   --  For the list command: current file and current line.
   List_Current_File : Source_File_Entry := No_Source_File_Entry;
   List_Current_Line : Natural := 0;
   List_Current_Line_Pos : Source_Ptr := 0;

   --  Set List_Current_* from a location.  To be called after program break
   --  to indicate current location.
   procedure Set_List_Current (Loc : Location_Type)
   is
      Offset : Natural;
   begin
      Files_Map.Location_To_Coord
        (Loc, List_Current_File, List_Current_Line_Pos,
         List_Current_Line, Offset);
   end Set_List_Current;

   procedure Disp_Current_Lines
   is
      use Files_Map;
      --  Number of lines to display before and after the current line.
      Radius : constant := 5;

      Buf : File_Buffer_Acc;

      Pos : Source_Ptr;
      Line : Natural;
      Len : Source_Ptr;
      C : Character;
   begin
      if List_Current_Line > Radius then
         Line := List_Current_Line - Radius;
      else
         Line := 1;
      end if;

      Pos := File_Line_To_Position (List_Current_File, Line);
      Buf := Get_File_Source (List_Current_File);

      while Line < List_Current_Line + Radius loop
         --  Compute line length.
         Len := 0;
         loop
            C := Buf (Pos + Len);
            exit when C = ASCII.CR or C = ASCII.LF or C = ASCII.EOT;
            Len := Len + 1;
         end loop;

         --  Disp line number.
         declare
            Str : constant String := Natural'Image (Line);
         begin
            if Line = List_Current_Line then
               Put ('*');
            else
               Put (' ');
            end if;
            Put ((Str'Length .. 5 => ' '));
            Put (Str (Str'First + 1 .. Str'Last));
            Put (' ');
         end;

         --  Disp line.
         Put_Line (String (Buf (Pos .. Pos + Len - 1)));

         --  Skip EOL.
         exit when C = ASCII.EOT;
         Pos := Pos + Len + 1;
         if C = ASCII.CR then
            if Buf (Pos) = ASCII.LF then
               Pos := Pos + 1;
            end if;
         else
            pragma Assert (C = ASCII.LF);
            if Buf (Pos) = ASCII.CR then
               Pos := Pos + 1;
            end if;
         end if;

         Line := Line + 1;
      end loop;
   end Disp_Current_Lines;

   procedure Disp_Source_Line (Loc : Location_Type)
   is
      use Files_Map;

      File : Source_File_Entry;
      Line_Pos : Source_Ptr;
      Line : Natural;
      Offset : Natural;
      Buf : File_Buffer_Acc;
      Next_Line_Pos : Source_Ptr;
   begin
      Location_To_Coord (Loc, File, Line_Pos, Line, Offset);
      Buf := Get_File_Source (File);
      Next_Line_Pos := File_Line_To_Position (File, Line + 1);
      Put (String (Buf (Line_Pos .. Next_Line_Pos - 1)));
   end Disp_Source_Line;

   --  The status of the debugger.  This status can be modified by a command
   --  as a side effect to resume or quit the debugger.
   type Command_Status_Type is (Status_Default, Status_Quit);
   Command_Status : Command_Status_Type;

   --  This exception can be raised by a debugger command to directly return
   --  to the prompt.
   Command_Error : exception;

   --  If set (by commands), call this procedure on empty line to repeat
   --  last command.
   Cmd_Repeat : Menu_Procedure;

   type Menu_Kind is (Menu_Command, Menu_Submenu);
   type Menu_Entry (Kind : Menu_Kind);
   type Menu_Entry_Acc is access all Menu_Entry;

   type Menu_Entry (Kind : Menu_Kind) is record
      Name : Cst_String_Acc;
      Help : Cst_String_Acc;
      Next : Menu_Entry_Acc;

      case Kind is
         when Menu_Command =>
            Proc : Menu_Procedure;
         when Menu_Submenu =>
            First : Menu_Entry_Acc := null;
      end case;
   end record;

   function Is_Blank (C : Character) return Boolean is
   begin
      return C = ' ' or else C = ASCII.HT;
   end Is_Blank;

   function Skip_Blanks (S : String) return Positive
   is
      P : Positive := S'First;
   begin
      while P <= S'Last and then Is_Blank (S (P)) loop
         P := P + 1;
      end loop;
      return P;
   end Skip_Blanks;

   function Skip_Blanks (S : String; F : Positive) return Positive is
   begin
      return Skip_Blanks (S (F .. S'Last));
   end Skip_Blanks;

   --  Return the position of the last character of the word (the last
   --  non-blank character).
   function Get_Word (S : String) return Positive
   is
      P : Positive := S'First;
   begin
      while P <= S'Last and then not Is_Blank (S (P)) loop
         P := P + 1;
      end loop;
      return P - 1;
   end Get_Word;

   function Get_Word (S : String; F : Positive) return Positive is
   begin
      return Get_Word (S (F .. S'Last));
   end Get_Word;

   procedure To_Num (Str : String; Res : out Uns32; Valid : out Boolean) is
   begin
      Res := 0;
      Valid := True;
      for P in Str'Range loop
         if Str (P) in '0' .. '9' then
            Res := Res * 10 + Character'Pos (Str (P)) - Character'Pos ('0');
         else
            Valid := False;
            return;
         end if;
      end loop;
   end To_Num;

   procedure Info_Params_Proc (Line : String)
   is
      pragma Unreferenced (Line);
      Decl : Iir;
      Params : Iir;
   begin
      Decl := Get_Source_Scope (Current_Instance);
      loop
         case Get_Kind (Decl) is
            when Iir_Kind_Procedure_Body
              | Iir_Kind_Function_Body =>
               Decl := Get_Subprogram_Specification (Decl);
               exit;
            when Iir_Kind_Process_Statement
              | Iir_Kind_Sensitized_Process_Statement =>
               Put_Line ("processes have no parameters");
               return;
            when Iir_Kind_While_Loop_Statement
              | Iir_Kind_If_Statement
              | Iir_Kind_For_Loop_Statement
              | Iir_Kind_Case_Statement =>
               Decl := Get_Parent (Decl);
            when others =>
               Vhdl.Errors.Error_Kind ("info_params_proc", Decl);
         end case;
      end loop;
      Params := Get_Interface_Declaration_Chain (Decl);
      Disp_Declaration_Objects (Current_Instance, Params);
   end Info_Params_Proc;

   procedure Info_Locals_Proc (Line : String)
   is
      pragma Unreferenced (Line);
      Decl : Iir;
      Decls : Iir;
   begin
      --  From statement to declaration.
      Decl := Get_Source_Scope (Current_Instance);
      loop
         case Get_Kind (Decl) is
            when Iir_Kind_Procedure_Body
               | Iir_Kind_Function_Body
               | Iir_Kind_Process_Statement
               | Iir_Kind_Sensitized_Process_Statement
               | Iir_Kind_Generate_Statement_Body =>
               Decls := Get_Declaration_Chain (Decl);
               exit;
            when Iir_Kind_While_Loop_Statement
              | Iir_Kind_If_Statement
              | Iir_Kind_For_Loop_Statement
              | Iir_Kind_Case_Statement =>
               Decl := Get_Parent (Decl);
            when others =>
               Vhdl.Errors.Error_Kind ("info_params_proc", Decl);
         end case;
      end loop;
      Disp_Declaration_Objects (Current_Instance, Decls);
   end Info_Locals_Proc;

   procedure Info_Instance_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      Debug_Synth_Instance (Current_Instance);
   end Info_Instance_Proc;

   --  Next statement in the same frame, but handle compound statements as
   --  one statement.
   procedure Next_Stmt_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      Exec_State := Exec_Next_Stmt;
      Exec_Instance := Current_Instance;
      Exec_Statement := Current_Loc;
      Flag_Need_Debug := True;
      Command_Status := Status_Quit;
   end Next_Stmt_Proc;

   --  Finish parent statement.
   procedure Finish_Stmt_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      Exec_State := Exec_Next_Stmt;
      Exec_Instance := Current_Instance;
      Exec_Statement := Get_Parent (Current_Loc);
      Flag_Need_Debug := True;
      Command_Status := Status_Quit;
   end Finish_Stmt_Proc;

   procedure Next_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      Exec_State := Exec_Next;
      Exec_Instance := Current_Instance;
      Flag_Need_Debug := True;
      Command_Status := Status_Quit;
      Cmd_Repeat := Next_Proc'Access;
   end Next_Proc;

   procedure Step_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      Exec_State := Exec_Single_Step;
      Flag_Need_Debug := True;
      Command_Status := Status_Quit;
      Cmd_Repeat := Step_Proc'Access;
   end Step_Proc;

   Break_Id : Name_Id;

   procedure Set_Breakpoint (Stmt : Iir) is
   begin
      Put_Line ("set breakpoint at: " & Files_Map.Image (Get_Location (Stmt)));
      Breakpoints.Append (Stmt);
      Flag_Need_Debug := True;
   end Set_Breakpoint;

   function Cb_Set_Break (El : Iir) return Walk_Status is
   begin
      case Get_Kind (El) is
         when Iir_Kind_Function_Declaration
           | Iir_Kind_Procedure_Declaration =>
            if Get_Identifier (El) = Break_Id
              and then
              Get_Implicit_Definition (El) not in Iir_Predefined_Implicit
            then
               Set_Breakpoint
                 (Get_Sequential_Statement_Chain (Get_Subprogram_Body (El)));
            end if;
         when others =>
            null;
      end case;
      return Walk_Continue;
   end Cb_Set_Break;

   procedure Break_Proc (Line : String)
   is
      Status : Walk_Status;
      P : Natural;
   begin
      P := Skip_Blanks (Line);
      if Line (P) = '"' then
         --  An operator name.
         declare
            use Str_Table;
            Str : String8_Id;
            Len : Nat32;
         begin
            Str := Create_String8;
            Len := 0;
            P := P + 1;
            while Line (P) /= '"' loop
               Append_String8_Char (Line (P));
               Len := Len + 1;
               P := P + 1;
            end loop;
            Break_Id := Vhdl.Parse.Str_To_Operator_Name
              (Str, Len, No_Location);
            --  FIXME: free string.
            --  FIXME: catch error.
         end;
      else
         Break_Id := Name_Table.Get_Identifier (Line (P .. Line'Last));
      end if;
      Status := Walk_Declarations (Cb_Set_Break'Access);
      pragma Assert (Status = Walk_Continue);
   end Break_Proc;

   procedure Help_Proc (Line : String);

   procedure Prepare_Continue is
   begin
      Command_Status := Status_Quit;

      --  Set Flag_Need_Debug only if there is at least one enabled breakpoint.
      Flag_Need_Debug := False;
      for I in Breakpoints.First .. Breakpoints.Last loop
         Flag_Need_Debug := True;
         exit;
      end loop;
   end Prepare_Continue;

   procedure Cont_Proc (Line : String) is
      pragma Unreferenced (Line);
   begin
      Prepare_Continue;
   end Cont_Proc;

   procedure Disp_A_Frame (Inst: Synth_Instance_Acc)
   is
      Src : Node;
   begin
      if Inst = Root_Instance then
         Put_Line ("root instance");
         return;
      end if;

      Src := Get_Source_Scope (Inst);
      Put (Vhdl.Errors.Disp_Node (Src));
      Put (" at ");
      Put (Files_Map.Image (Get_Location (Src)));
      New_Line;
   end Disp_A_Frame;

   procedure Debug_Bt (Instance : Synth_Instance_Acc)
   is
      Inst : Synth_Instance_Acc;
   begin
      Inst := Instance;
      while Inst /= null loop
         Disp_A_Frame (Inst);
         Inst := Get_Caller_Instance (Inst);
      end loop;
   end Debug_Bt;
   pragma Unreferenced (Debug_Bt);

   procedure Where_Proc (Line : String)
   is
      pragma Unreferenced (Line);
      Inst : Synth_Instance_Acc;
   begin
      --  Check_Current_Process;
      Inst := Current_Instance;
      while Inst /= null loop
         Disp_A_Frame (Inst);
         Inst := Get_Caller_Instance (Inst);
      end loop;
   end Where_Proc;

   procedure List_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      Disp_Current_Lines;
   end List_Proc;

   procedure List_Hierarchy (Line : String)
   is
      With_Objs : Boolean;
      Recurse : Boolean;
      F, L : Natural;
   begin
      With_Objs := False;
      Recurse := False;
      F := Line'First;
      loop
         F := Skip_Blanks (Line, F);
         exit when F > Line'Last;
         L := Get_Word (Line, F);
         if Line (F .. L) = "-v" then
            With_Objs := True;
         elsif Line (F .. L) = "-R" then
            Recurse := True;
         elsif Line (F .. L) = "-h" then
            Put_Line ("options:");
            Put_Line (" -h   this help");
            Put_Line (" -v   with objects");
            Put_Line (" -R   recurses");
            return;
         else
            Put_Line ("unknown option: " & Line (F .. L));
            return;
         end if;
         F := L + 1;
      end loop;

      Disp_Hierarchy (Current_Instance, Recurse, With_Objs);
   end List_Hierarchy;

   procedure Change_Hierarchy (Line : String)
   is
      F : Natural;
      Res : Synth_Instance_Acc;
   begin
      F := Skip_Blanks (Line);
      if Line (F .. Line'Last) = ".." then
         Res := Get_Instance_Path_Parent (Current_Instance);
         if Res = null then
            Put_Line ("already at top");
            return;
         end if;
      else
         Res := Get_Sub_Instance_By_Name (Current_Instance,
                                          Line (F .. Line'Last));
         if Res = null then
            Put_Line ("no such sub-instance");
            return;
         end if;
      end if;
      Current_Instance := Res;
   end Change_Hierarchy;

   procedure Print_Hierarchy_Path (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      Disp_Instance_Path (Current_Instance);
      New_Line;
   end Print_Hierarchy_Path;

   Menu_Info_Instance : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("inst*ance"),
      Help => new String'("display instance info"),
      Next => null, -- Menu_Info_Tree'Access,
      Proc => Info_Instance_Proc'Access);

   Menu_Info_Locals : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("locals"),
      Help => new String'("display local objects"),
      Next => Menu_Info_Instance'Access,
      Proc => Info_Locals_Proc'Access);

   Menu_Info_Params : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("param*eters"),
      Help => new String'("display parameters"),
      Next => Menu_Info_Locals'Access, -- Menu_Info_Tree'Access,
      Proc => Info_Params_Proc'Access);

   Menu_Info : aliased Menu_Entry :=
     (Kind => Menu_Submenu,
      Name => new String'("i*nfo"),
      Help => null,
      Next => null, -- Menu_Ps'Access,
      First => Menu_Info_Params'Access); --  Menu_Info_Proc'Access);

   Menu_Pwh : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("pwh"),
      Help => new String'("display current hierarchy path"),
      Next => Menu_Info'Access,
      Proc => Print_Hierarchy_Path'Access);

   Menu_Ch : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("ch"),
      Help => new String'("change hierarchy path"),
      Next => Menu_Pwh'Access,
      Proc => Change_Hierarchy'Access);

   Menu_Lh : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("lh"),
      Help => new String'("list hierarchy"),
      Next => Menu_Ch'Access,
      Proc => List_Hierarchy'Access);

   Menu_List : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("l*list"),
      Help => new String'("list source around current line"),
      Next => Menu_Lh'Access,
      Proc => List_Proc'Access);

   Menu_Cont : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("c*ont"),
      Help => new String'("continue simulation"),
      Next => Menu_List'Access, --Menu_Print'Access,
      Proc => Cont_Proc'Access);

   Menu_Nstmt : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("ns*tmt"),
      Help => new String'("execute statement (next statement)"),
      Next => Menu_Cont'Access, -- Menu_Up'Access,
      Proc => Next_Stmt_Proc'Access);

   Menu_Fstmt : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("fs*tmt"),
      Help => new String'("execute until end of subprogram"),
      Next => Menu_Nstmt'Access,
      Proc => Finish_Stmt_Proc'Access);

   Menu_Next : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("n*ext"),
      Help => new String'("execute to next statement"),
      Next => Menu_Fstmt'Access,
      Proc => Next_Proc'Access);

   Menu_Step : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("s*tep"),
      Help => new String'("execute one statement"),
      Next => Menu_Next'Access,
      Proc => Step_Proc'Access);

   Menu_Break : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("b*reak"),
      Help => new String'("set a breakpoint (or list then)"),
      Next => Menu_Step'Access,
      Proc => Break_Proc'Access);

   Menu_Where : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("w*here"),
      Help => new String'("disp call stack"),
      Next => Menu_Break'Access,
      Proc => Where_Proc'Access);

   Menu_Help2 : aliased Menu_Entry :=
     (Kind => Menu_Command,
      Name => new String'("?"),
      Help => new String'("print help"),
      Next => Menu_Where'Access,
      Proc => Help_Proc'Access);

   Menu_Top : aliased Menu_Entry :=
     (Kind => Menu_Submenu,
      Help => null,
      Name => null,
      Next => null,
      First => Menu_Help2'Access);

   --  Append command to MENU.
   procedure Append_Menu (Menu : Menu_Entry;
                          Name : Cst_String_Acc;
                          Help : Cst_String_Acc;
                          Proc : Menu_Procedure)
   is
      M, L : Menu_Entry_Acc;
   begin
      M := new Menu_Entry'(Kind => Menu_Command,
                           Name => Name,
                           Help => Help,
                           Next => null,
                           Proc => Proc);

      L := Menu.First;
      while L.Next /= null loop
         L := L.Next;
      end loop;
      L.Next := M;
   end Append_Menu;

   procedure Append_Menu_Command (Name : Cst_String_Acc;
                                  Help : Cst_String_Acc;
                                  Proc : Menu_Procedure) is
   begin
      Append_Menu (Menu_Top, Name, Help, Proc);
   end Append_Menu_Command;

   procedure Append_Info_Command (Name : Cst_String_Acc;
                                  Help : Cst_String_Acc;
                                  Proc : Menu_Procedure) is
   begin
      Append_Menu (Menu_Info, Name, Help, Proc);
   end Append_Info_Command;

   function Find_Menu (Menu : Menu_Entry_Acc; Cmd : String)
                      return Menu_Entry_Acc
   is
      function Is_Cmd (Cmd_Name : String; Str : String) return Boolean
      is
         -- Number of characters that were compared.
         P : Natural;
      begin
         P := 0;
         --  Prefix (before the '*').
         loop
            if P = Cmd_Name'Length then
               --  Full match.
               return P = Str'Length;
            end if;
            exit when Cmd_Name (Cmd_Name'First + P) = '*';
            if P = Str'Length then
               --  Command is too short
               return False;
            end if;
            if Cmd_Name (Cmd_Name'First + P) /= Str (Str'First + P) then
               return False;
            end if;
            P := P + 1;
         end loop;
         --  Suffix (after the '*')
         loop
            if P = Str'Length then
               return True;
            end if;
            if P + 1 = Cmd_Name'Length then
               --  String is too long
               return False;
            end if;
            if Cmd_Name (Cmd_Name'First + P + 1) /= Str (Str'First + P) then
               return False;
            end if;
            P := P + 1;
         end loop;
      end Is_Cmd;
      Ent : Menu_Entry_Acc;
   begin
      Ent := Menu.First;
      while Ent /= null loop
         if Is_Cmd (Ent.Name.all, Cmd) then
            return Ent;
         end if;
         Ent := Ent.Next;
      end loop;
      return null;
   end Find_Menu;

   procedure Parse_Command (Line : String;
                            P : in out Natural;
                            Menu : out Menu_Entry_Acc)
   is
      E : Natural;
   begin
      P := Skip_Blanks (Line (P .. Line'Last));
      if P > Line'Last then
         return;
      end if;
      E := Get_Word (Line (P .. Line'Last));
      Menu := Find_Menu (Menu, Line (P .. E));
      if Menu = null then
         Put_Line ("command '" & Line (P .. E) & "' not found");
      end if;
      P := E + 1;
   end Parse_Command;

   procedure Help_Proc (Line : String)
   is
      P : Natural;
      Root : Menu_Entry_Acc := Menu_Top'access;
   begin
      Put_Line ("This is the help command");
      P := Line'First;
      while P < Line'Last loop
         Parse_Command (Line, P, Root);
         if Root = null then
            return;
         elsif Root.Kind /= Menu_Submenu then
            Put_Line ("Menu entry " & Root.Name.all & " is not a submenu");
            return;
         end if;
      end loop;

      Root := Root.First;
      while Root /= null loop
         Put (Root.Name.all);
         if Root.Kind = Menu_Submenu then
            Put (" (menu)");
         end if;
         New_Line;
         Root := Root.Next;
      end loop;
   end Help_Proc;

   procedure Debug (Reason: Debug_Reason)
   is
      use Grt.Readline;
      Raw_Line : Ghdl_C_String;
      Prompt : Ghdl_C_String;
   begin
      Prompt := To_Ghdl_C_String (Prompt_Debug'Address);

      case Reason is
         when Reason_Init =>
            Prompt := To_Ghdl_C_String (Prompt_Init'Address);
         when Reason_Error =>
            Prompt := To_Ghdl_C_String (Prompt_Error'Address);
         when Reason_Break =>
            case Exec_State is
               when Exec_Run =>
                  if not Is_Breakpoint_Hit then
                     return;
                  end if;
                  Put_Line ("breakpoint hit");
               when Exec_Single_Step =>
                  null;
               when Exec_Next =>
                  if Current_Instance /= Exec_Instance then
                     return;
                  end if;
               when Exec_Next_Stmt =>
                  if Current_Instance /= Exec_Instance
                    or else Is_Within_Statement (Exec_Statement, Current_Loc)
                  then
                     return;
                  end if;
            end case;
            --  Default state.
            Exec_State := Exec_Run;
         when Reason_Time =>
            Exec_State := Exec_Run;
      end case;

      case Reason is
         when Reason_Error
           | Reason_Break =>
            Put ("stopped at: ");
            Disp_Iir_Location (Current_Loc);
            New_Line;
            Disp_Source_Line (Get_Location (Current_Loc));
         when others =>
            null;
      end case;

      if Current_Loc /= Null_Node then
         Set_List_Current (Get_Location (Current_Loc));
      end if;

      Command_Status := Status_Default;

      loop
         loop
            Raw_Line := Readline (Prompt);
            --  Skip empty lines
            if Raw_Line = null or else Raw_Line (1) = ASCII.NUL then
               if Cmd_Repeat /= null then
                  Cmd_Repeat.all ("");
                  case Command_Status is
                     when Status_Default =>
                        null;
                     when Status_Quit =>
                        return;
                  end case;
               end if;
            else
               Cmd_Repeat := null;
               exit;
            end if;
         end loop;
         declare
            Line_Last : constant Natural := strlen (Raw_Line);
            Line : String renames Raw_Line (1 .. Line_Last);
            P, E : Positive;
            Cmd : Menu_Entry_Acc := Menu_Top'Access;
         begin
            --  Find command
            P := 1;
            loop
               E := P;
               Parse_Command (Line, E, Cmd);
               exit when Cmd = null;
               case Cmd.Kind is
                  when Menu_Submenu =>
                     if E > Line_Last then
                        Put_Line ("missing command for submenu "
                                    & Line (P .. E - 1));
                        Cmd := null;
                        exit;
                     end if;
                     P := E;
                  when Menu_Command =>
                     exit;
               end case;
            end loop;

            if Cmd /= null then
               Cmd.Proc.all (Line (E .. Line_Last));

               case Command_Status is
                  when Status_Default =>
                     null;
                  when Status_Quit =>
                     exit;
               end case;
            end if;
         exception
            when Command_Error =>
               null;
         end;
      end loop;
      --  Put ("resuming");
   end Debug;

   procedure Debug_Init (Top : Node) is
   begin
      Flag_Debug_Enable := True;

      Current_Instance := null;
      Current_Loc := Top;

      --  To avoid warnings.
      Exec_Statement := Null_Node;
      Exec_Instance := null;

      Debug (Reason_Init);
   end Debug_Init;

   procedure Debug_Elab (Top : Synth_Instance_Acc) is
   begin
      Current_Instance := Top;
      Current_Loc := Get_Source_Scope (Top);
      Flag_Debug_Enable := True;

      --  To avoid warnings.
      Exec_Statement := Null_Node;
      Exec_Instance := null;

      Debug (Reason_Init);
   end Debug_Elab;

   procedure Debug_Break (Inst : Synth_Instance_Acc; Stmt : Node) is
   begin
      Current_Instance := Inst;
      Current_Loc := Stmt;

      Debug (Reason_Break);
   end Debug_Break;

   procedure Debug_Time is
   begin
      Current_Instance := Root_Instance;
      Current_Loc := Null_Node;

      Debug (Reason_Time);
   end Debug_Time;

   procedure Debug_Leave (Inst : Synth_Instance_Acc) is
   begin
      if Exec_Instance = Inst then
         --  Will be destroyed.
         Exec_Instance := null;

         case Exec_State is
            when Exec_Run =>
               null;
            when Exec_Single_Step =>
               null;
            when Exec_Next
              | Exec_Next_Stmt =>
               --  Leave the frame, will stop just after.
               Exec_State := Exec_Single_Step;
         end case;
      end if;
   end Debug_Leave;

   procedure Debug_Error (Inst : Synth_Instance_Acc; Expr : Node) is
   begin
      if Flag_Debug_Enable then
         Current_Instance := Inst;
         Current_Loc := Expr;
         Debug (Reason_Error);
      end if;
      if Error_Hook /= null then
         Error_Hook.all;
      end if;
   end Debug_Error;

end Elab.Debugger;