unit Pseudo;

interface

uses SysUtils, Classes, Rubies;

function RubyEvalStrings(src, args, ret: TStrings): Tvalue;

implementation

var
  Buf: string;
  StdoutBuf: TStrings;
  orig_stdin, orig_stdout, orig_stderr: Tvalue;
{$IFNDEF RUBY18}
  orig_defout: Tvalue;
{$ENDIF}
  cPseudoIO: Tvalue = 0;
  vPseudoIO: Tvalue = 0;

function PseudoIO_write(This, v: Tvalue): Tvalue; cdecl;
var
  str: Tvalue;
  len: Integer;
begin
  str := v;
  if RTYPE(str) <> T_STRING then str := rb_obj_as_string(str);
  len := ap_str_len(str);
  if len <> 0 then
  begin
    Buf := Buf + dl_String(str);
    if Assigned(StdoutBuf) then
      StdoutBuf.Text := Buf;
  end;
  result := INT2FIX(len);
end;

function PseudoIO_gets(This: Tvalue): Tvalue; cdecl;
var
  S: string;
begin
  readln(S);
  if Length(S) <> 0 then // 0 if \C-z. Eof cannot use This case.
    if S[1] = #4{\C-d} then SetLength(S, 0)
    else S := S + #10;

  if Length(S) = 0 then
    result := Qnil
  else
    result := rb_str_new2(PChar(S))
  ;
  rb_lastline_set(result); // $_ set
end;

function PseudoIO_getc(This: Tvalue): Tvalue; cdecl;
var
  c: Char;
begin
  read(c);
  if c = #0 then
    result := Qnil
  else
    result := CHR2FIX(c)
  ;
end;

procedure SetPseudoIO;
begin
  if vPseudoIO = 0 then
  begin
    cPseudoIO := rb_class_new(ap_cIO);
    rb_define_module_function(cPseudoIO, 'write', @PseudoIO_write, 1);
    rb_define_module_function(cPseudoIO, 'gets', @PseudoIO_gets, 0);
    rb_define_module_function(cPseudoIO, 'getc', @PseudoIO_getc, 0);
    vPseudoIO := rb_data_object_alloc(cPseudoIO, nil, nil, nil);
    rb_global_variable(@vPseudoIO);
  end;

  orig_stdin  := ap_stdin ;
  orig_stdout := ap_stdout;
  orig_stderr := ap_stderr;
{$IFNDEF RUBY18}
  orig_defout := ap_defout;
{$ENDIF}

  ap_set_stdin (vPseudoIO);
  ap_set_stdout(vPseudoIO);
  ap_set_stderr(vPseudoIO);
{$IFNDEF RUBY18}
  ap_set_defout(vPseudoIO);
{$ENDIF}
end;

procedure RemovePseudoIO;
begin
  ap_set_stdin (orig_stdin );
  ap_set_stdout(orig_stdout);
  ap_set_stderr(orig_stderr);
{$IFNDEF RUBY18}
  ap_set_defout(orig_defout);
{$ENDIF}
end;

function RubyEvalStrings(src, args, ret: TStrings): Tvalue;
var
  argc: Integer;
  argv: array of PChar;
  i, state: Integer;
begin
  try
    SetPseudoIO;
    Buf := '';
    StdoutBuf := ret;
    argc := args.count;
    SetLength(argv, argc);
    for i := 0 to argc-1 do argv[i] := PChar(args[i]);
    ruby_set_argv(argc, argv);
    result := rb_eval_string_protect(PChar(src.text), state);
    if state <> 0 then PseudoIO_write(vPseudoIO, ap_errinfo);
  finally
    RemovePseudoIO;
  end;
end;

end.
