2020-01-18 23:26:14 +01:00
module builder
2020-04-16 15:35:19 +02:00
import os
2021-02-04 08:09:54 +01:00
import v . token
2020-04-16 15:35:19 +02:00
import v . pref
import v . util
2021-04-02 00:57:09 +02:00
import v . ast
2020-04-16 15:35:19 +02:00
import v . vmod
import v . checker
import v . parser
import v . depgraph
2021-05-06 13:26:10 +02:00
import v . markused
2020-01-18 23:26:14 +01:00
pub struct Builder {
pub :
2021-01-12 04:38:43 +01:00
compiled_dir string // contains os.real_path() of the dir of the final file beeing compiled, or the dir itself when doing `v .`
module_path string
2020-02-03 07:31:54 +01:00
mut :
2021-01-12 04:38:43 +01:00
pref & pref . Preferences
2021-05-22 17:59:17 +02:00
checker & checker . Checker
2021-01-12 04:38:43 +01:00
global_scope & ast . Scope
out_name_c string
out_name_js string
max_nr_errors int = 100
2021-04-08 11:18:02 +02:00
stats_lines int // size of backend generated source code in lines
stats_bytes int // size of backend generated source code in bytes
2020-05-09 18:34:04 +02:00
pub mut :
module_search_paths [ ] string
2021-05-22 17:59:17 +02:00
parsed_files [ ] & ast . File
2020-07-26 15:54:18 +02:00
cached_msvc MsvcResult
2021-04-02 00:57:09 +02:00
table & ast . Table
2020-12-21 17:46:26 +01:00
ccoptions CcompilerOptions
2020-01-18 23:26:14 +01:00
}
2020-02-03 12:09:17 +01:00
pub fn new_builder ( pref & pref . Preferences ) Builder {
2020-04-07 00:44:19 +02:00
rdir := os . real_path ( pref . path )
compiled_dir := if os . is_dir ( rdir ) { rdir } else { os . dir ( rdir ) }
2021-04-02 00:57:09 +02:00
mut table := ast . new_table ( )
2020-11-05 19:15:26 +01:00
table . is_fmt = false
2020-05-20 19:33:29 +02:00
if pref . use_color == . always {
2020-07-24 07:31:05 +02:00
util . emanager . set_support_color ( true )
2020-05-20 19:33:29 +02:00
}
if pref . use_color == . never {
2020-07-24 07:31:05 +02:00
util . emanager . set_support_color ( false )
2020-05-20 19:33:29 +02:00
}
2021-02-18 14:58:47 +01:00
msvc := find_msvc ( pref . m64 ) or {
2020-07-07 20:35:49 +02:00
if pref . ccompiler == ' m s v c ' {
2021-02-28 21:20:21 +01:00
// verror('Cannot find MSVC on this OS')
2020-07-07 20:35:49 +02:00
}
2020-07-26 15:54:18 +02:00
MsvcResult {
valid : false
}
2020-07-07 20:35:49 +02:00
}
2021-02-05 15:34:56 +01:00
util . timing_set_should_print ( pref . show_timings || pref . is_verbose )
2020-04-16 15:35:19 +02:00
return Builder {
2020-02-03 12:09:17 +01:00
pref : pref
2020-01-18 23:26:14 +01:00
table : table
2021-02-05 08:27:14 +01:00
checker : checker . new_checker ( table , pref )
2020-04-04 05:14:40 +02:00
global_scope : & ast . Scope {
parent : 0
}
2020-04-07 00:44:19 +02:00
compiled_dir : compiled_dir
2021-02-14 19:22:24 +01:00
max_nr_errors : if pref . error_limit > 0 { pref . error_limit } else { 100 }
2020-07-07 20:35:49 +02:00
cached_msvc : msvc
2020-01-18 23:26:14 +01:00
}
2020-05-24 04:47:51 +02:00
// max_nr_errors: pref.error_limit ?? 100 TODO potential syntax?
2020-01-18 23:26:14 +01:00
}
2021-05-06 13:26:10 +02:00
pub fn ( mut b Builder ) front_stages ( v_files [ ] string ) ? {
util . timing_start ( ' P A R S E ' )
b . parsed_files = parser . parse_files ( v_files , b . table , b . pref , b . global_scope )
b . parse_imports ( )
2021-07-16 12:03:40 +02:00
mut timers := util . get_timers ( )
timers . show ( ' S C A N ' )
timers . show ( ' P A R S E ' )
timers . show_if_exists ( ' P A R S E s t m t ' )
2021-05-06 13:26:10 +02:00
if b . pref . only_check_syntax {
return error ( ' s t o p _ a f t e r _ p a r s e r ' )
}
}
pub fn ( mut b Builder ) middle_stages ( ) ? {
util . timing_start ( ' C H E C K ' )
2021-07-15 07:29:13 +02:00
b . checker . generic_insts_to_concrete ( )
2021-05-06 13:26:10 +02:00
b . checker . check_files ( b . parsed_files )
util . timing_measure ( ' C H E C K ' )
b . print_warnings_and_errors ( )
2021-05-06 18:37:10 +02:00
//
b . table . complete_interface_check ( )
2021-05-06 13:26:10 +02:00
if b . pref . skip_unused {
markused . mark_used ( mut b . table , b . pref , b . parsed_files )
}
}
pub fn ( mut b Builder ) front_and_middle_stages ( v_files [ ] string ) ? {
b . front_stages ( v_files ) ?
b . middle_stages ( ) ?
}
2020-02-03 07:31:54 +01:00
// parse all deps from already parsed files
2020-04-25 17:49:16 +02:00
pub fn ( mut b Builder ) parse_imports ( ) {
2020-04-26 09:17:13 +02:00
mut done_imports := [ ] string { }
2020-08-14 21:57:32 +02:00
if b . pref . is_vsh {
2020-07-26 15:54:18 +02:00
done_imports << ' o s '
}
2020-07-27 04:06:48 +02:00
// TODO (joe): decide if this is correct solution.
// in the case of building a module, the actual module files
// are passed via cmd line, so they have already been parsed
// by this stage. note that if one files from a module was
// parsed (but not all of them), then this will cause a problem.
// we could add a list of parsed files instead, but I think
// there is a better solution all around, I will revisit this.
// NOTE: there is a very similar occurance with the way
// internal module test's work, and this was the reason there
// were issues with duplicate declarations, so we should sort
// that out in a similar way.
2020-07-26 15:54:18 +02:00
for file in b . parsed_files {
if file . mod . name != ' m a i n ' && file . mod . name ! in done_imports {
done_imports << file . mod . name
2020-07-04 23:37:41 +02:00
}
2020-07-26 15:54:18 +02:00
}
2020-04-16 11:29:36 +02:00
// NB: b.parsed_files is appended in the loop,
// so we can not use the shorter `for in` form.
2020-04-16 15:35:19 +02:00
for i := 0 ; i < b . parsed_files . len ; i ++ {
2020-02-03 07:31:54 +01:00
ast_file := b . parsed_files [ i ]
2020-07-04 23:37:41 +02:00
for imp in ast_file . imports {
2020-02-03 07:31:54 +01:00
mod := imp . mod
2020-05-21 16:31:41 +02:00
if mod == ' b u i l t i n ' {
2021-02-04 08:09:54 +01:00
error_with_pos ( ' c a n n o t i m p o r t m o d u l e " b u i l t i n " ' , ast_file . path , imp . pos )
2020-05-21 16:31:41 +02:00
break
}
2020-02-03 07:31:54 +01:00
if mod in done_imports {
continue
}
2020-04-11 08:57:31 +02:00
import_path := b . find_module_path ( mod , ast_file . path ) or {
2021-04-02 00:57:09 +02:00
// v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)', v.parsers[i].import_ast.get_import_tok_idx(mod))
2020-02-07 18:46:42 +01:00
// break
2021-02-04 08:09:54 +01:00
error_with_pos ( ' c a n n o t i m p o r t m o d u l e " $ mod " ( n o t f o u n d ) ' , ast_file . path ,
imp . pos )
2020-05-14 09:59:29 +02:00
break
2020-02-03 07:31:54 +01:00
}
v_files := b . v_files_from_dir ( import_path )
if v_files . len == 0 {
2021-04-02 00:57:09 +02:00
// v.parsers[i].error_with_token_index('cannot import module "$mod" (no .v files in "$import_path")', v.parsers[i].import_ast.get_import_tok_idx(mod))
2021-02-04 08:09:54 +01:00
error_with_pos ( ' c a n n o t i m p o r t m o d u l e " $ mod " ( n o . v f i l e s i n " $ import_path " ) ' ,
ast_file . path , imp . pos )
2020-02-03 07:31:54 +01:00
}
// Add all imports referenced by these libs
2020-04-04 05:14:40 +02:00
parsed_files := parser . parse_files ( v_files , b . table , b . pref , b . global_scope )
2020-02-03 07:31:54 +01:00
for file in parsed_files {
2021-04-17 07:30:03 +02:00
mut name := file . mod . name
if name == ' ' {
name = file . mod . short_name
}
if name != mod {
2020-02-07 18:46:42 +01:00
// v.parsers[pidx].error_with_token_index('bad module definition: ${v.parsers[pidx].file_path} imports module "$mod" but $file is defined as module `$p_mod`', 1
2021-04-17 07:30:03 +02:00
error_with_pos ( ' b a d m o d u l e d e f i n i t i o n : $ ast_file . path i m p o r t s m o d u l e " $ mod " b u t $ file . path i s d e f i n e d a s m o d u l e ` $ name ` ' ,
2021-02-04 08:09:54 +01:00
ast_file . path , imp . pos )
2020-02-03 07:31:54 +01:00
}
}
b . parsed_files << parsed_files
done_imports << mod
}
}
2020-04-16 11:29:36 +02:00
b . resolve_deps ( )
2020-04-30 18:15:30 +02:00
//
if b . pref . print_v_files {
for p in b . parsed_files {
println ( p . path )
}
2020-05-05 07:00:52 +02:00
exit ( 0 )
2020-04-30 18:15:30 +02:00
}
2020-04-16 11:29:36 +02:00
}
2020-04-25 17:49:16 +02:00
pub fn ( mut b Builder ) resolve_deps ( ) {
2020-04-16 11:29:36 +02:00
graph := b . import_graph ( )
deps_resolved := graph . resolve ( )
if b . pref . is_verbose {
eprintln ( ' - - - - - - r e s o l v e d d e p e n d e n c i e s g r a p h : - - - - - - ' )
eprintln ( deps_resolved . display ( ) )
eprintln ( ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ' )
}
2021-05-20 00:20:25 +02:00
cycles := deps_resolved . display_cycles ( )
2020-07-04 23:37:41 +02:00
if cycles . len > 1 {
verror ( ' e r r o r : i m p o r t c y c l e d e t e c t e d b e t w e e n t h e f o l l o w i n g m o d u l e s : \n ' + cycles )
}
2020-04-26 09:17:13 +02:00
mut mods := [ ] string { }
2020-04-16 11:29:36 +02:00
for node in deps_resolved . nodes {
mods << node . name
}
if b . pref . is_verbose {
eprintln ( ' - - - - - - i m p o r t e d m o d u l e s : - - - - - - ' )
eprintln ( mods . str ( ) )
eprintln ( ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ' )
}
2021-05-22 17:59:17 +02:00
mut reordered_parsed_files := [ ] & ast . File { }
2020-04-16 11:29:36 +02:00
for m in mods {
for pf in b . parsed_files {
if m == pf . mod . name {
reordered_parsed_files << pf
2020-04-16 15:35:19 +02:00
// eprintln('pf.mod.name: $pf.mod.name | pf.path: $pf.path')
2020-04-16 11:29:36 +02:00
}
}
}
2020-07-08 13:19:58 +02:00
b . table . modules = mods
2020-04-16 11:29:36 +02:00
b . parsed_files = reordered_parsed_files
}
// graph of all imported modules
pub fn ( b & Builder ) import_graph ( ) & depgraph . DepGraph {
2020-12-20 15:21:32 +01:00
builtins := util . builtin_module_parts . clone ( )
2020-04-23 01:16:58 +02:00
mut graph := depgraph . new_dep_graph ( )
2020-04-16 11:29:36 +02:00
for p in b . parsed_files {
2021-05-20 00:20:25 +02:00
// eprintln('p.path: $p.path')
2020-04-26 09:17:13 +02:00
mut deps := [ ] string { }
2020-04-16 11:29:36 +02:00
if p . mod . name ! in builtins {
deps << ' b u i l t i n '
2020-07-04 23:37:41 +02:00
if b . pref . backend == . c {
// TODO JavaScript backend doesn't handle os for now
2020-08-14 21:57:32 +02:00
if b . pref . is_vsh && p . mod . name != ' o s ' {
2020-07-04 23:37:41 +02:00
deps << ' o s '
}
}
2020-04-16 11:29:36 +02:00
}
2020-07-04 23:37:41 +02:00
for m in p . imports {
2021-01-20 06:04:59 +01:00
if m . mod == p . mod . name {
continue
}
2020-04-16 11:29:36 +02:00
deps << m . mod
}
graph . add ( p . mod . name , deps )
}
2021-05-20 00:20:25 +02:00
$ if trace_import_graph ? {
eprintln ( graph . display ( ) )
}
2020-04-16 11:29:36 +02:00
return graph
2020-02-03 07:31:54 +01:00
}
2020-04-07 00:44:19 +02:00
pub fn ( b Builder ) v_files_from_dir ( dir string ) [ ] string {
2020-02-03 07:31:54 +01:00
if ! os . exists ( dir ) {
if dir == ' c o m p i l e r ' && os . is_dir ( ' v l i b ' ) {
println ( ' l o o k s l i k e y o u a r e t r y i n g t o b u i l d V w i t h a n o l d c o m m a n d ' )
2020-02-09 10:08:04 +01:00
println ( ' u s e ` v - o v c m d / v ` i n s t e a d o f ` v - o v c o m p i l e r ` ' )
2020-02-03 07:31:54 +01:00
}
verror ( " $ dir d o e s n ' t e x i s t " )
2020-04-07 00:44:19 +02:00
} else if ! os . is_dir ( dir ) {
2020-03-24 11:14:11 +01:00
verror ( " $ dir i s n ' t a d i r e c t o r y ! " )
2020-02-03 07:31:54 +01:00
}
2021-03-01 00:18:14 +01:00
mut files := os . ls ( dir ) or { panic ( err ) }
2020-04-07 00:44:19 +02:00
if b . pref . is_verbose {
2020-02-03 07:31:54 +01:00
println ( ' v _ f i l e s _ f r o m _ d i r ( " $ dir " ) ' )
}
2020-05-05 07:00:52 +02:00
return b . pref . should_compile_filtered_files ( dir , files )
2020-04-10 18:25:23 +02:00
}
2020-04-07 00:44:19 +02:00
pub fn ( b Builder ) log ( s string ) {
if b . pref . is_verbose {
2020-02-03 07:31:54 +01:00
println ( s )
}
}
2020-03-01 21:56:07 +01:00
2020-04-07 00:44:19 +02:00
pub fn ( b Builder ) info ( s string ) {
if b . pref . is_verbose {
2020-03-31 19:58:44 +02:00
println ( s )
}
}
2020-03-01 21:56:07 +01:00
[ inline ]
fn module_path ( mod string ) string {
// submodule support
2020-03-07 22:26:26 +01:00
return mod . replace ( ' . ' , os . path_separator )
2020-03-01 21:56:07 +01:00
}
2021-02-05 08:05:13 +01:00
// TODO: try to merge this & util.module functions to create a
2021-01-20 06:04:59 +01:00
// reliable multi use function. see comments in util/module.v
2020-10-15 12:32:28 +02:00
pub fn ( b & Builder ) find_module_path ( mod string , fpath string ) ? string {
2020-04-11 08:57:31 +02:00
// support @VROOT/v.mod relative paths:
2020-07-23 23:16:36 +02:00
mut mcache := vmod . get_cache ( )
2020-06-01 16:39:24 +02:00
vmod_file_location := mcache . get_by_file ( fpath )
2020-03-01 21:56:07 +01:00
mod_path := module_path ( mod )
2020-04-26 09:17:13 +02:00
mut module_lookup_paths := [ ] string { }
2021-01-23 09:33:22 +01:00
if vmod_file_location . vmod_file . len != 0
&& vmod_file_location . vmod_folder ! in b . module_search_paths {
2020-04-11 08:57:31 +02:00
module_lookup_paths << vmod_file_location . vmod_folder
}
module_lookup_paths << b . module_search_paths
2021-01-20 06:04:59 +01:00
module_lookup_paths << os . getwd ( )
2020-12-22 12:32:02 +01:00
// go up through parents looking for modules a folder.
// we need a proper solution that works most of the time. look at vdoc.get_parent_mod
if fpath . contains ( os . path_separator + ' m o d u l e s ' + os . path_separator ) {
parts := fpath . split ( os . path_separator )
for i := parts . len - 2 ; i >= 0 ; i -- {
if parts [ i ] == ' m o d u l e s ' {
module_lookup_paths << parts [ 0 .. i + 1 ] . join ( os . path_separator )
break
}
}
}
2020-04-11 08:57:31 +02:00
for search_path in module_lookup_paths {
2020-04-07 00:44:19 +02:00
try_path := os . join_path ( search_path , mod_path )
if b . pref . is_verbose {
2020-03-01 21:56:07 +01:00
println ( ' > > t r y i n g t o f i n d $ mod i n $ try_path . . ' )
}
if os . is_dir ( try_path ) {
2020-04-07 00:44:19 +02:00
if b . pref . is_verbose {
2020-03-01 21:56:07 +01:00
println ( ' < < f o u n d $ try_path . ' )
}
return try_path
}
}
2021-01-20 06:04:59 +01:00
// look up through parents
path_parts := fpath . split ( os . path_separator )
for i := path_parts . len - 2 ; i > 0 ; i -- {
p1 := path_parts [ 0 .. i ] . join ( os . path_separator )
try_path := os . join_path ( p1 , mod_path )
if b . pref . is_verbose {
println ( ' > > t r y i n g t o f i n d $ mod i n $ try_path . . ' )
}
if os . is_dir ( try_path ) {
return try_path
}
}
2020-04-11 08:57:31 +02:00
smodule_lookup_paths := module_lookup_paths . join ( ' , ' )
return error ( ' m o d u l e " $ mod " n o t f o u n d i n : \n $ smodule_lookup_paths ' )
2020-03-01 21:56:07 +01:00
}
2020-04-03 17:38:41 +02:00
2020-07-25 09:32:29 +02:00
fn ( b & Builder ) show_total_warns_and_errors_stats ( ) {
2021-04-08 11:18:02 +02:00
if b . checker . nr_errors == 0 && b . checker . nr_warnings == 0 && b . checker . nr_notices == 0 {
return
}
2020-07-25 09:32:29 +02:00
if b . pref . is_stats {
2021-03-14 08:37:38 +01:00
estring := util . bold ( b . checker . nr_errors . str ( ) )
wstring := util . bold ( b . checker . nr_warnings . str ( ) )
2021-03-22 18:43:06 +01:00
nstring := util . bold ( b . checker . nr_notices . str ( ) )
println ( ' c h e c k e r s u m m a r y : $ estring V e r r o r s , $ wstring V w a r n i n g s , $ nstring V n o t i c e s ' )
2020-07-25 09:32:29 +02:00
}
}
2020-05-05 07:00:52 +02:00
fn ( b & Builder ) print_warnings_and_errors ( ) {
2020-07-26 15:54:18 +02:00
defer {
b . show_total_warns_and_errors_stats ( )
}
2020-05-10 11:26:57 +02:00
if b . pref . output_mode == . silent {
if b . checker . nr_errors > 0 {
exit ( 1 )
}
return
}
2020-05-09 15:16:48 +02:00
if b . pref . is_verbose && b . checker . nr_warnings > 1 {
println ( ' $ b . checker . nr_warnings w a r n i n g s ' )
}
2021-03-22 18:43:06 +01:00
if b . pref . is_verbose && b . checker . nr_notices > 1 {
println ( ' $ b . checker . nr_notices n o t i c e s ' )
}
if b . checker . nr_notices > 0 && ! b . pref . skip_warnings {
for i , err in b . checker . notices {
kind := if b . pref . is_verbose {
' $ err . reporter n o t i c e # $ b . checker . nr_notices : '
} else {
' n o t i c e : '
}
ferror := util . formatted_error ( kind , err . message , err . file_path , err . pos )
eprintln ( ferror )
if err . details . len > 0 {
eprintln ( ' D e t a i l s : $ err . details ' )
}
if i > b . max_nr_errors {
return
}
}
}
2020-05-20 17:27:31 +02:00
if b . checker . nr_warnings > 0 && ! b . pref . skip_warnings {
2020-05-09 15:16:48 +02:00
for i , err in b . checker . warnings {
2020-12-27 14:20:30 +01:00
kind := if b . pref . is_verbose {
' $ err . reporter w a r n i n g # $ b . checker . nr_warnings : '
} else {
' w a r n i n g : '
}
2020-04-29 12:04:09 +02:00
ferror := util . formatted_error ( kind , err . message , err . file_path , err . pos )
eprintln ( ferror )
2020-07-04 21:24:44 +02:00
if err . details . len > 0 {
2021-01-30 09:20:09 +01:00
eprintln ( ' D e t a i l s : $ err . details ' )
2020-07-04 23:37:41 +02:00
}
2020-05-09 15:16:48 +02:00
// eprintln('')
2020-05-24 04:43:00 +02:00
if i > b . max_nr_errors {
2020-05-09 15:16:48 +02:00
return
}
2020-04-29 12:04:09 +02:00
}
2020-04-13 01:56:01 +02:00
}
2020-05-09 15:16:48 +02:00
//
if b . pref . is_verbose && b . checker . nr_errors > 1 {
println ( ' $ b . checker . nr_errors e r r o r s ' )
}
2020-04-29 12:04:09 +02:00
if b . checker . nr_errors > 0 {
2020-05-09 15:16:48 +02:00
for i , err in b . checker . errors {
2020-12-27 14:20:30 +01:00
kind := if b . pref . is_verbose {
' $ err . reporter e r r o r # $ b . checker . nr_errors : '
} else {
' e r r o r : '
}
2020-04-29 12:04:09 +02:00
ferror := util . formatted_error ( kind , err . message , err . file_path , err . pos )
eprintln ( ferror )
2020-07-04 21:24:44 +02:00
if err . details . len > 0 {
2021-01-30 09:20:09 +01:00
eprintln ( ' D e t a i l s : $ err . details ' )
2020-07-04 23:37:41 +02:00
}
2020-05-09 15:16:48 +02:00
// eprintln('')
2020-05-24 04:43:00 +02:00
if i > b . max_nr_errors {
2020-05-09 15:16:48 +02:00
return
}
2020-04-29 12:04:09 +02:00
}
2020-07-25 09:32:29 +02:00
b . show_total_warns_and_errors_stats ( )
2020-04-29 12:04:09 +02:00
exit ( 1 )
2020-04-29 11:38:36 +02:00
}
2020-05-20 17:27:31 +02:00
if b . table . redefined_fns . len > 0 {
2020-11-03 09:00:06 +01:00
mut total_conflicts := 0
2020-05-20 17:27:31 +02:00
for fn_name in b . table . redefined_fns {
// Find where this function was already declared
2020-11-03 09:00:06 +01:00
mut redefines := [ ] FunctionRedefinition { }
mut redefine_conflicts := map [ string ] int { }
2020-05-20 17:27:31 +02:00
for file in b . parsed_files {
for stmt in file . stmts {
if stmt is ast . FnDecl {
2020-07-09 17:14:14 +02:00
if stmt . name == fn_name {
2020-12-22 21:38:13 +01:00
fheader := stmt . stringify ( b . table , ' m a i n ' , map [ string ] string { } )
2020-11-03 09:00:06 +01:00
redefines << FunctionRedefinition {
fpath : file . path
fline : stmt . pos . line_nr
f : stmt
fheader : fheader
}
redefine_conflicts [ fheader ] ++
2020-05-20 17:27:31 +02:00
}
}
}
}
2021-03-01 21:38:31 +01:00
if redefines . len > 0 {
2020-11-03 09:00:06 +01:00
eprintln ( ' r e d e f i n i t i o n o f f u n c t i o n ` $ fn_name ` ' )
for redefine in redefines {
eprintln ( util . formatted_error ( ' c o n f l i c t i n g d e c l a r a t i o n : ' , redefine . fheader ,
redefine . fpath , redefine . f . pos ) )
}
total_conflicts ++
}
}
if total_conflicts > 0 {
2020-07-25 09:32:29 +02:00
b . show_total_warns_and_errors_stats ( )
2020-05-20 17:27:31 +02:00
exit ( 1 )
}
}
2020-04-29 11:38:36 +02:00
}
2020-11-03 09:00:06 +01:00
struct FunctionRedefinition {
fpath string
fline int
fheader string
f ast . FnDecl
}
2021-02-04 08:09:54 +01:00
fn error_with_pos ( s string , fpath string , pos token . Position ) {
ferror := util . formatted_error ( ' b u i l d e r e r r o r : ' , s , fpath , pos )
eprintln ( ferror )
exit ( 1 )
}
2021-07-05 04:05:37 +02:00
[ noreturn ]
2020-04-03 17:38:41 +02:00
fn verror ( s string ) {
util . verror ( ' b u i l d e r e r r o r ' , s )
}