2014-08-07 01:07:04 +00:00
/*
Copyright ( c ) 2014 Microsoft Corporation . All rights reserved .
Released under Apache 2.0 license as described in the file LICENSE .
Author : Leonardo de Moura
*/
# include <string>
2014-08-07 01:10:33 +00:00
# include <functional>
2014-09-03 08:35:04 +00:00
# include <limits>
2014-09-06 00:50:55 +00:00
# include <algorithm>
# include <vector>
2014-08-11 02:57:24 +00:00
# include "util/sstream.h"
2014-08-30 00:46:17 +00:00
# include "util/exception.h"
2014-08-29 19:59:22 +00:00
# include "util/sexpr/option_declarations.h"
2014-09-05 22:14:05 +00:00
# include "util/bitap_fuzzy_search.h"
2014-09-03 08:35:04 +00:00
# include "kernel/instantiate.h"
2014-09-02 19:57:20 +00:00
# include "library/aliases.h"
2014-09-03 08:35:04 +00:00
# include "library/type_util.h"
# include "library/unifier.h"
# include "library/opaque_hints.h"
2014-09-05 07:40:34 +00:00
# include "library/scoped_ext.h"
2014-09-03 08:35:04 +00:00
# include "library/tactic/goal.h"
2014-08-07 01:07:04 +00:00
# include "frontends/lean/server.h"
# include "frontends/lean/parser.h"
2014-08-30 00:46:17 +00:00
// #define LEAN_SERVER_DIAGNOSTIC
# if defined(LEAN_SERVER_DIAGNOSTIC)
# define DIAG(CODE) CODE
# else
# define DIAG(CODE)
# endif
2014-09-06 20:17:26 +00:00
# define LEAN_FUZZY_MAX_ERRORS 3
# define LEAN_FUZZY_MAX_ERRORS_FACTOR 3
# define LEAN_FIND_CONSUME_IMPLICIT true // lean will add metavariables for implicit arguments when printing the type of declarations in FINDP and FINDG
2014-09-09 17:36:41 +00:00
# define LEAN_FINDG_MAX_STEPS 128 // maximum number of steps per unification problem
2014-09-06 20:17:26 +00:00
2014-08-07 01:07:04 +00:00
namespace lean {
2014-08-27 21:53:16 +00:00
server : : file : : file ( std : : istream & in , std : : string const & fname ) : m_fname ( fname ) {
for ( std : : string line ; std : : getline ( in , line ) ; ) {
m_lines . push_back ( line ) ;
}
}
2014-08-07 01:07:04 +00:00
2014-09-03 08:35:04 +00:00
void server : : file : : replace_line ( unsigned line_num , std : : string const & new_line ) {
2014-08-27 21:53:16 +00:00
lock_guard < mutex > lk ( m_lines_mutex ) ;
2014-09-03 08:35:04 +00:00
while ( line_num > = m_lines . size ( ) )
2014-08-11 17:40:18 +00:00
m_lines . push_back ( " " ) ;
2014-09-05 08:36:17 +00:00
#if 0
2014-09-03 08:35:04 +00:00
std : : string const & old_line = m_lines [ line_num ] ;
2014-08-28 04:09:52 +00:00
unsigned i = 0 ;
while ( i < old_line . size ( ) & & i < new_line . size ( ) & & old_line [ i ] = = new_line [ i ] )
i + + ;
2014-09-05 08:36:17 +00:00
# endif
2014-08-28 04:09:52 +00:00
m_info . block_new_info ( true ) ;
2014-09-05 08:36:17 +00:00
#if 0
// It turns out that is not a good idea to try to "keep" some of the information.
// The info_manager will accumulate conflicting information and confuse the frontend.
// Example: open a theorem, delete the proof, then type "and", then continue with "and.elim"
// Now, the info_manager contains the "and" and "and.elim" type.
2014-09-03 08:35:04 +00:00
m_info . invalidate_line_col ( line_num + 1 , i ) ;
2014-09-05 08:36:17 +00:00
# endif
m_info . invalidate_line ( line_num + 1 ) ;
2014-09-03 08:35:04 +00:00
m_lines [ line_num ] = new_line ;
2014-08-07 01:07:04 +00:00
}
2014-09-03 08:35:04 +00:00
void server : : file : : insert_line ( unsigned line_num , std : : string const & new_line ) {
2014-08-27 21:53:16 +00:00
lock_guard < mutex > lk ( m_lines_mutex ) ;
m_info . block_new_info ( true ) ;
2014-09-03 08:35:04 +00:00
m_info . insert_line ( line_num + 1 ) ;
while ( line_num > = m_lines . size ( ) )
2014-08-11 17:40:18 +00:00
m_lines . push_back ( " " ) ;
m_lines . push_back ( " " ) ;
2014-09-03 08:35:04 +00:00
lean_assert ( m_lines . size ( ) > = line_num + 1 ) ;
2014-08-11 17:40:18 +00:00
unsigned i = m_lines . size ( ) ;
2014-09-03 08:35:04 +00:00
while ( i > line_num ) {
2014-08-11 17:40:18 +00:00
- - i ;
m_lines [ i ] = m_lines [ i - 1 ] ;
}
2014-09-03 08:35:04 +00:00
m_lines [ line_num ] = new_line ;
2014-08-07 01:07:04 +00:00
}
2014-09-03 08:35:04 +00:00
void server : : file : : remove_line ( unsigned line_num ) {
2014-08-27 21:53:16 +00:00
lock_guard < mutex > lk ( m_lines_mutex ) ;
m_info . block_new_info ( true ) ;
2014-09-03 08:35:04 +00:00
m_info . remove_line ( line_num + 1 ) ;
if ( line_num > = m_lines . size ( ) )
2014-08-11 17:40:18 +00:00
return ;
lean_assert ( ! m_lines . empty ( ) ) ;
2014-09-03 08:35:04 +00:00
for ( unsigned i = line_num ; i < m_lines . size ( ) - 1 ; i + + )
2014-08-11 17:40:18 +00:00
m_lines [ i ] = m_lines [ i + 1 ] ;
m_lines . pop_back ( ) ;
2014-08-07 01:07:04 +00:00
}
2014-08-30 01:12:22 +00:00
void server : : file : : show ( std : : ostream & out , bool valid ) {
2014-08-30 00:46:17 +00:00
lock_guard < mutex > lk ( m_lines_mutex ) ;
2014-08-30 01:12:22 +00:00
for ( unsigned i = 0 ; i < m_lines . size ( ) ; i + + ) {
if ( valid ) {
if ( m_info . is_invalidated ( i + 1 ) )
out < < " * " ;
else
out < < " " ;
out < < " " ;
}
2014-08-30 00:46:17 +00:00
out < < m_lines [ i ] < < " \n " ;
2014-08-30 01:12:22 +00:00
}
2014-08-30 00:46:17 +00:00
}
2014-08-07 01:07:04 +00:00
/**
\ brief Return index i < = m_snapshots . size ( ) s . t .
* forall j < i , m_snapshots [ j ] . m_line < line
* forall i < = j < m_snapshots . size ( ) , m_snapshots [ j ] . m_line > = line
*/
2014-09-03 08:35:04 +00:00
unsigned server : : file : : find ( unsigned line_num ) {
2014-08-07 01:07:04 +00:00
unsigned low = 0 ;
unsigned high = m_snapshots . size ( ) ;
while ( true ) {
lean_assert ( low < = high ) ;
if ( low = = high )
return low ;
unsigned mid = low + ( ( high - low ) / 2 ) ;
lean_assert ( low < = mid & & mid < high ) ;
lean_assert ( mid < m_snapshots . size ( ) ) ;
snapshot const & s = m_snapshots [ mid ] ;
2014-09-03 08:35:04 +00:00
if ( s . m_line < line_num ) {
2014-08-07 01:07:04 +00:00
low = mid + 1 ;
} else {
high = mid ;
}
}
}
2014-08-27 21:53:16 +00:00
/** \brief Copy lines [starting_from, m_lines.size()) to block and return the total number of lines */
unsigned server : : file : : copy_to ( std : : string & block , unsigned starting_from ) {
unsigned num_lines = m_lines . size ( ) ;
for ( unsigned j = starting_from ; j < num_lines ; j + + ) {
block + = m_lines [ j ] ;
block + = ' \n ' ;
}
return num_lines ;
2014-08-16 00:24:37 +00:00
}
2014-08-27 21:53:16 +00:00
server : : worker : : worker ( environment const & env , io_state const & ios , definition_cache & cache ) :
m_empty_snapshot ( env , ios . get_options ( ) ) ,
m_cache ( cache ) ,
2014-09-03 08:35:04 +00:00
m_todo_line_num ( 0 ) ,
2014-08-27 21:53:16 +00:00
m_todo_options ( ios . get_options ( ) ) ,
m_terminate ( false ) ,
m_thread ( [ = ] ( ) {
io_state _ios ( ios ) ;
while ( ! m_terminate ) {
file_ptr todo_file ;
2014-09-03 08:35:04 +00:00
unsigned todo_line_num = 0 ;
2014-08-27 21:53:16 +00:00
options todo_options ;
// wait for next task
while ( ! m_terminate ) {
unique_lock < mutex > lk ( m_todo_mutex ) ;
if ( m_todo_file ) {
todo_file = m_todo_file ;
2014-09-03 08:35:04 +00:00
todo_line_num = m_todo_line_num ;
2014-08-27 21:53:16 +00:00
todo_options = m_todo_options ;
break ;
} else {
m_todo_cv . wait ( lk ) ;
}
}
// extract block of code and snapshot from todo_file
reset_interrupt ( ) ;
2014-08-30 00:46:17 +00:00
bool worker_interrupted = false ;
2014-08-27 21:53:16 +00:00
if ( m_terminate )
break ;
2014-08-30 00:46:17 +00:00
DIAG ( std : : cerr < < " processing ' " < < todo_file - > get_fname ( ) < < " ' \n " ; )
2014-08-27 21:53:16 +00:00
std : : string block ;
unsigned num_lines ;
snapshot s ;
{
lean_assert ( todo_file ) ;
lock_guard < mutex > lk ( todo_file - > m_lines_mutex ) ;
2014-09-03 08:35:04 +00:00
unsigned i = todo_file - > find ( todo_line_num ) ;
2014-08-27 21:53:16 +00:00
todo_file - > m_snapshots . resize ( i ) ;
s = i = = 0 ? m_empty_snapshot : todo_file - > m_snapshots [ i - 1 ] ;
lean_assert ( s . m_line > 0 ) ;
2014-08-30 01:12:22 +00:00
todo_file - > m_info . block_new_info ( false ) ;
todo_file - > m_info . set_processed_upto ( s . m_line ) ;
2014-08-27 21:53:16 +00:00
num_lines = todo_file - > copy_to ( block , s . m_line - 1 ) ;
}
if ( m_terminate )
break ;
// parse block of code with respect to snapshot
try {
std : : istringstream strm ( block ) ;
2014-08-30 00:46:17 +00:00
# if defined(LEAN_SERVER_DIAGNOSTIC)
std : : shared_ptr < output_channel > out1 ( new stderr_channel ( ) ) ;
std : : shared_ptr < output_channel > out2 ( new stderr_channel ( ) ) ;
# else
2014-08-27 21:53:16 +00:00
std : : shared_ptr < output_channel > out1 ( new string_output_channel ( ) ) ;
std : : shared_ptr < output_channel > out2 ( new string_output_channel ( ) ) ;
2014-08-30 00:46:17 +00:00
# endif
2014-08-27 21:53:16 +00:00
io_state tmp_ios ( _ios , out1 , out2 ) ;
tmp_ios . set_options ( join ( s . m_options , _ios . get_options ( ) ) ) ;
bool use_exceptions = false ;
unsigned num_threads = 1 ;
parser p ( s . m_env , tmp_ios , strm , todo_file - > m_fname . c_str ( ) , use_exceptions , num_threads ,
2014-09-10 15:39:39 +00:00
& s , & todo_file - > m_snapshots , & todo_file - > m_info ) ;
2014-08-27 21:53:16 +00:00
p . set_cache ( & m_cache ) ;
p ( ) ;
2014-08-30 00:46:17 +00:00
} catch ( interrupted & ) {
worker_interrupted = true ;
} catch ( exception & ex ) {
DIAG ( std : : cerr < < " worker exception: " < < ex . what ( ) < < " \n " ; )
}
if ( ! m_terminate & & ! worker_interrupted ) {
DIAG ( std : : cerr < < " finished ' " < < todo_file - > get_fname ( ) < < " ' \n " ; )
2014-08-27 21:53:16 +00:00
unique_lock < mutex > lk ( m_todo_mutex ) ;
2014-09-03 08:35:04 +00:00
if ( m_todo_file = = todo_file & & m_last_file = = todo_file & & m_todo_line_num = = todo_line_num ) {
m_todo_line_num = num_lines + 1 ;
2014-08-27 21:53:16 +00:00
m_todo_file = nullptr ;
m_todo_cv . notify_all ( ) ;
}
}
}
} ) { }
server : : worker : : ~ worker ( ) {
m_terminate = true ;
request_interrupt ( ) ;
m_thread . join ( ) ;
2014-08-16 00:24:37 +00:00
}
2014-08-27 21:53:16 +00:00
void server : : worker : : request_interrupt ( ) {
m_todo_cv . notify_all ( ) ;
m_thread . request_interrupt ( ) ;
2014-08-16 00:24:37 +00:00
}
2014-09-09 17:28:11 +00:00
bool server : : worker : : wait ( optional < unsigned > const & ms ) {
2014-08-27 21:53:16 +00:00
while ( true ) {
unique_lock < mutex > lk ( m_todo_mutex ) ;
if ( ! m_todo_file )
2014-09-09 17:28:11 +00:00
return true ;
if ( ms ) {
chrono : : milliseconds d ( * ms ) ;
m_todo_cv . wait_for ( lk , d ) ;
if ( ! m_todo_file )
return true ;
return false ;
} else {
m_todo_cv . wait ( lk ) ;
}
2014-08-16 00:24:37 +00:00
}
2014-08-11 17:40:18 +00:00
}
2014-09-03 08:35:04 +00:00
void server : : worker : : set_todo ( file_ptr const & f , unsigned line_num , options const & o ) {
2014-08-27 21:53:16 +00:00
lock_guard < mutex > lk ( m_todo_mutex ) ;
2014-09-03 08:35:04 +00:00
if ( m_last_file ! = f | | line_num < m_todo_line_num )
m_todo_line_num = line_num ;
2014-08-27 21:53:16 +00:00
m_todo_file = f ;
m_last_file = f ;
m_todo_options = o ;
m_todo_cv . notify_all ( ) ;
}
server : : server ( environment const & env , io_state const & ios , unsigned num_threads ) :
m_env ( env ) , m_ios ( ios ) , m_out ( ios . get_regular_channel ( ) . get_stream ( ) ) ,
m_num_threads ( num_threads ) , m_empty_snapshot ( m_env , m_ios . get_options ( ) ) ,
m_worker ( env , ios , m_cache ) {
2014-09-04 19:42:19 +00:00
# if !defined(LEAN_MULTI_THREAD)
lean_unreachable ( ) ;
# endif
2014-08-27 21:53:16 +00:00
}
server : : ~ server ( ) {
}
void server : : interrupt_worker ( ) {
m_worker . request_interrupt ( ) ;
}
2014-08-11 17:40:18 +00:00
static std : : string g_load ( " LOAD " ) ;
static std : : string g_visit ( " VISIT " ) ;
static std : : string g_replace ( " REPLACE " ) ;
static std : : string g_insert ( " INSERT " ) ;
static std : : string g_remove ( " REMOVE " ) ;
static std : : string g_info ( " INFO " ) ;
2014-08-14 21:40:46 +00:00
static std : : string g_set ( " SET " ) ;
2014-08-14 23:08:43 +00:00
static std : : string g_eval ( " EVAL " ) ;
2014-08-17 19:14:42 +00:00
static std : : string g_wait ( " WAIT " ) ;
2014-08-27 17:31:01 +00:00
static std : : string g_clear_cache ( " CLEAR_CACHE " ) ;
2014-08-28 14:40:02 +00:00
static std : : string g_echo ( " ECHO " ) ;
2014-08-29 19:59:22 +00:00
static std : : string g_options ( " OPTIONS " ) ;
2014-08-30 00:46:17 +00:00
static std : : string g_show ( " SHOW " ) ;
2014-08-30 01:12:22 +00:00
static std : : string g_valid ( " VALID " ) ;
2014-08-30 00:46:17 +00:00
static std : : string g_sleep ( " SLEEP " ) ;
2014-09-02 19:57:20 +00:00
static std : : string g_findp ( " FINDP " ) ;
2014-09-03 08:35:04 +00:00
static std : : string g_findg ( " FINDG " ) ;
2014-08-11 17:40:18 +00:00
static bool is_command ( std : : string const & cmd , std : : string const & line ) {
return line . compare ( 0 , cmd . size ( ) , cmd ) = = 0 ;
}
static std : : string & ltrim ( std : : string & s ) {
s . erase ( s . begin ( ) , std : : find_if ( s . begin ( ) , s . end ( ) , std : : not1 ( std : : ptr_fun < int , int > ( std : : isspace ) ) ) ) ;
return s ;
}
static std : : string & rtrim ( std : : string & s ) {
s . erase ( std : : find_if ( s . rbegin ( ) , s . rend ( ) , std : : not1 ( std : : ptr_fun < int , int > ( std : : isspace ) ) ) . base ( ) , s . end ( ) ) ;
return s ;
}
static std : : string & trim ( std : : string & s ) {
return ltrim ( rtrim ( s ) ) ;
}
2014-09-03 08:35:04 +00:00
void server : : process_from ( unsigned line_num ) {
m_worker . set_todo ( m_file , line_num , m_ios . get_options ( ) ) ;
2014-08-11 02:57:24 +00:00
}
2014-09-06 19:34:20 +00:00
void server : : load_file ( std : : string const & fname , bool error_if_nofile ) {
2014-08-27 21:53:16 +00:00
interrupt_worker ( ) ;
2014-08-07 01:07:04 +00:00
std : : ifstream in ( fname ) ;
if ( in . bad ( ) | | in . fail ( ) ) {
2014-09-06 19:34:20 +00:00
if ( error_if_nofile ) {
m_out < < " -- ERROR failed to open file ' " < < fname < < " ' " < < std : : endl ;
} else {
m_file . reset ( new file ( in , fname ) ) ;
2014-09-12 23:47:56 +00:00
m_file_map . erase ( fname ) ;
2014-09-06 19:34:20 +00:00
m_file_map . insert ( mk_pair ( fname , m_file ) ) ;
}
2014-08-07 01:07:04 +00:00
} else {
2014-09-12 23:47:56 +00:00
m_cache . clear ( ) ;
2014-08-27 21:53:16 +00:00
m_file . reset ( new file ( in , fname ) ) ;
2014-09-12 23:47:56 +00:00
m_file_map . erase ( fname ) ;
2014-08-11 17:40:18 +00:00
m_file_map . insert ( mk_pair ( fname , m_file ) ) ;
2014-08-16 00:24:37 +00:00
process_from ( 0 ) ;
2014-08-11 02:57:24 +00:00
}
}
2014-08-11 17:40:18 +00:00
void server : : visit_file ( std : : string const & fname ) {
2014-08-27 21:53:16 +00:00
interrupt_worker ( ) ;
2014-08-11 17:40:18 +00:00
auto it = m_file_map . find ( fname ) ;
2014-08-16 00:24:37 +00:00
if ( it = = m_file_map . end ( ) ) {
2014-09-06 19:34:20 +00:00
bool error_if_nofile = false ;
load_file ( fname , error_if_nofile ) ;
2014-08-16 00:24:37 +00:00
} else {
2014-08-11 17:40:18 +00:00
m_file = it - > second ;
2014-08-16 00:24:37 +00:00
process_from ( 0 ) ;
}
2014-08-07 01:07:04 +00:00
}
2014-08-11 02:57:24 +00:00
void server : : read_line ( std : : istream & in , std : : string & line ) {
if ( ! std : : getline ( in , line ) )
throw exception ( " unexpected end of input " ) ;
}
2014-09-03 08:35:04 +00:00
void consume_spaces ( std : : string const & data , unsigned & i ) {
while ( i < data . size ( ) & & std : : isspace ( data [ i ] ) ) {
i + + ;
}
}
unsigned consume_num ( std : : string const & data , unsigned & i ) {
unsigned sz = data . size ( ) ;
if ( i > = sz | | ! std : : isdigit ( data [ i ] ) )
throw exception ( " numeral expected " ) ;
unsigned long long r = 0 ;
while ( i < sz & & std : : isdigit ( data [ i ] ) ) {
r = r * 10 + ( data [ i ] - ' 0 ' ) ;
if ( r > std : : numeric_limits < unsigned > : : max ( ) )
throw exception ( " numeral is too big to fit in a 32-bit machine unsigned integer " ) ;
i + + ;
}
return r ;
}
2014-09-09 17:28:11 +00:00
optional < unsigned > server : : get_optional_num ( std : : string const & line , std : : string const & cmd ) {
std : : string data = line . substr ( cmd . size ( ) ) ;
unsigned i = 0 ;
consume_spaces ( data , i ) ;
if ( i = = data . size ( ) )
return optional < unsigned > ( ) ;
unsigned r = consume_num ( data , i ) ;
return optional < unsigned > ( r ) ;
}
2014-09-03 08:35:04 +00:00
void check_line_num ( unsigned line_num ) {
if ( line_num = = 0 )
2014-08-11 02:57:24 +00:00
throw exception ( " line numbers are indexed from 1 " ) ;
2014-09-03 08:35:04 +00:00
}
// Given a line of the form "cmd line_num", return the line_num
unsigned server : : get_line_num ( std : : string const & line , std : : string const & cmd ) {
std : : string data = line . substr ( cmd . size ( ) ) ;
unsigned i = 0 ;
consume_spaces ( data , i ) ;
unsigned r = consume_num ( data , i ) ;
check_line_num ( r ) ;
2014-08-11 02:57:24 +00:00
return r ;
}
2014-09-04 21:02:53 +00:00
pair < unsigned , optional < unsigned > > server : : get_line_opt_col_num ( std : : string const & line , std : : string const & cmd ) {
2014-09-03 08:35:04 +00:00
std : : string data = line . substr ( cmd . size ( ) ) ;
unsigned i = 0 ;
consume_spaces ( data , i ) ;
unsigned line_num = consume_num ( data , i ) ;
check_line_num ( line_num ) ;
consume_spaces ( data , i ) ;
2014-09-04 21:02:53 +00:00
if ( i = = data . size ( ) )
return mk_pair ( line_num , optional < unsigned > ( ) ) ;
2014-09-03 08:35:04 +00:00
unsigned colnum = consume_num ( data , i ) ;
2014-09-04 21:02:53 +00:00
return mk_pair ( line_num , optional < unsigned > ( colnum ) ) ;
}
pair < unsigned , unsigned > server : : get_line_col_num ( std : : string const & line , std : : string const & cmd ) {
auto r = get_line_opt_col_num ( line , cmd ) ;
if ( r . second )
return mk_pair ( r . first , * r . second ) ;
else
return mk_pair ( r . first , 0 ) ;
2014-09-03 08:35:04 +00:00
}
2014-08-11 17:40:18 +00:00
void server : : check_file ( ) {
if ( ! m_file )
throw exception ( " no file has been loaded/visited " ) ;
}
2014-09-03 08:35:04 +00:00
void server : : replace_line ( unsigned line_num , std : : string const & new_line ) {
2014-08-27 21:53:16 +00:00
interrupt_worker ( ) ;
2014-08-11 17:40:18 +00:00
check_file ( ) ;
2014-09-03 08:35:04 +00:00
m_file - > replace_line ( line_num , new_line ) ;
process_from ( line_num ) ;
2014-08-11 17:40:18 +00:00
}
2014-09-03 08:35:04 +00:00
void server : : insert_line ( unsigned line_num , std : : string const & new_line ) {
2014-08-27 21:53:16 +00:00
interrupt_worker ( ) ;
2014-08-11 17:40:18 +00:00
check_file ( ) ;
2014-09-03 08:35:04 +00:00
m_file - > insert_line ( line_num , new_line ) ;
process_from ( line_num ) ;
2014-08-11 17:40:18 +00:00
}
2014-09-03 08:35:04 +00:00
void server : : remove_line ( unsigned line_num ) {
2014-08-27 21:53:16 +00:00
interrupt_worker ( ) ;
2014-08-11 17:40:18 +00:00
check_file ( ) ;
2014-09-03 08:35:04 +00:00
m_file - > remove_line ( line_num ) ;
process_from ( line_num ) ;
2014-08-11 17:40:18 +00:00
}
2014-08-14 21:40:46 +00:00
void server : : set_option ( std : : string const & line ) {
std : : string cmd = " set_option " ;
cmd + = line ;
std : : istringstream strm ( cmd ) ;
m_out < < " -- BEGINSET " < < std : : endl ;
try {
parser p ( m_env , m_ios , strm , " SET_command " , true ) ;
p ( ) ;
m_ios . set_options ( p . ios ( ) . get_options ( ) ) ;
} catch ( exception & ex ) {
m_out < < ex . what ( ) < < std : : endl ;
}
m_out < < " -- ENDSET " < < std : : endl ;
}
2014-09-04 21:02:53 +00:00
void server : : show_info ( unsigned line_num , optional < unsigned > const & col_num ) {
2014-08-21 23:49:21 +00:00
check_file ( ) ;
2014-09-03 02:04:01 +00:00
m_out < < " -- BEGININFO " ;
2014-09-03 08:35:04 +00:00
if ( m_file - > infom ( ) . is_invalidated ( line_num ) )
2014-09-03 02:04:01 +00:00
m_out < < " STALE " ;
2014-09-03 08:35:04 +00:00
if ( line_num > = m_file - > infom ( ) . get_processed_upto ( ) )
2014-09-03 02:04:01 +00:00
m_out < < " NAY " ;
m_out < < std : : endl ;
2014-09-04 21:02:53 +00:00
m_file - > infom ( ) . display ( m_env , m_ios , line_num , col_num ) ;
2014-08-27 01:06:37 +00:00
m_out < < " -- ENDINFO " < < std : : endl ;
2014-08-16 00:24:37 +00:00
}
2014-08-27 01:06:37 +00:00
void server : : eval_core ( environment const & env , options const & o , std : : string const & line ) {
2014-08-14 23:08:43 +00:00
std : : istringstream strm ( line ) ;
2014-08-27 17:46:49 +00:00
io_state ios ( m_ios , o ) ;
2014-08-14 23:08:43 +00:00
m_out < < " -- BEGINEVAL " < < std : : endl ;
try {
2014-08-27 17:46:49 +00:00
parser p ( env , ios , strm , " EVAL_command " , true ) ;
2014-08-14 23:08:43 +00:00
p ( ) ;
} catch ( exception & ex ) {
m_out < < ex . what ( ) < < std : : endl ;
}
m_out < < " -- ENDEVAL " < < std : : endl ;
}
2014-08-27 01:06:37 +00:00
void server : : eval ( std : : string const & line ) {
if ( ! m_file ) {
eval_core ( m_env , m_ios . get_options ( ) , line ) ;
2014-08-27 21:53:16 +00:00
} else if ( auto p = m_file - > infom ( ) . get_final_env_opts ( ) ) {
2014-08-27 17:46:49 +00:00
eval_core ( p - > first , join ( p - > second , m_ios . get_options ( ) ) , line ) ;
2014-08-27 01:06:37 +00:00
} else {
eval_core ( m_env , m_ios . get_options ( ) , line ) ;
}
}
2014-08-29 19:59:22 +00:00
void server : : show_options ( ) {
m_out < < " -- BEGINOPTIONS " < < std : : endl ;
2014-09-03 00:29:49 +00:00
options const & o = m_ios . get_options ( ) ;
2014-08-29 19:59:22 +00:00
option_declarations const & decls = get_option_declarations ( ) ;
for ( auto it = decls . begin ( ) ; it ! = decls . end ( ) ; it + + ) {
option_declaration const & d = it - > second ;
2014-09-03 00:29:49 +00:00
m_out < < " -- " < < d . get_name ( ) < < " | " < < d . kind ( ) < < " | " ;
2014-09-03 00:42:21 +00:00
d . display_value ( m_out , o ) ;
2014-09-03 00:29:49 +00:00
m_out < < " | " < < d . get_description ( ) < < " \n " ;
2014-08-29 19:59:22 +00:00
}
m_out < < " -- ENDOPTIONS " < < std : : endl ;
}
2014-08-30 01:12:22 +00:00
void server : : show ( bool valid ) {
2014-08-30 00:46:17 +00:00
check_file ( ) ;
m_out < < " -- BEGINSHOW " < < std : : endl ;
2014-08-30 01:12:22 +00:00
m_file - > show ( m_out , valid ) ;
2014-08-30 00:46:17 +00:00
m_out < < " -- ENDSHOW " < < std : : endl ;
}
2014-09-02 19:57:20 +00:00
void server : : display_decl ( name const & short_name , name const & long_name , environment const & env , options const & o ) {
declaration const & d = env . get ( long_name ) ;
io_state_stream out = regular ( env , m_ios ) . update_options ( o ) ;
2014-09-06 20:17:26 +00:00
expr type = d . get_type ( ) ;
if ( LEAN_FIND_CONSUME_IMPLICIT ) {
while ( true ) {
if ( ! is_pi ( type ) )
break ;
if ( ! binding_info ( type ) . is_implicit ( ) )
break ;
2014-09-07 16:36:03 +00:00
std : : string q ( " ? " ) ;
q + = binding_name ( type ) . to_string ( ) ;
expr m = mk_constant ( name ( q . c_str ( ) ) ) ;
2014-09-06 20:17:26 +00:00
type = instantiate ( binding_body ( type ) , m ) ;
}
}
out < < short_name < < " | " < < mk_pair ( flatten ( out . get_formatter ( ) ( type ) ) , o ) < < " \n " ;
2014-09-02 19:57:20 +00:00
}
2014-09-06 04:57:35 +00:00
optional < name > is_uniquely_aliased ( environment const & env , name const & n ) {
if ( auto it = is_expr_aliased ( env , n ) )
if ( length ( get_expr_aliases ( env , * it ) ) = = 1 )
return it ;
return optional < name > ( ) ;
}
/** \brief Return an (atomic) name if \c n can be referenced by this atomic
name in the given environment . */
optional < name > is_essentially_atomic ( environment const & env , name const & n ) {
if ( n . is_atomic ( ) )
return optional < name > ( n ) ;
list < name > const & ns_list = get_namespaces ( env ) ;
for ( name const & ns : ns_list ) {
if ( is_prefix_of ( ns , n ) ) {
auto n_prime = n . replace_prefix ( ns , name ( ) ) ;
if ( n_prime . is_atomic ( ) )
return optional < name > ( n_prime ) ;
break ;
}
}
if ( auto it = is_uniquely_aliased ( env , n ) )
if ( it - > is_atomic ( ) )
return it ;
return optional < name > ( ) ;
}
2014-09-06 00:50:55 +00:00
void server : : display_decl ( name const & d , environment const & env , options const & o ) {
// using namespace override resolution rule
list < name > const & ns_list = get_namespaces ( env ) ;
for ( name const & ns : ns_list ) {
2014-09-15 17:07:49 +00:00
if ( is_prefix_of ( ns , d ) & & ns ! = d ) {
2014-09-06 00:50:55 +00:00
display_decl ( d . replace_prefix ( ns , name ( ) ) , d , env , o ) ;
return ;
}
}
// if the alias is unique use it
2014-09-06 04:57:35 +00:00
if ( auto it = is_uniquely_aliased ( env , d ) ) {
display_decl ( * it , d , env , o ) ;
2014-09-06 00:50:55 +00:00
} else {
display_decl ( d , d , env , o ) ;
}
}
2014-09-05 22:14:05 +00:00
unsigned get_fuzzy_match_max_errors ( unsigned prefix_sz ) {
unsigned r = ( prefix_sz / LEAN_FUZZY_MAX_ERRORS_FACTOR ) ;
if ( r > LEAN_FUZZY_MAX_ERRORS )
return LEAN_FUZZY_MAX_ERRORS ;
return r ;
}
2014-09-08 15:04:04 +00:00
optional < name > exact_prefix_match ( environment const & env , std : : string const & pattern , declaration const & d ) {
if ( auto it = is_essentially_atomic ( env , d . get_name ( ) ) ) {
std : : string it_str = it - > to_string ( ) ;
// if pattern "perfectly" matches beginning of declaration name, we just display d on the top of the list
if ( it_str . compare ( 0 , pattern . size ( ) , pattern ) = = 0 )
return it ;
} else {
std : : string d_str = d . get_name ( ) . to_string ( ) ;
if ( d_str . compare ( 0 , pattern . size ( ) , pattern ) = = 0 )
return optional < name > ( d . get_name ( ) ) ;
}
return optional < name > ( ) ;
}
2014-09-05 22:14:05 +00:00
void server : : find_pattern ( unsigned line_num , std : : string const & pattern ) {
2014-09-02 19:57:20 +00:00
check_file ( ) ;
m_out < < " -- BEGINFINDP " ;
unsigned upto = m_file - > infom ( ) . get_processed_upto ( ) ;
2014-09-03 08:35:04 +00:00
optional < pair < environment , options > > env_opts = m_file - > infom ( ) . get_closest_env_opts ( line_num ) ;
2014-09-02 19:57:20 +00:00
if ( ! env_opts ) {
m_out < < " NAY " < < std : : endl ;
m_out < < " -- ENDFINDP " < < std : : endl ;
return ;
}
2014-09-03 08:35:04 +00:00
if ( upto < line_num )
2014-09-02 19:57:20 +00:00
m_out < < " STALE " ;
environment const & env = env_opts - > first ;
options opts = env_opts - > second ;
2014-09-06 03:59:14 +00:00
token_table const & tt = get_token_table ( env ) ;
if ( is_token ( tt , pattern . c_str ( ) ) ) {
// we ignore patterns that match commands, keywords, and tokens.
m_out < < " \n -- ENDFINDP " < < std : : endl ;
return ;
}
2014-09-02 19:57:20 +00:00
opts = join ( opts , m_ios . get_options ( ) ) ;
m_out < < std : : endl ;
2014-09-05 22:14:05 +00:00
unsigned max_errors = get_fuzzy_match_max_errors ( pattern . size ( ) ) ;
2014-09-06 00:50:55 +00:00
std : : vector < pair < std : : string , name > > selected ;
2014-09-05 22:14:05 +00:00
bitap_fuzzy_search matcher ( pattern , max_errors ) ;
2014-09-02 19:57:20 +00:00
env . for_each_declaration ( [ & ] ( declaration const & d ) {
2014-09-08 15:04:04 +00:00
if ( auto it = exact_prefix_match ( env , pattern , d ) ) {
display_decl ( * it , d . get_name ( ) , env , opts ) ;
} else {
std : : string text = d . get_name ( ) . to_string ( ) ;
if ( matcher . match ( text ) )
selected . emplace_back ( text , d . get_name ( ) ) ;
2014-09-06 04:57:35 +00:00
}
2014-09-06 00:50:55 +00:00
} ) ;
unsigned sz = selected . size ( ) ;
if ( sz = = 1 ) {
display_decl ( selected [ 0 ] . second , env , opts ) ;
} else if ( sz > 1 ) {
std : : vector < pair < std : : string , name > > next_selected ;
for ( unsigned k = 0 ; k < = max_errors ; k + + ) {
bitap_fuzzy_search matcher ( pattern , k ) ;
for ( auto const & s : selected ) {
if ( matcher . match ( s . first ) ) {
display_decl ( s . second , env , opts ) ;
} else {
next_selected . push_back ( s ) ;
2014-09-05 07:40:34 +00:00
}
}
2014-09-06 00:50:55 +00:00
std : : swap ( selected , next_selected ) ;
next_selected . clear ( ) ;
}
}
2014-09-02 19:57:20 +00:00
m_out < < " -- ENDFINDP " < < std : : endl ;
}
2014-09-03 08:35:04 +00:00
void consume_pos_neg_strs ( std : : string const & filters , buffer < std : : string > & pos_names , buffer < std : : string > & neg_names ) {
unsigned i = 0 ;
unsigned sz = filters . size ( ) ;
std : : string val ;
while ( true ) {
consume_spaces ( filters , i ) ;
if ( i = = sz )
return ;
if ( filters [ i ] = = ' + ' | | filters [ i ] = = ' - ' ) {
bool pos = filters [ i ] = = ' + ' ;
i + + ;
val . clear ( ) ;
while ( i < sz & & ! std : : isspace ( filters [ i ] ) ) {
val + = filters [ i ] ;
i + + ;
}
if ( val . empty ( ) )
throw exception ( " invalid empty filter " ) ;
name n = string_to_name ( val ) ;
if ( ! n . is_atomic ( ) )
throw exception ( " invalid filter, atomic name expected " ) ;
if ( pos )
pos_names . push_back ( n . to_string ( ) ) ;
else
neg_names . push_back ( n . to_string ( ) ) ;
} else {
throw exception ( " invalid filter, '+' or '-' expected " ) ;
}
}
}
bool is_part_of ( std : : string const & p , name n ) {
while ( true ) {
if ( n . is_string ( ) ) {
std : : string s ( n . get_string ( ) ) ;
if ( s . find ( p ) ! = std : : string : : npos )
return true ;
}
if ( n . is_atomic ( ) | | n . is_anonymous ( ) )
return false ;
n = n . get_prefix ( ) ;
}
}
bool match_type ( type_checker & tc , expr const & meta , expr const & expected_type , declaration const & d ) {
name_generator ngen = tc . mk_ngen ( ) ;
goal g ( meta , expected_type ) ;
buffer < level > ls ;
unsigned num_ls = length ( d . get_univ_params ( ) ) ;
for ( unsigned i = 0 ; i < num_ls ; i + + )
ls . push_back ( mk_meta_univ ( ngen . next ( ) ) ) ;
expr dt = instantiate_type_univ_params ( d , to_list ( ls . begin ( ) , ls . end ( ) ) ) ;
unsigned num_e = get_expect_num_args ( tc , expected_type ) ;
unsigned num_d = get_expect_num_args ( tc , dt ) ;
if ( num_e > num_d )
return false ;
for ( unsigned i = 0 ; i < num_d - num_e ; i + + ) {
dt = tc . whnf ( dt ) . first ;
expr meta = g . mk_meta ( ngen . next ( ) , binding_domain ( dt ) ) ;
dt = instantiate ( binding_body ( dt ) , meta ) ;
}
// Remark: we ignore declarations where the resultant type is of the form
// (?M ...) because they unify with almost everything. They produce a lot of noise in the output.
// Perhaps we should have an option to enable/disable this kind of declaration. For now, we
// just ingore them.
if ( is_meta ( dt ) )
return false ; // matches anything
2014-09-09 17:36:41 +00:00
try {
unifier_config cfg ;
cfg . m_max_steps = LEAN_FINDG_MAX_STEPS ;
auto r = unify ( tc . env ( ) , dt , expected_type , tc . mk_ngen ( ) , true , substitution ( ) , cfg ) ;
return static_cast < bool > ( r . pull ( ) ) ;
} catch ( exception & ) {
return false ;
}
2014-09-03 08:35:04 +00:00
}
static name g_tmp_prefix = name : : mk_internal_unique_name ( ) ;
void server : : find_goal_matches ( unsigned line_num , unsigned col_num , std : : string const & filters ) {
buffer < std : : string > pos_names , neg_names ;
consume_pos_neg_strs ( filters , pos_names , neg_names ) ;
m_out < < " -- BEGINFINDG " ;
optional < pair < environment , options > > env_opts = m_file - > infom ( ) . get_closest_env_opts ( line_num ) ;
if ( ! env_opts ) {
m_out < < " NAY " < < std : : endl ;
m_out < < " -- ENDFINDG " < < std : : endl ;
return ;
}
if ( line_num > = m_file - > infom ( ) . get_processed_upto ( ) )
m_out < < " NAY " ;
m_out < < std : : endl ;
environment const & env = env_opts - > first ;
options const & opts = env_opts - > second ;
name_generator ngen ( g_tmp_prefix ) ;
std : : unique_ptr < type_checker > tc = mk_type_checker_with_hints ( env , ngen , true ) ;
if ( auto meta = m_file - > infom ( ) . get_meta_at ( line_num , col_num ) ) {
if ( is_meta ( * meta ) ) {
if ( auto type = m_file - > infom ( ) . get_type_at ( line_num , col_num ) ) {
env . for_each_declaration ( [ & ] ( declaration const & d ) {
if ( std : : all_of ( pos_names . begin ( ) , pos_names . end ( ) ,
[ & ] ( std : : string const & pos ) { return is_part_of ( pos , d . get_name ( ) ) ; } ) & &
std : : all_of ( neg_names . begin ( ) , neg_names . end ( ) ,
[ & ] ( std : : string const & neg ) { return ! is_part_of ( neg , d . get_name ( ) ) ; } ) & &
match_type ( * tc . get ( ) , * meta , * type , d ) ) {
2014-09-03 16:43:57 +00:00
if ( optional < name > alias = is_expr_aliased ( env , d . get_name ( ) ) )
display_decl ( * alias , d . get_name ( ) , env , opts ) ;
else
display_decl ( d . get_name ( ) , d . get_name ( ) , env , opts ) ;
2014-09-03 08:35:04 +00:00
}
} ) ;
} } }
m_out < < " -- ENDFINDG " < < std : : endl ;
}
2014-09-09 17:28:11 +00:00
void server : : wait ( optional < unsigned > ms ) {
m_out < < " -- BEGINWAIT " < < std : : endl ;
if ( ! m_worker . wait ( ms ) )
m_out < < " -- INTERRUPTED \n " ;
m_out < < " -- ENDWAIT " < < std : : endl ;
}
2014-08-07 01:07:04 +00:00
bool server : : operator ( ) ( std : : istream & in ) {
for ( std : : string line ; std : : getline ( in , line ) ; ) {
2014-08-11 02:57:24 +00:00
try {
2014-08-11 17:40:18 +00:00
if ( is_command ( g_load , line ) ) {
std : : string fname = line . substr ( g_load . size ( ) ) ;
trim ( fname ) ;
load_file ( fname ) ;
} else if ( is_command ( g_visit , line ) ) {
std : : string fname = line . substr ( g_visit . size ( ) ) ;
2014-08-11 02:57:24 +00:00
trim ( fname ) ;
2014-08-11 17:40:18 +00:00
visit_file ( fname ) ;
2014-08-28 14:40:02 +00:00
} else if ( is_command ( g_echo , line ) ) {
std : : string str = line . substr ( g_echo . size ( ) ) ;
m_out < < " -- " < < str < < " \n " ;
2014-08-11 02:57:24 +00:00
} else if ( is_command ( g_replace , line ) ) {
2014-09-03 08:35:04 +00:00
unsigned line_num = get_line_num ( line , g_replace ) ;
2014-08-11 02:57:24 +00:00
read_line ( in , line ) ;
2014-09-03 08:35:04 +00:00
replace_line ( line_num - 1 , line ) ;
2014-08-11 02:57:24 +00:00
} else if ( is_command ( g_insert , line ) ) {
2014-09-03 08:35:04 +00:00
unsigned line_num = get_line_num ( line , g_insert ) ;
2014-08-11 02:57:24 +00:00
read_line ( in , line ) ;
2014-09-03 08:35:04 +00:00
insert_line ( line_num - 1 , line ) ;
2014-08-11 02:57:24 +00:00
} else if ( is_command ( g_remove , line ) ) {
2014-09-03 08:35:04 +00:00
unsigned line_num = get_line_num ( line , g_remove ) ;
remove_line ( line_num - 1 ) ;
2014-08-11 02:57:24 +00:00
} else if ( is_command ( g_info , line ) ) {
2014-09-04 21:02:53 +00:00
auto line_col = get_line_opt_col_num ( line , g_info ) ;
show_info ( line_col . first , line_col . second ) ;
2014-08-14 21:40:46 +00:00
} else if ( is_command ( g_set , line ) ) {
read_line ( in , line ) ;
set_option ( line ) ;
2014-08-14 23:08:43 +00:00
} else if ( is_command ( g_eval , line ) ) {
read_line ( in , line ) ;
eval ( line ) ;
2014-08-27 17:31:01 +00:00
} else if ( is_command ( g_clear_cache , line ) ) {
m_cache . clear ( ) ;
2014-08-29 19:59:22 +00:00
} else if ( is_command ( g_options , line ) ) {
show_options ( ) ;
2014-08-17 19:14:42 +00:00
} else if ( is_command ( g_wait , line ) ) {
2014-09-09 17:28:11 +00:00
optional < unsigned > ms = get_optional_num ( line , g_wait ) ;
wait ( ms ) ;
2014-08-30 00:46:17 +00:00
} else if ( is_command ( g_show , line ) ) {
2014-08-30 01:12:22 +00:00
show ( false ) ;
} else if ( is_command ( g_valid , line ) ) {
show ( true ) ;
2014-08-30 00:46:17 +00:00
} else if ( is_command ( g_sleep , line ) ) {
2014-09-03 08:35:04 +00:00
unsigned ms = get_line_num ( line , g_sleep ) ;
2014-08-30 00:46:17 +00:00
chrono : : milliseconds d ( ms ) ;
this_thread : : sleep_for ( d ) ;
2014-09-02 19:57:20 +00:00
} else if ( is_command ( g_findp , line ) ) {
2014-09-03 08:35:04 +00:00
unsigned line_num = get_line_num ( line , g_findp ) ;
read_line ( in , line ) ;
2014-09-05 22:14:05 +00:00
if ( line . size ( ) > 63 )
line . resize ( 63 ) ;
find_pattern ( line_num , line ) ;
2014-09-03 08:35:04 +00:00
} else if ( is_command ( g_findg , line ) ) {
pair < unsigned , unsigned > line_col_num = get_line_col_num ( line , g_findg ) ;
2014-09-02 19:57:20 +00:00
read_line ( in , line ) ;
2014-09-03 08:35:04 +00:00
find_goal_matches ( line_col_num . first , line_col_num . second , line ) ;
2014-08-07 01:07:04 +00:00
} else {
2014-08-11 02:57:24 +00:00
throw exception ( sstream ( ) < < " unexpected command line: " < < line ) ;
2014-08-07 01:07:04 +00:00
}
2014-08-11 02:57:24 +00:00
} catch ( exception & ex ) {
2014-08-11 17:40:18 +00:00
m_out < < " -- ERROR " < < ex . what ( ) < < std : : endl ;
2014-08-07 01:07:04 +00:00
}
}
return true ;
}
}