/core/library/toplevel_proc/toplevel_proc.pl

https://github.com/ciao-lang/ciao · Perl · 135 lines · 128 code · 7 blank · 0 comment · 2 complexity · 529e0f598ea400955fba452c36eb4181 MD5 · raw file

  1. :- module(toplevel_proc, [
  2. start/1, kill/1,
  3. format/3,
  4. wait_for_answer/4
  5. ], [fsyntax, dcg, assertions]).
  6. :- doc(title, "Interactive top-level as an external process").
  7. :- doc(author, "Remy Haemmerle").
  8. :- doc(author, "Jose F. Morales (minor)").
  9. :- doc(module, "This module provides an abstraction for starting new
  10. toplevels as an external processes (see @lib{process}).
  11. This modules is currently used for testing automatically
  12. interactions with the toplevel. Buffering on streams is
  13. disabled.").
  14. :- doc(bug, "Make input_set_unbuf optional").
  15. :- doc(bug, "Make it more robust (see stream_wait)").
  16. :- doc(bug, "Experimental, use with care").
  17. :- doc(bug, "Missing a polite and clean 'stop/1' predicate").
  18. :- use_module(library(process)).
  19. :- use_module(library(format), [format/2, format/3]).
  20. :- use_module(library(stream_wait)).
  21. :- use_module(engine(stream_basic), [flush_output/1, close/1]).
  22. :- use_module(engine(io_basic)).
  23. :- use_module(ciaobld(config_common), [cmd_path/4]).
  24. cmd_execname(ciaosh) := ~cmd_path(core, plexe, 'ciaosh').
  25. :- pred format(TopLevelProc, String, Args) # "Equivalent to
  26. @pred{format(IntputStream, String, Args)} from module @lib{format},
  27. where the @var{InputStream} correspond to the input stream of the
  28. top-level identified by @var{TopLevelProc}.".
  29. format(TL, _Str, _Arg):-
  30. var(TL), !,
  31. throw(error(instantiation_error, 'toplevel_proc:format'/3-1)).
  32. format(TL, Str, Args):-
  33. Status = ~get_status(TL), !,
  34. (
  35. var(Status) ->
  36. true
  37. ;
  38. throw(error(toplevel_proc_dead, 'toplevel_proc:format'/3-1))
  39. ),
  40. format:format(~get_input(TL), Str, Args),
  41. flush_output(~get_input(TL)).
  42. format(TL, _Str, _Args):-
  43. throw(error(domain_error(toplevel_proc, TL), 'toplevel_proc:format'/3-1)).
  44. get_input(toplevel(Stream, _, _, _, _)) := Stream.
  45. get_output(toplevel(_, Stream, _, _, _)) := Stream.
  46. get_error(toplevel(_, _, Stream, _, _)) := Stream.
  47. get_process(toplevel(_, _, _, Process, _)) := Process.
  48. get_status(toplevel(_, _, _, _, Status)) := Status.
  49. :- pred start(TopLevelProc) # "Starts a Ciao Top-level and unifies
  50. @var{TopLevelProc} with an implementation defined identifier.".
  51. start(TL):-
  52. TL = toplevel(IS, OS, ES, Process, _),
  53. process_call(~cmd_execname(ciaosh), [],
  54. [stdin(stream(IS)), stdout(stream(OS)), stderr(stream(ES)),
  55. background(Process)]),
  56. stream_wait:input_set_unbuf(OS),
  57. stream_wait:input_set_unbuf(ES),
  58. toplevel_proc:format(TL, "true.\n\n", []),
  59. (
  60. wait_for_answer(TL, _, _, 2000000) ->
  61. true
  62. ;
  63. throw(error(unknown_error, 'toplevel_proc:start'/1))
  64. ).
  65. :- pred kill(TopLevelProc) # "Kills the top-level identified by
  66. @var{TopLevelProc}, after having close its associated stream.
  67. Silently suceeds if the top-level have been previously killed".
  68. kill(TL):-
  69. var(~get_status(TL)), !,
  70. close(~get_input(TL)),
  71. close(~get_output(TL)),
  72. close(~get_error(TL)),
  73. process_kill(~get_process(TL)),
  74. % join to avoid zombies (kill just send signal)
  75. process_join(~get_process(TL)).
  76. kill(_).
  77. :- pred wait_for_answer(TopLevelProc, Str, EStr, TimeOut) # "Waits for
  78. an answer form the toplevel identified by @var{TopLevelProc} and
  79. unifies @var{Str} with the list of characters reads so far.
  80. Answers are assumed to occur when either the string \"yes\" or the
  81. string \"no\" are read for the output of the toplevel. The system
  82. waits at most @var{TimeOut} microseconds between each character
  83. read.".
  84. wait_for_answer(TL, Str, EStr, TimeOut) :-
  85. wait_for_answer_(~get_output(TL), -1, TimeOut, Str, []),
  86. get_error_string_(~get_error(TL), EStr, []).
  87. wait_for_answer_(S, C0, TimeOut) -->
  88. {
  89. stream_wait:input_wait(S, TimeOut), !,
  90. get_code(S, C)
  91. },
  92. [C],
  93. {
  94. (C0 = -1, C = 0'n, C0_ = C;
  95. C0 = 0'n, C = 0'o, C0_ = 0;
  96. C0 = -1, C = 0'y, C0_ = C;
  97. C0 = 0'y, C = 0'e, C0_ = C;
  98. C0 = 0'e, C = 0's, C0_ = 0);
  99. C0_ = -1
  100. },!,
  101. (
  102. {C0_ = 0} ->
  103. []
  104. ;
  105. {C = -1} ->
  106. [-1]
  107. ;
  108. wait_for_answer_(S, C0_, TimeOut)
  109. ).
  110. get_error_string_(S) -->
  111. {
  112. stream_wait:input_wait(S, 0),!,
  113. get_code(S, C)
  114. },
  115. [C],
  116. get_error_string_(S).
  117. get_error_string_(_) --> [].