Ofort interpreter for Fortran

I forked the new CodeBench project (compiler tools for Mac and iPad), extracted just the Fortran interpreter, and increased its coverage of Fortran using Codex, now covering much of Fortran 95 and parts of later standards. The interpreter is written in C and can be compiled with gcc or clang. It’s called ofort because in the original project it was named the OfflinAi Fortran Interpreter. It can be run interactively or with ofort foo.f90 but does not produce stand-alone executables. There is much of Fortran remaining to be covered, but I think it is already interesting to play with.

A sample session is

c:\c\public_domain\github\ofort>ofort
Enter Fortran source.
Commands: . runs, .run [n] [-- args] repeats, .time [n] [-- args] times, .runq [n] [-- args] runs and quits, .quit quits, .clear clears, .del n[:m] deletes lines, .ins n text inserts, .rep n text replaces, .rename old new renames, .list lists, .decl lists declarations, .vars [names] lists values, .info [names] lists details, .shapes [names] lists array shapes, .sizes [names] lists array sizes, .stats [names] lists array stats, .load file loads, .load-run file loads/runs.

ofort> integer, parameter :: n = 10**7
ofort> integer, parameter :: dp = kind(1.0d0)
ofort> real(kind=dp) :: x(n)
ofort> call random_number(x)
ofort> print*,sum(x)/size(x)
ofort> .time 5
0.5000781
0.5001794
0.4998747
0.4998919
0.4998335

   total          avg           sd          min          max
6.210490     1.242098     0.070298     1.176757     1.361660 s

ofort> .shapes
x: 10000000
ofort> q
Saved main.f90
3 Likes

Does the above take 1s to execute?

In LFortran everything is instant:

$ lfortran
Interactive Fortran. Experimental prototype, not ready for end users.
LFortran version: 0.63.0-186-gf083bf364
  * Use Ctrl-D to exit
  * Use Enter to submit
  * Use Alt-Enter or Ctrl-N to make a new line
    - Editing (Keys: Left, Right, Home, End, Backspace, Delete)
    - History (Keys: Up, Down)
>>> integer, parameter :: n = 10**7                                      1,32  ]
>>> integer, parameter :: dp = kind(1.0d0)                               1,39  ]
>>> real(kind=dp) :: x(n)                                                1,22  ]
>>> call random_number(x)                                                1,22  ]
>>> print*,sum(x)/size(x)                                                1,22  ]
0.50001864952994668
1 Like

Yes, more than a second. It’s an interpreter, and I have not tried to optimize speed. For smaller problems the overall time taken by ofort can be lower than the time taken to compile and run with gfortran. For the program

integer, parameter :: n = 10**4
integer, parameter :: dp = kind(1.0d0)
real(kind=dp) :: x(n)
call random_number(x)
print*,sum(x)/size(x)
end

on my PC the run time is 0.1s with ofort and the compile time is about 0.3s with gfortran or ifx. A compilable source file is created at the end of an interactive session.

Ofort now has a --fast option, so .times 5 for the program above gives

total avg sd min max

0.222714 0.044543 0.002006 0.042517 0.047595 s

and it is faster than gfortran and ifx for some microbenchmarks, with the codes here. Without a compilation step it gives instant results for moderate size problems. The coverage of Fortran has improved using the Fujitsu test suite. Ofort does not support coarrays or full runtime polymorphism, including select type, but it does support type-bound procedures. There is a simple cross-platform (I hope, only tested on Windows) GUI using Python that runs ofort and also gfortran, ifx, and LFortran. It only handles a single source file.

Codex 5.5 quickly fixes bugs, which I hope people will submit.

1 Like

So you can rename it “sofort” now :sweat_smile:.

Do you plan to provide a Fortran binding? Now Ofort can be used as a repl but if you statically link it to a Fortran code one could create an ‘eval’ function or some kind of lambdas.

Good idea – added a Fortran binding, as detailed in the README. One can now a run a program

program demo_eval
use ofort_binding, only: ofort_interpreter
implicit none
type(ofort_interpreter) :: interp
integer :: rc
real(8) :: y

call interp%create()
rc = interp%execute(“integer :: n = 2; print*,n**5”)
call check()
write (*, ‘(a)’, advance=‘no’) interp%output()

call interp%reset()
call interp%set_trace_assign(.true.)
rc = interp%execute(“integer :: x; x = 7 * 8”)
call check()
write (*, ‘(a)’, advance=‘no’) interp%warnings()

call interp%reset()
rc = interp%execute("real function f(x); real :: x; f = xx + 1; end")
call check()
y = interp%call_real1(“f”, 3.0d0)
write (, ‘(g0)’) y
call interp%destroy()

contains
subroutine check()
if (rc /= 0) then
write (*, ‘(a)’) trim(interp%error())
stop 1
end if
end subroutine check
end program demo_eval

and get output

32
56
10.000000000000000

1 Like