2020-04-15 23:16:49 +02:00
module js
2020-04-26 07:35:59 +02:00
import strings
import v . ast
2020-12-08 17:49:20 +01:00
import v . token
2020-04-26 07:35:59 +02:00
import v . pref
import v . util
2021-07-27 11:35:54 +02:00
import v . util . version
2020-05-17 21:39:01 +02:00
import v . depgraph
2021-06-10 07:33:46 +02:00
import encoding . base64
import v . gen . js . sourcemap
2021-07-13 21:52:01 +02:00
2020-04-15 23:16:49 +02:00
const (
2020-05-15 15:55:49 +02:00
// https://ecma-international.org/ecma-262/#sec-reserved-words
2021-03-06 18:09:28 +01:00
js_reserved = [ ' a w a i t ' , ' b r e a k ' , ' c a s e ' , ' c a t c h ' , ' c l a s s ' , ' c o n s t ' , ' c o n t i n u e ' , ' d e b u g g e r ' ,
2021-01-30 09:42:18 +01:00
' d e f a u l t ' , ' d e l e t e ' , ' d o ' , ' e l s e ' , ' e n u m ' , ' e x p o r t ' , ' e x t e n d s ' , ' f i n a l l y ' , ' f o r ' , ' f u n c t i o n ' ,
' i f ' , ' i m p l e m e n t s ' , ' i m p o r t ' , ' i n ' , ' i n s t a n c e o f ' , ' i n t e r f a c e ' , ' l e t ' , ' n e w ' , ' p a c k a g e ' ,
' p r i v a t e ' , ' p r o t e c t e d ' , ' p u b l i c ' , ' r e t u r n ' , ' s t a t i c ' , ' s u p e r ' , ' s w i t c h ' , ' t h i s ' , ' t h r o w ' ,
' t r y ' , ' t y p e o f ' , ' v a r ' , ' v o i d ' , ' w h i l e ' , ' w i t h ' , ' y i e l d ' , ' N u m b e r ' , ' S t r i n g ' , ' B o o l e a n ' ,
2021-12-07 10:11:54 +01:00
' A r r a y ' , ' M a p ' , ' d o c u m e n t ' , ' P r o m i s e ' ]
2020-12-08 17:49:20 +01:00
// used to generate type structs
2022-04-15 19:50:51 +02:00
v_types = [ ' i 8 ' , ' i 1 6 ' , ' i n t ' , ' i 6 4 ' , ' u 8 ' , ' u 1 6 ' , ' u 3 2 ' , ' u 6 4 ' , ' f 3 2 ' , ' f 6 4 ' ,
2021-10-29 10:23:40 +02:00
' i n t _ l i t e r a l ' , ' f l o a t _ l i t e r a l ' , ' b o o l ' , ' s t r i n g ' , ' m a p ' , ' a r r a y ' , ' r u n e ' , ' a n y ' , ' v o i d p t r ' ]
2022-04-15 19:31:10 +02:00
shallow_equatables = [ ast . Kind . i8 , . i16 , . int , . i64 , . u8 , . u16 , . u32 , . u64 , . f32 , . f64 ,
2021-09-21 01:12:38 +02:00
. int_literal , . float_literal , . bool , . string ]
2020-04-15 23:16:49 +02:00
)
2021-06-10 07:33:46 +02:00
struct SourcemapHelper {
src_path string
src_line u32
ns_pos u32
}
2020-12-07 18:36:22 +01:00
struct Namespace {
2021-01-30 09:42:18 +01:00
name string
2020-12-07 18:36:22 +01:00
mut :
2021-06-10 07:33:46 +02:00
pub_vars [ ] string
imports map [ string ] string
indent int
methods map [ string ] [ ] ast . FnDecl
sourcemap_helper [ ] SourcemapHelper
2020-12-07 18:36:22 +01:00
}
2021-05-07 14:58:48 +02:00
[ heap ]
2020-04-15 23:16:49 +02:00
struct JsGen {
2021-07-19 14:55:03 +02:00
pref & pref . Preferences
2020-04-27 22:53:26 +02:00
mut :
2021-09-16 06:07:48 +02:00
table & ast . Table
definitions strings . Builder
ns & Namespace
namespaces map [ string ] & Namespace
doc & JsDoc
enable_doc bool
file & ast . File
tmp_count int
inside_ternary bool
2022-01-14 17:43:18 +01:00
inside_or bool
2021-09-16 06:07:48 +02:00
inside_loop bool
inside_map_set bool // map.set(key, value)
inside_builtin bool
inside_if_optional bool
generated_builtin bool
inside_def_typ_decl bool
is_test bool
stmt_start_pos int
defer_stmts [ ] ast . DeferStmt
fn_decl & ast . FnDecl // pointer to the FnDecl we are currently inside otherwise 0
2021-09-29 14:33:14 +02:00
generated_str_fns [ ] StrType
str_types [ ] StrType // types that need automatic str() generation
2021-10-28 18:31:56 +02:00
copy_types [ ] StrType // types that need to be deep copied
generated_copy_fns [ ] StrType
array_fn_definitions [ ] string // array equality functions that have been defined
map_fn_definitions [ ] string // map equality functions that have been defined
struct_fn_definitions [ ] string // struct equality functions that have been defined
sumtype_fn_definitions [ ] string // sumtype equality functions that have been defined
alias_fn_definitions [ ] string // alias equality functions that have been defined
auto_fn_definitions [ ] string // auto generated functions defination list
anon_fn_definitions [ ] string // anon generated functions defination list
copy_fn_definitions [ ] string
2021-09-16 06:07:48 +02:00
method_fn_decls map [ string ] [ ] ast . FnDecl
builtin_fns [ ] string // Functions defined in `builtin`
empty_line bool
cast_stack [ ] ast . Type
call_stack [ ] ast . CallExpr
is_vlines_enabled bool // is it safe to generate #line directives when -g is passed
sourcemap & sourcemap . SourceMap // maps lines in generated javascrip file to original source files and line
comptime_var_type_map map [ string ] ast . Type
defer_ifdef string
2021-11-25 15:49:53 +01:00
cur_concrete_types [ ] ast . Type
2021-09-16 06:07:48 +02:00
out strings . Builder = strings . new_builder ( 128 )
2021-09-26 06:33:53 +02:00
array_sort_fn map [ string ] bool
2021-10-18 09:56:21 +02:00
wasm_export map [ string ] [ ] string
wasm_import map [ string ] [ ] string
2021-10-22 21:03:19 +02:00
init_global map [ string ] map [ string ] ast . Expr // initializers for constants or globals, should be invoked before module init.
2020-04-15 23:16:49 +02:00
}
2021-08-26 14:20:54 +02:00
fn ( mut g JsGen ) write_tests_definitions ( ) {
g . definitions . writeln ( ' g l o b a l T h i s . g _ t e s t _ o k s = 0 ; ' )
g . definitions . writeln ( ' g l o b a l T h i s . g _ t e s t _ f a i l s = 0 ; ' )
}
2021-05-22 17:59:17 +02:00
pub fn gen ( files [ ] & ast . File , table & ast . Table , pref & pref . Preferences ) string {
2020-04-15 23:16:49 +02:00
mut g := & JsGen {
definitions : strings . new_builder ( 100 )
table : table
pref : pref
fn_decl : 0
empty_line : true
doc : 0
2020-12-07 18:36:22 +01:00
ns : 0
2020-05-20 16:57:42 +02:00
enable_doc : true
2021-05-22 17:59:17 +02:00
file : 0
2021-09-09 19:21:01 +02:00
sourcemap : 0
2020-04-15 23:16:49 +02:00
}
g . doc = new_jsdoc ( g )
2020-05-20 16:57:42 +02:00
// TODO: Add '[-no]-jsdoc' flag
if pref . is_prod {
g . enable_doc = false
2021-06-10 07:33:46 +02:00
g . is_vlines_enabled = false
2020-05-20 16:57:42 +02:00
}
2020-04-15 23:16:49 +02:00
g . init ( )
2020-05-17 21:39:01 +02:00
mut graph := depgraph . new_dep_graph ( )
2021-06-10 07:33:46 +02:00
if g . pref . sourcemap {
mut sg := sourcemap . generate_empty_map ( )
g . sourcemap = sg . add_map ( ' ' , ' ' , g . pref . sourcemap_src_included , 0 , 0 )
}
2021-08-26 14:20:54 +02:00
mut tests_inited := false
2020-04-15 23:16:49 +02:00
// Get class methods
for file in files {
g . file = file
g . enter_namespace ( g . file . mod . name )
2021-01-30 10:05:59 +01:00
g . is_test = g . pref . is_test
2020-04-15 23:16:49 +02:00
g . find_class_methods ( file . stmts )
g . escape_namespace ( )
}
for file in files {
g . file = file
g . enter_namespace ( g . file . mod . name )
2021-09-08 19:30:46 +02:00
if g . enable_doc {
g . writeln ( ' / * * @ n a m e s p a c e $ file . mod . name * / ' )
}
2021-01-30 10:05:59 +01:00
g . is_test = g . pref . is_test
2020-05-17 21:39:01 +02:00
// store imports
mut imports := [ ] string { }
for imp in g . file . imports {
imports << imp . mod
}
2021-09-08 19:30:46 +02:00
2020-05-17 21:39:01 +02:00
graph . add ( g . file . mod . name , imports )
2020-12-08 17:49:20 +01:00
// builtin types
if g . file . mod . name == ' b u i l t i n ' && ! g . generated_builtin {
g . gen_builtin_type_defs ( )
2021-09-26 06:33:53 +02:00
g . writeln ( ' O b j e c t . d e f i n e P r o p e r t y ( a r r a y . p r o t o t y p e , " l e n " , { g e t : f u n c t i o n ( ) { r e t u r n n e w i n t ( t h i s . a r r . a r r . l e n g t h ) ; } , s e t : f u n c t i o n ( l ) { t h i s . a r r . a r r . l e n g t h = l . v a l u e O f ( ) ; } } ) ; ' )
2021-12-15 14:47:34 +01:00
g . writeln ( ' O b j e c t . d e f i n e P r o p e r t y ( m a p . p r o t o t y p e , " l e n " , { g e t : f u n c t i o n ( ) { r e t u r n n e w i n t ( t h i s . l e n g t h ) ; } , s e t : f u n c t i o n ( l ) { } } ) ; ' )
2021-09-26 06:33:53 +02:00
g . writeln ( ' O b j e c t . d e f i n e P r o p e r t y ( a r r a y . p r o t o t y p e , " l e n g t h " , { g e t : f u n c t i o n ( ) { r e t u r n n e w i n t ( t h i s . a r r . a r r . l e n g t h ) ; } , s e t : f u n c t i o n ( l ) { t h i s . a r r . a r r . l e n g t h = l . v a l u e O f ( ) ; } } ) ; ' )
2020-12-08 17:49:20 +01:00
g . generated_builtin = true
}
2021-08-26 14:20:54 +02:00
if g . is_test && ! tests_inited {
g . write_tests_definitions ( )
tests_inited = true
}
2020-04-15 23:16:49 +02:00
g . stmts ( file . stmts )
// store the current namespace
g . escape_namespace ( )
}
2021-09-29 14:33:14 +02:00
for i := 0 ; i < g . str_types . len ; i ++ {
g . final_gen_str ( g . str_types [ i ] )
}
2021-10-28 18:31:56 +02:00
for i := 0 ; i < g . copy_types . len ; i ++ {
g . final_gen_copy ( g . copy_types [ i ] )
}
2021-08-26 14:20:54 +02:00
if g . pref . is_test {
g . gen_js_main_for_tests ( )
}
2021-09-11 13:24:47 +02:00
g . enter_namespace ( ' m a i n ' )
// generate JS methods for interface methods
2021-11-11 13:36:32 +01:00
for iface_name , iface_types in g . table . iface_types {
2022-01-02 14:47:58 +01:00
iface := g . table . find_sym ( iface_name ) or { panic ( ' u n r e a c h a b l e : i n t e r f a c e m u s t e x i s t ' ) }
2021-09-11 13:24:47 +02:00
for ty in iface_types {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( ty )
2021-11-11 13:36:32 +01:00
for method in iface . methods {
2021-12-19 17:25:18 +01:00
p_sym := g . table . sym ( ty )
2021-11-17 10:41:33 +01:00
mname := if p_sym . has_method ( method . name ) {
g . js_name ( p_sym . name ) + ' _ ' + method . name
} else {
g . js_name ( iface_name ) + ' _ ' + method . name
}
2021-09-11 13:24:47 +02:00
g . write ( ' $ { g . js_name ( sym . name ) } . p r o t o t y p e . $ method . name = f u n c t i o n ( ' )
for i , param in method . params {
if i == 0 {
continue
}
g . write ( ' $ { g . js_name ( param . name ) } ' )
if i != method . params . len - 1 {
g . write ( ' , ' )
}
}
g . writeln ( ' ) { ' )
g . inc_indent ( )
g . write ( ' r e t u r n $ { mname } ( ' )
for i , param in method . params {
if i == 0 {
g . write ( ' t h i s ' )
} else {
g . write ( ' $ { g . js_name ( param . name ) } ' )
}
if i != method . params . len - 1 {
g . write ( ' , ' )
}
}
g . writeln ( ' ) ' )
g . dec_indent ( )
g . writeln ( ' } ' )
}
}
}
2021-10-01 20:23:49 +02:00
for mod_name in g . table . modules {
g . writeln ( ' / / I n i t i a l i z a t i o n s f o r m o d u l e $ mod_name ' )
2021-10-22 21:03:19 +02:00
for global , expr in g . init_global [ mod_name ] {
g . write ( ' $ global = ' )
g . expr ( expr )
g . writeln ( ' ; ' )
}
2021-10-01 20:23:49 +02:00
init_fn_name := ' $ { mod_name } . i n i t '
if initfn := g . table . find_fn ( init_fn_name ) {
if initfn . return_type == ast . void_type && initfn . params . len == 0 {
mod_c_name := util . no_dots ( mod_name )
init_fn_c_name := ' $ { mod_c_name } _ _ i n i t '
g . writeln ( ' $ { init_fn_c_name } ( ) ; ' )
}
}
}
2021-10-22 21:03:19 +02:00
2021-10-18 09:56:21 +02:00
if ! g . pref . is_shared {
2021-12-15 14:47:34 +01:00
if g . pref . output_es5 {
g . write ( ' j s _ m a i n ( ) ; ' )
} else {
g . write ( ' l o a d R o u t i n e ( ) . t h e n ( _ = > j s _ m a i n ( ) ) ; ' )
}
2021-10-18 09:56:21 +02:00
}
2021-09-11 13:24:47 +02:00
g . escape_namespace ( )
2020-05-17 21:39:01 +02:00
// resolve imports
2021-10-18 09:56:21 +02:00
// deps_resolved := graph.resolve()
// nodes := deps_resolved.nodes
2021-08-26 14:20:54 +02:00
2021-09-08 19:30:46 +02:00
mut out := g . definitions . str ( ) + g . hashes ( )
2021-12-15 14:47:34 +01:00
if ! g . pref . output_es5 {
out += ' \n l e t w a s m E x p o r t O b j e c t ; \n '
out += ' c o n s t l o a d R o u t i n e = a s y n c ( ) = > { \n '
for mod , functions in g . wasm_import {
if g . pref . backend == . js_browser {
out += ' \n a w a i t f e t c h ( " $ mod " ) . t h e n ( r e s p o n e = > r e s p o n e . a r r a y B u f f e r ( ) ) . t h e n ( b y t e s = > '
out += ' W e b A s s e m b l y . i n s t a n t i a t e ( b y t e s , '
exports := g . wasm_export [ mod ]
out += ' { i m p o r t s : { \n '
for i , exp in exports {
out += g . js_name ( exp ) + ' : ' + ' \$ w a s m ' + g . js_name ( exp )
if i != exports . len - 1 {
out += ' , \n '
}
2021-10-18 09:56:21 +02:00
}
2021-12-15 14:47:34 +01:00
out += ' } } ) ) . t h e n ( o b j = > w a s m E x p o r t O b j e c t = o b j . i n s t a n c e . e x p o r t s ) ; \n '
for fun in functions {
out += ' g l o b a l T h i s . $ { g . js_name ( fun ) } = w a s m E x p o r t O b j e c t . $ { g . js_name ( fun ) } ; \n '
}
} else {
verror ( ' W e b A s s e m b l y e x p o r t i s s u p p o r t e d o n l y f o r b r o w s e r b a c k e n d a t t h e m o m e n t ' )
2021-10-18 09:56:21 +02:00
}
}
2021-12-15 14:47:34 +01:00
out += ' } \n '
2021-10-18 09:56:21 +02:00
}
2021-03-04 14:02:16 +01:00
// equality check for js objects
// TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js')
2021-03-06 18:09:28 +01:00
// unsafe {
2021-03-04 14:02:16 +01:00
// mut eq_fn := $embed_file('fast_deep_equal.js')
// out += eq_fn.data().vstring()
//}
out += fast_deep_eq_fn
2021-10-18 09:56:21 +02:00
/ *
2021-09-08 19:30:46 +02:00
if pref . is_shared {
// Export, through CommonJS, the module of the entry file if `-shared` was passed
export := nodes [ nodes . len - 1 ] . name
out += ' i f ( t y p e o f m o d u l e = = = " o b j e c t " & & m o d u l e . e x p o r t s ) m o d u l e . e x p o r t s = $ export ; \n '
2021-10-18 09:56:21 +02:00
} * /
2021-09-08 19:30:46 +02:00
out += ' \n '
2021-10-01 20:23:49 +02:00
out += g . out . str ( )
2021-09-08 19:30:46 +02:00
/ *
2021-09-11 13:24:47 +02:00
TODO ( playX ) : Again add support for these doc comments
2020-07-20 18:57:13 +02:00
for node in nodes {
2020-06-20 13:22:49 +02:00
name := g . js_name ( node . name ) . replace ( ' . ' , ' _ ' )
2020-05-31 20:48:31 +02:00
if g . enable_doc {
out += ' / * * @ n a m e s p a c e $ name * / \n '
}
2021-09-08 19:30:46 +02:00
// out += 'const $name = (function ('
2021-02-17 05:19:25 +01:00
mut namespace := g . namespaces [ node . name ]
2021-09-08 19:30:46 +02:00
2021-09-09 19:21:01 +02:00
2021-06-10 07:33:46 +02:00
if g . pref . sourcemap {
// calculate current output start line
mut current_line := u32 ( out . count ( ' \n ' ) + 1 )
mut sm_pos := u32 ( 0 )
for sourcemap_ns_entry in namespace . sourcemap_helper {
// calculate final generated location in output based on position
2021-09-08 19:30:46 +02:00
current_segment := g . out . substr ( int ( sm_pos ) , int ( sourcemap_ns_entry . ns_pos ) )
2021-06-10 07:33:46 +02:00
current_line += u32 ( current_segment . count ( ' \n ' ) )
current_column := if last_nl_pos := current_segment . last_index ( ' \n ' ) {
u32 ( current_segment . len - last_nl_pos - 1 )
} else {
u32 ( 0 )
}
g . sourcemap . add_mapping ( sourcemap_ns_entry . src_path , sourcemap . SourcePosition {
source_line : sourcemap_ns_entry . src_line
source_column : 0 // sourcemap_ns_entry.src_column
} , current_line , current_column , ' ' )
sm_pos = sourcemap_ns_entry . ns_pos
}
}
2021-09-09 19:21:01 +02:00
2021-06-10 07:33:46 +02:00
2020-04-15 23:16:49 +02:00
// public scope
2020-05-31 20:48:31 +02:00
out += ' \n '
2021-09-08 19:30:46 +02:00
} * /
2021-09-11 13:24:47 +02:00
2021-06-10 07:33:46 +02:00
if g . pref . sourcemap {
out += g . create_sourcemap ( )
}
2021-12-05 12:33:53 +01:00
2021-06-10 07:33:46 +02:00
return out
}
fn ( g JsGen ) create_sourcemap ( ) string {
mut sm := g . sourcemap
mut out := ' \n / / # s o u r c e M a p p i n g U R L = d a t a : a p p l i c a t i o n / j s o n ; b a s e 6 4 , '
out += base64 . encode ( sm . to_json ( ) . str ( ) . bytes ( ) )
out += ' \n '
2020-04-15 23:16:49 +02:00
return out
}
2021-08-26 14:20:54 +02:00
pub fn ( mut g JsGen ) gen_js_main_for_tests ( ) {
g . enter_namespace ( ' m a i n ' )
2021-12-15 14:47:34 +01:00
if ! g . pref . output_es5 {
g . write ( ' a s y n c ' )
}
g . writeln ( ' f u n c t i o n j s _ m a i n ( ) { ' )
2021-08-26 14:20:54 +02:00
g . inc_indent ( )
all_tfuncs := g . get_all_test_function_names ( )
g . writeln ( ' ' )
g . writeln ( ' g l o b a l T h i s . V T E S T = 1 ' )
2021-08-28 15:57:33 +02:00
if g . pref . is_stats {
2021-09-16 13:00:15 +02:00
g . writeln ( ' l e t b t = m a i n _ _ s t a r t _ t e s t i n g ( n e w i n t ( $ all_tfuncs . len ) , n e w s t r i n g ( " $ g . pref . path " ) ) ' )
2021-08-28 15:57:33 +02:00
}
2021-08-26 14:20:54 +02:00
for tname in all_tfuncs {
tcname := g . js_name ( tname )
if g . pref . is_stats {
2021-09-16 13:00:15 +02:00
g . writeln ( ' m a i n _ _ B e n c h e d T e s t s _ t e s t i n g _ s t e p _ s t a r t ( b t , n e w s t r i n g ( " $ tcname " ) ) ' )
2021-08-26 14:20:54 +02:00
}
2021-12-05 12:33:53 +01:00
g . writeln ( ' t r y { l e t r e s = $ { tcname } ( ) ; i f ( r e s i n s t a n c e o f P r o m i s e ) { a w a i t r e s ; } } c a t c h ( _ e ) { } ' )
2021-08-26 14:20:54 +02:00
if g . pref . is_stats {
2021-09-16 13:00:15 +02:00
g . writeln ( ' m a i n _ _ B e n c h e d T e s t s _ t e s t i n g _ s t e p _ e n d ( b t ) ; ' )
2021-08-26 14:20:54 +02:00
}
}
g . writeln ( ' ' )
if g . pref . is_stats {
2021-09-16 13:00:15 +02:00
g . writeln ( ' m a i n _ _ B e n c h e d T e s t s _ e n d _ t e s t i n g ( b t ) ; ' )
2021-08-26 14:20:54 +02:00
}
g . dec_indent ( )
2021-09-11 13:24:47 +02:00
g . writeln ( ' } ' )
2021-08-26 14:20:54 +02:00
g . escape_namespace ( )
}
fn ( g & JsGen ) get_all_test_function_names ( ) [ ] string {
mut tfuncs := [ ] string { }
mut tsuite_begin := ' '
mut tsuite_end := ' '
for _ , f in g . table . fns {
2022-02-12 14:06:09 +01:00
if ! f . is_test {
continue
}
2021-08-26 14:20:54 +02:00
if f . name . ends_with ( ' . t e s t s u i t e _ b e g i n ' ) {
tsuite_begin = f . name
continue
}
if f . name . contains ( ' . t e s t _ ' ) {
tfuncs << f . name
continue
}
if f . name . ends_with ( ' . t e s t s u i t e _ e n d ' ) {
tsuite_end = f . name
continue
}
}
mut all_tfuncs := [ ] string { }
if tsuite_begin . len > 0 {
all_tfuncs << tsuite_begin
}
all_tfuncs << tfuncs
if tsuite_end . len > 0 {
all_tfuncs << tsuite_end
}
return all_tfuncs
}
2020-12-07 18:36:22 +01:00
pub fn ( mut g JsGen ) enter_namespace ( name string ) {
if g . namespaces [ name ] == 0 {
2020-04-15 23:16:49 +02:00
// create a new namespace
2020-12-07 18:36:22 +01:00
ns := & Namespace {
name : name
}
g . namespaces [ name ] = ns
g . ns = ns
2020-05-20 16:57:42 +02:00
} else {
2020-12-07 18:36:22 +01:00
g . ns = g . namespaces [ name ]
2020-04-15 23:16:49 +02:00
}
2020-12-07 18:36:22 +01:00
g . inside_builtin = name == ' b u i l t i n '
2020-04-15 23:16:49 +02:00
}
2020-05-17 13:51:18 +02:00
pub fn ( mut g JsGen ) escape_namespace ( ) {
2020-12-12 10:42:07 +01:00
g . ns = & Namespace ( 0 )
2020-12-07 18:36:22 +01:00
g . inside_builtin = false
2020-04-15 23:16:49 +02:00
}
2020-05-17 13:51:18 +02:00
pub fn ( mut g JsGen ) push_pub_var ( s string ) {
2020-12-07 18:36:22 +01:00
g . ns . pub_vars << g . js_name ( s )
2020-04-15 23:16:49 +02:00
}
2020-05-17 13:51:18 +02:00
pub fn ( mut g JsGen ) find_class_methods ( stmts [ ] ast . Stmt ) {
2020-04-15 23:16:49 +02:00
for stmt in stmts {
2020-11-25 12:09:40 +01:00
match stmt {
2020-04-15 23:16:49 +02:00
ast . FnDecl {
2020-07-17 19:13:22 +02:00
if stmt . is_method {
2020-04-15 23:16:49 +02:00
// Found struct method, store it to be generated along with the class.
2020-12-08 17:49:20 +01:00
mut class_name := g . table . get_type_name ( stmt . receiver . typ )
2020-04-15 23:16:49 +02:00
// Workaround until `map[key] << val` works.
2020-04-27 22:53:26 +02:00
mut arr := g . method_fn_decls [ class_name ]
2020-04-15 23:16:49 +02:00
arr << stmt
g . method_fn_decls [ class_name ] = arr
}
}
else { }
}
}
}
2020-05-17 13:51:18 +02:00
pub fn ( mut g JsGen ) init ( ) {
2020-05-20 16:57:42 +02:00
g . definitions . writeln ( ' / / G e n e r a t e d b y t h e V c o m p i l e r \n ' )
2021-08-28 15:57:33 +02:00
// g.definitions.writeln('"use strict";')
2020-04-15 23:16:49 +02:00
g . definitions . writeln ( ' ' )
2021-07-13 14:36:06 +02:00
g . definitions . writeln ( ' v a r \$ g l o b a l = ( n e w F u n c t i o n ( " r e t u r n t h i s " ) ) ( ) ; ' )
2021-12-15 14:47:34 +01:00
if g . pref . output_es5 {
g . definitions . writeln ( ' g l o b a l T h i s = \$ g l o b a l ; ' )
}
2021-08-18 10:33:37 +02:00
g . definitions . writeln ( ' f u n c t i o n \$ r e f ( v a l u e ) { i f ( v a l u e i n s t a n c e o f \$ r e f ) { r e t u r n v a l u e ; } t h i s . v a l = v a l u e ; } ' )
2021-07-24 14:35:17 +02:00
g . definitions . writeln ( ' \$ r e f . p r o t o t y p e . v a l u e O f = f u n c t i o n ( ) { r e t u r n t h i s . v a l ; } ' )
2021-07-19 14:55:03 +02:00
if g . pref . backend != . js_node {
g . definitions . writeln ( ' c o n s t \$ p r o c e s s = { ' )
g . definitions . writeln ( ' a r c h : " j s " , ' )
if g . pref . backend == . js_freestanding {
2021-12-20 14:18:21 +01:00
g . definitions . writeln ( ' p l a t f o r m : " f r e e s t a n d i n g " , ' )
2021-07-19 14:55:03 +02:00
} else {
2021-12-20 14:18:21 +01:00
g . definitions . writeln ( ' p l a t f o r m : " b r o w s e r " , ' )
2021-07-19 14:55:03 +02:00
}
2021-12-20 14:18:21 +01:00
g . definitions . writeln ( ' c w d : f u n c t i o n ( ) { r e t u r n " " } ' )
2021-07-19 14:55:03 +02:00
g . definitions . writeln ( ' } ' )
g . definitions . writeln ( ' c o n s t \$ o s = { ' )
g . definitions . writeln ( ' e n d i a n e s s : " L E " , ' )
g . definitions . writeln ( ' } ' )
} else {
g . definitions . writeln ( ' c o n s t \$ o s = r e q u i r e ( " o s " ) ; ' )
g . definitions . writeln ( ' c o n s t \$ p r o c e s s = p r o c e s s ; ' )
}
2021-10-07 14:55:47 +02:00
g . definitions . writeln ( ' f u n c t i o n c h e c k D e f i n e ( k e y ) { ' )
g . definitions . writeln ( ' \t i f ( g l o b a l T h i s . h a s O w n P r o p e r t y ( k e y ) ) { r e t u r n ! ! g l o b a l T h i s [ k e y ] ; } r e t u r n f a l s e ; ' )
g . definitions . writeln ( ' } ' )
2022-01-14 17:43:18 +01:00
g . definitions . writeln ( ' f u n c t i o n B r e a k E x c e p t i o n ( ) { } ' )
g . definitions . writeln ( ' f u n c t i o n C o n t i n u e E x c e p t i o n ( ) { } ' )
2022-01-15 08:55:03 +01:00
g . definitions . writeln ( ' f u n c t i o n R e t u r n E x c e p t i o n ( v a l ) { t h i s . v a l = v a l ; } ' )
2020-04-15 23:16:49 +02:00
}
pub fn ( g JsGen ) hashes ( ) string {
2021-07-27 11:35:54 +02:00
mut res := ' / / V _ C O M M I T _ H A S H $ version . vhash ( ) \n '
res += ' / / V _ C U R R E N T _ C O M M I T _ H A S H $ { version . githash ( g . pref . building_v ) } \n '
2020-04-15 23:16:49 +02:00
return res
}
2021-07-05 04:05:37 +02:00
[ noreturn ]
2020-12-07 18:36:22 +01:00
fn verror ( msg string ) {
eprintln ( ' j s g e n e r r o r : $ msg ' )
exit ( 1 )
}
[ inline ]
2020-05-17 13:51:18 +02:00
pub fn ( mut g JsGen ) gen_indent ( ) {
2020-12-07 18:36:22 +01:00
if g . ns . indent > 0 && g . empty_line {
2021-09-08 19:30:46 +02:00
g . out . write_string ( util . tabs ( g . ns . indent ) )
2020-04-15 23:16:49 +02:00
}
g . empty_line = false
}
2020-12-07 18:36:22 +01:00
[ inline ]
2020-05-17 13:51:18 +02:00
pub fn ( mut g JsGen ) inc_indent ( ) {
2020-12-07 18:36:22 +01:00
g . ns . indent ++
2020-04-15 23:16:49 +02:00
}
2020-12-07 18:36:22 +01:00
[ inline ]
2020-05-17 13:51:18 +02:00
pub fn ( mut g JsGen ) dec_indent ( ) {
2020-12-07 18:36:22 +01:00
g . ns . indent --
2020-04-15 23:16:49 +02:00
}
2020-12-07 18:36:22 +01:00
[ inline ]
2020-05-17 13:51:18 +02:00
pub fn ( mut g JsGen ) write ( s string ) {
2020-12-29 16:14:08 +01:00
if g . ns == 0 {
verror ( ' g . w r i t e : n o t i n a n a m e s p a c e ' )
}
2020-04-15 23:16:49 +02:00
g . gen_indent ( )
2021-09-08 19:30:46 +02:00
g . out . write_string ( s )
2020-04-15 23:16:49 +02:00
}
2020-12-07 18:36:22 +01:00
[ inline ]
2020-05-17 13:51:18 +02:00
pub fn ( mut g JsGen ) writeln ( s string ) {
2020-12-29 16:14:08 +01:00
if g . ns == 0 {
verror ( ' g . w r i t e l n : n o t i n a n a m e s p a c e ' )
}
2020-04-15 23:16:49 +02:00
g . gen_indent ( )
2021-09-08 19:30:46 +02:00
g . out . writeln ( s )
2020-07-17 19:13:22 +02:00
g . empty_line = true
2020-04-15 23:16:49 +02:00
}
2020-12-07 18:36:22 +01:00
[ inline ]
2020-05-17 13:51:18 +02:00
pub fn ( mut g JsGen ) new_tmp_var ( ) string {
2020-04-15 23:16:49 +02:00
g . tmp_count ++
2020-05-15 15:55:49 +02:00
return ' _ t m p $ g . tmp_count '
}
2020-05-20 16:57:42 +02:00
// 'mod1.mod2.fn' => 'mod1.mod2'
// 'fn' => ''
2020-05-15 15:55:49 +02:00
[ inline ]
2020-05-20 16:57:42 +02:00
fn get_ns ( s string ) string {
2020-12-07 18:36:22 +01:00
idx := s . last_index ( ' . ' ) or { return ' ' }
2020-05-31 20:48:31 +02:00
return s . substr ( 0 , idx )
2020-05-20 16:57:42 +02:00
}
fn ( mut g JsGen ) get_alias ( name string ) string {
2020-05-31 20:48:31 +02:00
ns := get_ns ( name )
2020-07-17 19:13:22 +02:00
if ns == ' ' {
return name
}
2020-12-07 18:36:22 +01:00
alias := g . ns . imports [ ns ]
2020-07-17 19:13:22 +02:00
if alias == ' ' {
return name
}
2020-05-31 20:48:31 +02:00
return alias + ' . ' + name . split ( ' . ' ) . last ( )
2020-05-20 16:57:42 +02:00
}
fn ( mut g JsGen ) js_name ( name_ string ) string {
2021-09-08 19:30:46 +02:00
mut name := name_
if name . starts_with ( ' J S . ' ) {
name = name [ 3 .. ]
return name
}
name = name_ . replace ( ' . ' , ' _ _ ' )
if name in js . js_reserved {
return ' _ v _ $ name '
}
return name
2020-04-15 23:16:49 +02:00
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) stmts ( stmts [ ] ast . Stmt ) {
2020-04-15 23:16:49 +02:00
for stmt in stmts {
g . stmt ( stmt )
}
}
2021-06-10 07:33:46 +02:00
[ inline ]
2022-01-26 11:36:28 +01:00
fn ( mut g JsGen ) write_v_source_line_info ( pos token . Pos ) {
2021-06-10 07:33:46 +02:00
// g.inside_ternary == 0 &&
if g . pref . sourcemap {
g . ns . sourcemap_helper << SourcemapHelper {
src_path : util . vlines_escape_path ( g . file . path , g . pref . ccompiler )
src_line : u32 ( pos . line_nr + 1 )
2021-09-08 19:30:46 +02:00
ns_pos : u32 ( g . out . len )
2021-06-10 07:33:46 +02:00
}
}
if g . pref . is_vlines && g . is_vlines_enabled {
2021-09-08 19:30:46 +02:00
g . write ( ' / * $ { pos . line_nr + 1 } $ g . out . len * / ' )
2021-06-10 07:33:46 +02:00
}
}
2021-07-13 14:36:06 +02:00
fn ( mut g JsGen ) gen_global_decl ( node ast . GlobalDecl ) {
mod := if g . pref . build_mode == . build_module { ' e n u m e r a b l e : f a l s e ' } else { ' e n u m e r a b l e : t r u e ' }
for field in node . fields {
if field . has_expr {
tmp_var := g . new_tmp_var ( )
g . write ( ' c o n s t $ tmp_var = ' )
g . expr ( field . expr )
g . writeln ( ' ; ' )
g . writeln ( ' O b j e c t . d e f i n e P r o p e r t y ( \$ g l o b a l , " $ field . name " , {
configurable : false ,
$ mod ,
writable : true ,
value : $ tmp_var
}
) ; // global')
} else {
// TODO(playXE): Initialize with default value of type
2021-08-15 17:09:51 +02:00
if field . typ . is_ptr ( ) {
g . writeln ( ' O b j e c t . d e f i n e P r o p e r t y ( \$ g l o b a l , " $ field . name " , {
configurable : false ,
$ mod ,
writable : true ,
value : new \ $ ref ( { } )
}
) ; // global')
} else {
g . writeln ( ' O b j e c t . d e f i n e P r o p e r t y ( \$ g l o b a l , " $ field . name " , {
configurable : false ,
$ mod ,
writable : true ,
value : { }
}
) ; // global')
}
2021-07-13 14:36:06 +02:00
}
}
}
2021-09-16 13:00:15 +02:00
fn ( mut g JsGen ) gen_alias_type_decl ( node ast . AliasTypeDecl ) {
name := if g . ns . name == ' b u i l t i n ' { node . name } else { ' $ { g . js_name ( g . ns . name ) } _ _ $ node . name ' }
g . writeln ( ' f u n c t i o n $ { name } ( v a l ) { r e t u r n v a l ; } ' )
}
2022-03-13 08:53:29 +01:00
fn ( mut g JsGen ) stmt_no_semi ( node_ ast . Stmt ) {
2021-09-08 19:30:46 +02:00
g . stmt_start_pos = g . out . len
2022-03-13 08:53:29 +01:00
mut node := unsafe { node_ }
2021-10-28 18:31:56 +02:00
match mut node {
2021-08-25 13:40:53 +02:00
ast . EmptyStmt { }
ast . AsmStmt {
panic ( ' i n l i n e a s m i s n o t s u p p o r t e d b y j s ' )
}
ast . AssertStmt {
g . write_v_source_line_info ( node . pos )
2021-10-28 18:31:56 +02:00
g . gen_assert_stmt ( mut node )
2021-08-25 13:40:53 +02:00
}
ast . AssignStmt {
g . write_v_source_line_info ( node . pos )
g . gen_assign_stmt ( node , false )
}
ast . Block {
g . write_v_source_line_info ( node . pos )
g . gen_block ( node )
g . writeln ( ' ' )
}
ast . BranchStmt {
g . write_v_source_line_info ( node . pos )
g . gen_branch_stmt ( node )
}
2021-11-17 07:29:43 +01:00
ast . ComptimeFor { }
2021-08-25 13:40:53 +02:00
ast . ConstDecl {
g . write_v_source_line_info ( node . pos )
g . gen_const_decl ( node )
}
ast . DeferStmt {
g . defer_stmts << node
}
ast . EnumDecl {
g . write_v_source_line_info ( node . pos )
g . gen_enum_decl ( node )
g . writeln ( ' ' )
}
ast . ExprStmt {
g . write_v_source_line_info ( node . pos )
g . gen_expr_stmt_no_semi ( node )
}
ast . FnDecl {
g . write_v_source_line_info ( node . pos )
2021-10-28 18:31:56 +02:00
2021-08-25 13:40:53 +02:00
g . gen_fn_decl ( node )
}
ast . ForCStmt {
g . write_v_source_line_info ( node . pos )
g . gen_for_c_stmt ( node )
g . writeln ( ' ' )
}
ast . ForInStmt {
g . write_v_source_line_info ( node . pos )
g . gen_for_in_stmt ( node )
g . writeln ( ' ' )
}
ast . ForStmt {
g . write_v_source_line_info ( node . pos )
g . gen_for_stmt ( node )
g . writeln ( ' ' )
}
ast . GlobalDecl {
g . write_v_source_line_info ( node . pos )
g . gen_global_decl ( node )
g . writeln ( ' ' )
}
ast . GotoLabel {
g . write_v_source_line_info ( node . pos )
g . writeln ( ' $ { g . js_name ( node . name ) } : ' )
}
ast . GotoStmt {
// skip: JS has no goto
}
ast . HashStmt {
g . write_v_source_line_info ( node . pos )
g . gen_hash_stmt ( node )
}
ast . Import {
g . ns . imports [ node . mod ] = node . alias
}
ast . InterfaceDecl {
g . write_v_source_line_info ( node . pos )
g . gen_interface_decl ( node )
}
ast . Module {
// skip: namespacing implemented externally
}
ast . NodeError { }
ast . Return {
if g . defer_stmts . len > 0 {
g . gen_defer_stmts ( )
}
g . gen_return_stmt ( node )
}
ast . SqlStmt { }
ast . StructDecl {
g . write_v_source_line_info ( node . pos )
g . gen_struct_decl ( node )
}
2021-08-28 15:57:33 +02:00
ast . TypeDecl { }
2021-08-25 13:40:53 +02:00
}
}
2022-03-13 08:53:29 +01:00
fn ( mut g JsGen ) stmt ( node_ ast . Stmt ) {
2021-09-08 19:30:46 +02:00
g . stmt_start_pos = g . out . len
2022-03-13 08:53:29 +01:00
mut node := unsafe { node_ }
2021-10-28 18:31:56 +02:00
match mut node {
2021-04-02 00:57:09 +02:00
ast . EmptyStmt { }
2021-03-17 01:43:17 +01:00
ast . AsmStmt {
panic ( ' i n l i n e a s m i s n o t s u p p o r t e d b y j s ' )
}
2020-04-15 23:16:49 +02:00
ast . AssertStmt {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2021-10-28 18:31:56 +02:00
g . gen_assert_stmt ( mut node )
2020-04-15 23:16:49 +02:00
}
ast . AssignStmt {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2021-08-25 13:40:53 +02:00
g . gen_assign_stmt ( node , true )
2020-04-15 23:16:49 +02:00
}
ast . Block {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_block ( node )
2020-04-15 23:16:49 +02:00
g . writeln ( ' ' )
}
ast . BranchStmt {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_branch_stmt ( node )
2020-04-15 23:16:49 +02:00
}
2021-11-17 07:29:43 +01:00
ast . ComptimeFor { }
2020-05-24 22:49:01 +02:00
ast . ConstDecl {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_const_decl ( node )
2020-05-24 22:49:01 +02:00
}
2020-04-15 23:16:49 +02:00
ast . DeferStmt {
2020-11-24 13:58:29 +01:00
g . defer_stmts << node
2020-04-15 23:16:49 +02:00
}
ast . EnumDecl {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_enum_decl ( node )
2020-04-15 23:16:49 +02:00
g . writeln ( ' ' )
}
ast . ExprStmt {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_expr_stmt ( node )
2020-04-15 23:16:49 +02:00
}
ast . FnDecl {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_fn_decl ( node )
2020-04-15 23:16:49 +02:00
}
ast . ForCStmt {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_for_c_stmt ( node )
2020-04-15 23:16:49 +02:00
g . writeln ( ' ' )
}
ast . ForInStmt {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_for_in_stmt ( node )
2020-04-15 23:16:49 +02:00
g . writeln ( ' ' )
}
ast . ForStmt {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_for_stmt ( node )
2020-04-15 23:16:49 +02:00
g . writeln ( ' ' )
}
2020-05-24 22:49:01 +02:00
ast . GlobalDecl {
2021-07-13 14:36:06 +02:00
g . write_v_source_line_info ( node . pos )
g . gen_global_decl ( node )
g . writeln ( ' ' )
2020-05-24 22:49:01 +02:00
}
2020-04-15 23:16:49 +02:00
ast . GotoLabel {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . writeln ( ' $ { g . js_name ( node . name ) } : ' )
2020-04-15 23:16:49 +02:00
}
ast . GotoStmt {
// skip: JS has no goto
}
ast . HashStmt {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_hash_stmt ( node )
2020-04-15 23:16:49 +02:00
}
2020-05-17 21:39:01 +02:00
ast . Import {
2020-12-07 18:36:22 +01:00
g . ns . imports [ node . mod ] = node . alias
2020-05-17 21:39:01 +02:00
}
2020-04-15 23:16:49 +02:00
ast . InterfaceDecl {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_interface_decl ( node )
2020-04-15 23:16:49 +02:00
}
2020-05-24 22:49:01 +02:00
ast . Module {
// skip: namespacing implemented externally
}
2021-03-30 09:33:29 +02:00
ast . NodeError { }
2020-04-15 23:16:49 +02:00
ast . Return {
if g . defer_stmts . len > 0 {
g . gen_defer_stmts ( )
}
2020-07-17 19:13:22 +02:00
g . gen_return_stmt ( node )
2020-04-15 23:16:49 +02:00
}
2020-07-17 19:13:22 +02:00
ast . SqlStmt { }
2020-04-15 23:16:49 +02:00
ast . StructDecl {
2021-06-10 07:33:46 +02:00
g . write_v_source_line_info ( node . pos )
2020-07-17 19:13:22 +02:00
g . gen_struct_decl ( node )
2020-04-15 23:16:49 +02:00
}
ast . TypeDecl {
2021-10-28 18:31:56 +02:00
match mut node {
2021-09-16 13:00:15 +02:00
ast . AliasTypeDecl {
g . gen_alias_type_decl ( node )
}
else { }
}
2020-04-15 23:16:49 +02:00
}
}
}
2022-03-13 08:53:29 +01:00
fn ( mut g JsGen ) expr ( node_ ast . Expr ) {
2022-03-06 18:01:22 +01:00
// Note: please keep the type names in the match here in alphabetical order:
2022-03-13 08:53:29 +01:00
mut node := unsafe { node_ }
2021-10-28 18:31:56 +02:00
match mut node {
2022-02-21 16:42:54 +01:00
ast . ComptimeType {
verror ( ' n o t y e t i m p l e m e n t e d ' )
}
2021-03-31 10:13:15 +02:00
ast . EmptyExpr { }
2020-05-24 22:49:01 +02:00
ast . AnonFn {
2021-10-28 18:31:56 +02:00
g . gen_anon_fn ( mut node )
2020-05-24 22:49:01 +02:00
}
2021-10-06 13:32:42 +02:00
ast . ArrayDecompose { }
2020-04-15 23:16:49 +02:00
ast . ArrayInit {
2020-07-17 19:13:22 +02:00
g . gen_array_init_expr ( node )
2020-04-15 23:16:49 +02:00
}
2020-05-24 22:49:01 +02:00
ast . AsCast {
// skip: JS has no types, so no need to cast
// TODO: Is jsdoc needed here for TS support?
}
ast . Assoc {
// TODO
}
2021-10-06 13:32:42 +02:00
ast . AtExpr {
g . write ( ' " $ node . val " ' )
}
2020-04-15 23:16:49 +02:00
ast . BoolLiteral {
2021-09-08 19:30:46 +02:00
g . write ( ' n e w b o o l ( ' )
2020-07-17 19:13:22 +02:00
if node . val == true {
2020-04-15 23:16:49 +02:00
g . write ( ' t r u e ' )
2020-05-20 16:57:42 +02:00
} else {
2020-04-15 23:16:49 +02:00
g . write ( ' f a l s e ' )
}
2021-09-08 19:30:46 +02:00
g . write ( ' ) ' )
2020-04-15 23:16:49 +02:00
}
2020-05-24 22:49:01 +02:00
ast . CallExpr {
2020-07-17 19:13:22 +02:00
g . gen_call_expr ( node )
2020-05-24 22:49:01 +02:00
}
ast . CastExpr {
2020-12-08 17:49:20 +01:00
g . gen_type_cast_expr ( node )
2020-05-24 22:49:01 +02:00
}
2021-10-06 13:32:42 +02:00
ast . ChanInit {
// TODO
}
2020-04-15 23:16:49 +02:00
ast . CharLiteral {
2021-10-06 09:43:49 +02:00
if utf8_str_len ( node . val ) < node . val . len {
g . write ( " n e w r u n e ( ' $ node . val ' . c h a r C o d e A t ( ) ) " )
} else {
2022-04-15 13:58:56 +02:00
g . write ( " n e w u 8 ( ' $ node . val ' ) " )
2021-10-06 09:43:49 +02:00
}
2020-04-15 23:16:49 +02:00
}
2020-07-17 19:13:22 +02:00
ast . Comment { }
2021-10-06 13:32:42 +02:00
ast . ComptimeCall {
// TODO
}
ast . ComptimeSelector {
// TODO
}
2020-05-24 22:49:01 +02:00
ast . ConcatExpr {
// TODO
2020-04-15 23:16:49 +02:00
}
2021-10-06 13:32:42 +02:00
ast . CTempVar {
2021-10-07 14:55:47 +02:00
g . write ( ' $ node . name ' )
2021-10-06 13:32:42 +02:00
}
ast . DumpExpr {
g . write ( ' / * a s t . D u m p E x p r : $ node . expr * / ' )
}
2020-04-15 23:16:49 +02:00
ast . EnumVal {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( node . typ )
2020-05-31 20:48:31 +02:00
styp := g . js_name ( sym . name )
2020-07-17 19:13:22 +02:00
g . write ( ' $ { styp } . $ node . val ' )
2020-04-15 23:16:49 +02:00
}
ast . FloatLiteral {
2021-03-04 14:02:16 +01:00
g . gen_float_literal_expr ( node )
2020-04-15 23:16:49 +02:00
}
2021-01-15 13:45:26 +01:00
ast . GoExpr {
2021-04-11 23:56:25 +02:00
g . gen_go_expr ( node )
2021-01-15 13:45:26 +01:00
}
2020-04-15 23:16:49 +02:00
ast . Ident {
2020-07-17 19:13:22 +02:00
g . gen_ident ( node )
2020-04-15 23:16:49 +02:00
}
ast . IfExpr {
2020-07-17 19:13:22 +02:00
g . gen_if_expr ( node )
2020-04-15 23:16:49 +02:00
}
ast . IfGuardExpr {
// TODO no optionals yet
}
2020-05-24 22:49:01 +02:00
ast . IndexExpr {
2020-07-17 19:13:22 +02:00
g . gen_index_expr ( node )
2020-04-15 23:16:49 +02:00
}
ast . InfixExpr {
2021-09-08 19:30:46 +02:00
g . infix_expr ( node )
2020-05-24 22:49:01 +02:00
}
ast . IntegerLiteral {
2021-03-04 14:02:16 +01:00
g . gen_integer_literal_expr ( node )
2020-04-15 23:16:49 +02:00
}
2021-10-06 13:32:42 +02:00
ast . Likely {
g . write ( ' ( ' )
g . expr ( node . expr )
g . write ( ' ) ' )
}
2020-07-04 12:44:25 +02:00
ast . LockExpr {
2020-07-17 19:13:22 +02:00
g . gen_lock_expr ( node )
2020-07-04 12:44:25 +02:00
}
2021-10-06 13:32:42 +02:00
ast . NodeError { }
2021-08-29 13:27:17 +02:00
ast . None {
2021-09-08 19:30:46 +02:00
g . write ( ' n o n e _ _ ' )
2021-08-29 13:27:17 +02:00
}
2021-10-06 13:32:42 +02:00
ast . MapInit {
g . gen_map_init_expr ( node )
}
2020-05-24 22:49:01 +02:00
ast . MatchExpr {
2021-08-22 18:36:49 +02:00
g . match_expr ( node )
2020-04-15 23:16:49 +02:00
}
2020-05-24 22:49:01 +02:00
ast . OrExpr {
// TODO
}
ast . ParExpr {
2020-06-20 13:22:49 +02:00
g . write ( ' ( ' )
2020-07-17 19:13:22 +02:00
g . expr ( node . expr )
2020-06-20 13:22:49 +02:00
g . write ( ' ) ' )
2020-04-15 23:16:49 +02:00
}
ast . PostfixExpr {
2021-10-01 20:23:49 +02:00
// match node.expr {
// ast.IndexExpr {
// g.gen_postfix_index_expr(node.expr,node.op)
// } else {
2020-07-17 19:13:22 +02:00
g . expr ( node . expr )
2021-08-03 13:59:46 +02:00
if node . op in [ . inc , . dec ] {
g . write ( ' . v a l $ node . op ' )
} else {
g . write ( node . op . str ( ) )
}
2021-10-01 20:23:49 +02:00
// }
// }
2020-04-15 23:16:49 +02:00
}
2020-05-24 22:49:01 +02:00
ast . PrefixExpr {
2020-07-17 19:13:22 +02:00
if node . op in [ . amp , . mul ] {
2021-07-13 21:52:01 +02:00
if node . op == . amp {
2021-08-18 10:33:37 +02:00
// if !node.right_type.is_pointer() {
// kind of weird way to handle references but it allows us to access type methods easily.
2021-09-11 13:24:47 +02:00
/ *
2021-08-18 10:33:37 +02:00
g . write ( ' ( f u n c t i o n ( x ) { ' )
g . write ( ' r e t u r n { v a l : x , _ _ p r o t o _ _ : O b j e c t . g e t P r o t o t y p e O f ( x ) , v a l u e O f : f u n c t i o n ( ) { r e t u r n t h i s . v a l ; } } } ) ( ' )
g . expr ( node . right )
2021-09-11 13:24:47 +02:00
g . write ( ' ) ' ) * /
g . write ( ' n e w \$ r e f ( ' )
g . expr ( node . right )
2021-08-18 10:33:37 +02:00
g . write ( ' ) ' )
//} else {
// g.expr(node.right)
// }
2021-07-13 21:52:01 +02:00
} else {
g . write ( ' ( ' )
g . expr ( node . right )
2021-07-28 12:01:00 +02:00
g . write ( ' ) . v a l u e O f ( ) ' )
2021-07-13 21:52:01 +02:00
}
2020-06-20 13:22:49 +02:00
} else {
2020-07-17 19:13:22 +02:00
g . write ( node . op . str ( ) )
2021-11-15 13:13:44 +01:00
g . expr ( node . right )
g . write ( ' . v a l ' )
2020-06-20 13:22:49 +02:00
}
2020-05-24 22:49:01 +02:00
}
ast . RangeExpr {
// Only used in IndexExpr, requires index type info
2020-04-15 23:16:49 +02:00
}
2020-09-16 15:34:57 +02:00
ast . SelectExpr {
// TODO: to be implemented
}
2020-04-15 23:16:49 +02:00
ast . SelectorExpr {
2020-07-17 19:13:22 +02:00
g . gen_selector_expr ( node )
2020-04-15 23:16:49 +02:00
}
2021-06-13 05:26:13 +02:00
ast . SizeOf , ast . IsRefType {
2020-05-24 22:49:01 +02:00
// TODO
2021-01-30 12:57:09 +01:00
}
ast . OffsetOf {
// TODO
2020-05-20 16:57:42 +02:00
}
2020-07-17 19:13:22 +02:00
ast . SqlExpr {
2020-06-16 12:14:22 +02:00
// TODO
}
2020-05-24 22:49:01 +02:00
ast . StringInterLiteral {
2020-07-17 19:13:22 +02:00
g . gen_string_inter_literal ( node )
2020-04-15 23:16:49 +02:00
}
2020-05-24 22:49:01 +02:00
ast . StringLiteral {
2021-03-04 14:02:16 +01:00
g . gen_string_literal ( node )
2020-04-15 23:16:49 +02:00
}
2020-05-24 22:49:01 +02:00
ast . StructInit {
2021-11-25 15:49:53 +01:00
if node . unresolved {
resolved := ast . resolve_init ( node , g . unwrap_generic ( node . typ ) , g . table )
g . expr ( resolved )
} else {
g . gen_struct_init ( node )
}
2020-04-15 23:16:49 +02:00
}
2021-04-02 00:57:09 +02:00
ast . TypeNode {
2021-08-22 18:36:49 +02:00
typ := g . unwrap_generic ( node . typ )
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( typ )
2021-09-08 19:30:46 +02:00
g . write ( ' $ { g . js_name ( sym . name ) } ' )
2020-05-24 22:49:01 +02:00
}
ast . TypeOf {
2020-07-17 19:13:22 +02:00
g . gen_typeof_expr ( node )
2020-05-24 22:49:01 +02:00
// TODO: Should this print the V type or the JS type?
}
2020-07-12 12:58:33 +02:00
ast . UnsafeExpr {
2020-09-19 18:18:36 +02:00
g . expr ( node . expr )
2020-07-12 12:58:33 +02:00
}
2020-04-15 23:16:49 +02:00
}
2020-05-20 16:57:42 +02:00
}
2020-05-17 21:39:01 +02:00
2021-10-07 14:55:47 +02:00
struct UnsupportedAssertCtempTransform {
2022-02-11 14:52:33 +01:00
Error
2021-10-07 14:55:47 +02:00
}
const unsupported_ctemp_assert_transform = IError ( UnsupportedAssertCtempTransform { } )
fn ( mut g JsGen ) assert_subexpression_to_ctemp ( expr ast . Expr , expr_type ast . Type ) ? ast . Expr {
match expr {
ast . CallExpr {
return g . new_ctemp_var_then_gen ( expr , expr_type )
}
ast . ParExpr {
if expr . expr is ast . CallExpr {
return g . new_ctemp_var_then_gen ( expr . expr , expr_type )
}
}
ast . SelectorExpr {
if expr . expr is ast . CallExpr {
2021-12-19 17:25:18 +01:00
sym := g . table . final_sym ( g . unwrap_generic ( expr . expr . return_type ) )
2021-10-07 14:55:47 +02:00
if sym . kind == . struct_ {
if ( sym . info as ast . Struct ) . is_union {
return js . unsupported_ctemp_assert_transform
}
}
return g . new_ctemp_var_then_gen ( expr , expr_type )
}
}
else { }
}
return js . unsupported_ctemp_assert_transform
}
fn ( mut g JsGen ) new_ctemp_var ( expr ast . Expr , expr_type ast . Type ) ast . CTempVar {
return ast . CTempVar {
name : g . new_tmp_var ( )
typ : expr_type
is_ptr : expr_type . is_ptr ( )
orig : expr
}
}
fn ( mut g JsGen ) new_ctemp_var_then_gen ( expr ast . Expr , expr_type ast . Type ) ast . CTempVar {
x := g . new_ctemp_var ( expr , expr_type )
g . gen_ctemp_var ( x )
return x
}
fn ( mut g JsGen ) gen_ctemp_var ( tvar ast . CTempVar ) {
g . write ( ' l e t $ tvar . name = ' )
g . expr ( tvar . orig )
g . writeln ( ' ; ' )
}
2021-08-26 14:20:54 +02:00
fn ( mut g JsGen ) gen_assert_metainfo ( node ast . AssertStmt ) string {
mod_path := g . file . path
2021-10-28 18:31:56 +02:00
fn_name := if g . fn_decl == voidptr ( 0 ) || g . fn_decl . is_anon { ' a n o n ' } else { g . fn_decl . name }
2021-08-26 14:20:54 +02:00
line_nr := node . pos . line_nr
src := node . expr . str ( )
metaname := ' v _ a s s e r t _ m e t a _ i n f o _ $ g . new_tmp_var ( ) '
g . writeln ( ' l e t $ metaname = { } ' )
2021-09-08 19:30:46 +02:00
g . writeln ( ' $ { metaname } . f p a t h = n e w s t r i n g ( " $ mod_path " ) ; ' )
g . writeln ( ' $ { metaname } . l i n e _ n r = n e w i n t ( " $ line_nr " ) ' )
g . writeln ( ' $ { metaname } . f n _ n a m e = n e w s t r i n g ( " $ fn_name " ) ' )
2021-08-26 14:20:54 +02:00
metasrc := src
g . writeln ( ' $ { metaname } . s r c = " $ metasrc " ' )
2022-03-13 08:53:29 +01:00
match node . expr {
2021-08-26 14:20:54 +02:00
ast . InfixExpr {
expr_op_str := node . expr . op . str ( )
expr_left_str := node . expr . left . str ( )
expr_right_str := node . expr . right . str ( )
2021-09-08 19:30:46 +02:00
g . writeln ( ' \t $ { metaname } . o p = n e w s t r i n g ( " $ expr_op_str " ) ; ' )
g . writeln ( ' \t $ { metaname } . l l a b e l = n e w s t r i n g ( " $ expr_left_str " ) ; ' )
g . writeln ( ' \t $ { metaname } . r l a b e l = n e w s t r i n g ( " $ expr_right_str " ) ; ' )
2021-10-07 14:55:47 +02:00
g . write ( ' \t $ { metaname } . l v a l u e = ' )
2021-08-26 14:20:54 +02:00
g . gen_assert_single_expr ( node . expr . left , node . expr . left_type )
2021-10-07 14:55:47 +02:00
g . writeln ( ' ; ' )
g . write ( ' \t $ { metaname } . r v a l u e = ' )
2021-08-26 14:20:54 +02:00
g . gen_assert_single_expr ( node . expr . right , node . expr . right_type )
2021-10-07 14:55:47 +02:00
g . writeln ( ' ; ' )
2021-08-26 14:20:54 +02:00
}
ast . CallExpr {
2021-09-08 19:30:46 +02:00
g . writeln ( ' \t $ { metaname } . o p = n e w s t r i n g ( " c a l l " ) ; ' )
2021-08-26 14:20:54 +02:00
}
else { }
}
return metaname
}
fn ( mut g JsGen ) gen_assert_single_expr ( expr ast . Expr , typ ast . Type ) {
// eprintln('> gen_assert_single_expr typ: $typ | expr: $expr | typeof(expr): ${typeof(expr)}')
unknown_value := ' * u n k n o w n v a l u e * '
match expr {
ast . CastExpr , ast . IfExpr , ast . IndexExpr , ast . MatchExpr {
2021-10-07 14:55:47 +02:00
g . write ( ' n e w s t r i n g ( " $ unknown_value " ) ' )
2021-08-26 14:20:54 +02:00
}
ast . PrefixExpr {
2021-10-07 14:55:47 +02:00
if expr . right is ast . CastExpr {
// TODO: remove this check;
// vlib/builtin/map_test.v (a map of &int, set to &int(0)) fails
// without special casing ast.CastExpr here
g . write ( ' n e w s t r i n g ( " $ unknown_value " ) ' )
} else {
g . gen_expr_to_string ( expr , typ )
}
2021-08-26 14:20:54 +02:00
}
ast . TypeNode {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( g . unwrap_generic ( typ ) )
2021-10-07 14:55:47 +02:00
g . write ( ' n e w s t r i n g ( " $ sym . name " ' )
2021-08-26 14:20:54 +02:00
}
else {
2021-10-07 14:55:47 +02:00
mut should_clone := true
if typ == ast . string_type && expr is ast . StringLiteral {
should_clone = false
}
if expr is ast . CTempVar {
if expr . orig is ast . CallExpr {
should_clone = false
if expr . orig . or_block . kind == . propagate {
should_clone = true
}
if expr . orig . is_method && expr . orig . args . len == 0
&& expr . orig . name == ' t y p e _ n a m e ' {
should_clone = true
}
}
}
if should_clone {
g . write ( ' s t r i n g _ c l o n e ( ' )
}
g . gen_expr_to_string ( expr , typ )
if should_clone {
g . write ( ' ) ' )
}
2021-08-26 14:20:54 +02:00
}
}
2021-09-29 14:33:14 +02:00
// g.writeln(' /* typeof: ' + expr.type_name() + ' type: ' + typ.str() + ' */ ')
2021-08-26 14:20:54 +02:00
}
2020-05-24 22:49:01 +02:00
// TODO
2021-10-28 18:31:56 +02:00
fn ( mut g JsGen ) gen_assert_stmt ( mut node ast . AssertStmt ) {
2021-10-07 14:55:47 +02:00
if ! node . is_used {
2021-04-13 12:07:57 +02:00
return
}
2021-10-07 14:55:47 +02:00
2020-04-15 23:16:49 +02:00
g . writeln ( ' / / a s s e r t ' )
2021-10-07 14:55:47 +02:00
if mut node . expr is ast . InfixExpr {
if subst_expr := g . assert_subexpression_to_ctemp ( node . expr . left , node . expr . left_type ) {
node . expr . left = subst_expr
}
if subst_expr := g . assert_subexpression_to_ctemp ( node . expr . right , node . expr . right_type ) {
node . expr . right = subst_expr
}
}
2021-10-28 18:31:56 +02:00
2020-04-15 23:16:49 +02:00
g . write ( ' i f ( ' )
2021-10-28 18:31:56 +02:00
g . expr ( node . expr )
2021-09-08 19:30:46 +02:00
g . write ( ' . v a l u e O f ( ) ) { ' )
2021-10-28 18:31:56 +02:00
s_assertion := node . expr . str ( ) . replace ( ' " ' , " ' " )
2020-06-20 13:22:49 +02:00
mut mod_path := g . file . path . replace ( ' \\ ' , ' \\ \\ ' )
2020-04-15 23:16:49 +02:00
if g . is_test {
2021-10-28 18:31:56 +02:00
metaname_ok := g . gen_assert_metainfo ( node )
2020-04-15 23:16:49 +02:00
g . writeln ( ' g _ t e s t _ o k s + + ; ' )
2021-09-08 19:30:46 +02:00
g . writeln ( ' m a i n _ _ c b _ a s s e r t i o n _ o k ( $ metaname_ok ) ; ' )
2020-04-15 23:16:49 +02:00
g . writeln ( ' } e l s e { ' )
2021-10-28 18:31:56 +02:00
metaname_fail := g . gen_assert_metainfo ( node )
2020-04-15 23:16:49 +02:00
g . writeln ( ' g _ t e s t _ f a i l s + + ; ' )
2021-09-08 19:30:46 +02:00
g . writeln ( ' m a i n _ _ c b _ a s s e r t i o n _ f a i l e d ( $ metaname_fail ) ; ' )
2021-09-13 10:44:55 +02:00
g . writeln ( ' b u i l t i n _ _ e x i t ( 1 ) ; ' )
2020-04-15 23:16:49 +02:00
g . writeln ( ' } ' )
return
}
g . writeln ( ' } e l s e { ' )
2020-06-20 13:22:49 +02:00
g . inc_indent ( )
2021-10-28 18:31:56 +02:00
fname := if g . fn_decl == voidptr ( 0 ) || g . fn_decl . is_anon { ' a n o n ' } else { g . fn_decl . name }
g . writeln ( ' b u i l t i n _ _ e p r i n t l n ( n e w s t r i n g ( " $ mod_path : $ { node . pos . line_nr + 1 } : F A I L : f n $ { fname } ( ) : a s s e r t $ s_assertion " ) ) ; ' )
2021-09-13 10:44:55 +02:00
g . writeln ( ' b u i l t i n _ _ e x i t ( 1 ) ; ' )
2020-06-20 13:22:49 +02:00
g . dec_indent ( )
2020-04-15 23:16:49 +02:00
g . writeln ( ' } ' )
}
2021-08-25 13:40:53 +02:00
fn ( mut g JsGen ) gen_assign_stmt ( stmt ast . AssignStmt , semicolon bool ) {
2020-07-08 15:17:28 +02:00
if stmt . left . len > stmt . right . len {
2020-04-15 23:16:49 +02:00
// multi return
2021-10-22 21:03:19 +02:00
g . write ( ' l e t [ ' )
2020-07-08 15:17:28 +02:00
for i , left in stmt . left {
2020-06-16 13:20:16 +02:00
if ! left . is_blank_ident ( ) {
g . expr ( left )
2020-06-09 09:45:50 +02:00
}
2020-07-08 15:17:28 +02:00
if i < stmt . left . len - 1 {
2020-05-31 20:48:31 +02:00
g . write ( ' , ' )
2020-04-15 23:16:49 +02:00
}
}
2020-05-31 20:48:31 +02:00
g . write ( ' ] = ' )
2020-07-08 15:17:28 +02:00
g . expr ( stmt . right [ 0 ] )
2021-08-25 13:40:53 +02:00
if semicolon {
g . writeln ( ' ; ' )
}
2020-05-20 16:57:42 +02:00
} else {
2020-04-15 23:16:49 +02:00
// `a := 1` | `a,b := 1,2`
2020-07-08 15:17:28 +02:00
for i , left in stmt . left {
mut op := stmt . op
2020-07-17 19:13:22 +02:00
if stmt . op == . decl_assign {
op = . assign
}
2021-08-03 13:59:46 +02:00
is_assign := stmt . op in [ . plus_assign , . minus_assign , . mult_assign , . div_assign ,
. xor_assign , . mod_assign , . or_assign , . and_assign , . right_shift_assign ,
2021-09-21 15:20:09 +02:00
. left_shift_assign ]
2021-08-04 11:46:24 +02:00
2020-07-08 15:17:28 +02:00
val := stmt . right [ i ]
2020-06-16 13:20:16 +02:00
mut is_mut := false
if left is ast . Ident {
2020-07-09 17:14:14 +02:00
is_mut = left . is_mut
if left . kind == . blank_ident || left . name in [ ' ' , ' _ ' ] {
2020-06-16 13:20:16 +02:00
tmp_var := g . new_tmp_var ( )
// TODO: Can the tmp_var declaration be omitted?
g . write ( ' c o n s t $ tmp_var = ' )
g . expr ( val )
g . writeln ( ' ; ' )
continue
}
2020-06-09 09:45:50 +02:00
}
2021-11-25 15:49:53 +01:00
mut styp := if stmt . left_types . len > i { g . typ ( stmt . left_types [ i ] ) } else { ' ' }
2021-12-19 17:25:18 +01:00
// l_sym := g.table.sym(stmt.left_types[i])
2020-04-15 23:16:49 +02:00
if ! g . inside_loop && styp . len > 0 {
2020-05-31 20:48:31 +02:00
g . doc . gen_typ ( styp )
2020-04-15 23:16:49 +02:00
}
2020-07-08 15:17:28 +02:00
if stmt . op == . decl_assign {
2020-06-16 13:20:16 +02:00
if g . inside_loop || is_mut {
g . write ( ' l e t ' )
} else {
g . write ( ' c o n s t ' )
}
}
2021-09-26 06:33:53 +02:00
mut array_set := false
2021-10-01 20:23:49 +02:00
mut map_set := false
2021-09-26 06:33:53 +02:00
match left {
ast . IndexExpr {
g . expr ( left . left )
if left . left_type . is_ptr ( ) {
g . write ( ' . v a l u e O f ( ) ' )
}
array_set = true
2021-10-01 20:23:49 +02:00
2021-12-19 17:25:18 +01:00
if g . table . sym ( left . left_type ) . kind == . map {
2021-12-15 14:47:34 +01:00
g . writeln ( ' . l e n g t h + + ; ' )
g . expr ( left . left )
g . write ( ' . m a p [ ' )
2021-10-01 20:23:49 +02:00
map_set = true
2021-09-26 06:33:53 +02:00
} else {
g . write ( ' . a r r . s e t ( ' )
}
2021-10-01 20:23:49 +02:00
if map_set {
g . expr ( left . index )
2021-12-15 14:47:34 +01:00
g . write ( ' . \$ t o J S ( ) ] = ' )
2021-10-01 20:23:49 +02:00
} else {
g . write ( ' n e w i n t ( ' )
g . cast_stack << ast . int_type_idx
g . expr ( left . index )
g . write ( ' . v a l u e O f ( ) ' )
g . cast_stack . delete_last ( )
g . write ( ' ) , ' )
}
2021-09-26 06:33:53 +02:00
}
else {
g . expr ( left )
}
}
2021-07-14 10:43:48 +02:00
mut is_ptr := false
2021-09-26 06:33:53 +02:00
if stmt . op == . assign && stmt . left_types [ i ] . is_ptr ( ) && ! array_set {
2021-07-14 10:43:48 +02:00
is_ptr = true
g . write ( ' . v a l ' )
2021-07-13 21:52:01 +02:00
}
2021-12-23 10:36:42 +01:00
mut floor := false
2021-10-03 09:08:21 +02:00
if false && g . inside_map_set && op == . assign {
2020-06-16 13:20:16 +02:00
g . inside_map_set = false
2021-12-15 14:47:34 +01:00
g . write ( ' ] = ' )
2020-06-16 13:20:16 +02:00
g . expr ( val )
2021-07-14 10:43:48 +02:00
if is_ptr {
g . write ( ' . v a l ' )
}
2020-04-15 23:16:49 +02:00
} else {
2021-09-26 06:33:53 +02:00
if is_assign && array_set {
2021-09-29 14:33:14 +02:00
g . write ( ' n e w $ { styp } ( ' )
2021-12-23 10:36:42 +01:00
2021-09-26 06:33:53 +02:00
g . expr ( left )
2021-12-19 17:25:18 +01:00
l_sym := g . table . sym ( stmt . left_types [ i ] )
2021-08-04 11:46:24 +02:00
if l_sym . kind == . string {
g . write ( ' . s t r ' )
} else {
g . write ( ' . v a l ' )
}
2021-09-26 06:33:53 +02:00
match op {
. plus_assign {
g . write ( ' + ' )
}
. minus_assign {
g . write ( ' - ' )
}
. mult_assign {
g . write ( ' * ' )
}
. div_assign {
g . write ( ' / ' )
}
. mod_assign {
g . write ( ' % ' )
}
. xor_assign {
g . write ( ' ^ ' )
}
. and_assign {
g . write ( ' & ' )
}
. right_shift_assign {
g . write ( ' > > ' )
}
. left_shift_assign {
g . write ( ' < < ' )
}
. or_assign {
g . write ( ' | ' )
}
else {
panic ( ' u n e x p e c t e d o p $ op ' )
}
}
} else if is_assign && ! array_set {
2021-12-19 17:25:18 +01:00
l_sym := g . table . sym ( stmt . left_types [ i ] )
2021-09-26 06:33:53 +02:00
if l_sym . kind == . string {
g . write ( ' . s t r ' )
} else {
g . write ( ' . v a l ' )
}
2021-11-25 15:49:53 +01:00
2021-09-26 06:33:53 +02:00
if ! array_set {
g . write ( ' = ' )
}
2021-12-23 10:36:42 +01:00
if ( l_sym . name != ' f 6 4 ' || l_sym . name != ' f 3 2 ' )
&& ( l_sym . name != ' i 6 4 ' && l_sym . name != ' u 6 4 ' ) && l_sym . name != ' s t r i n g ' {
g . write ( ' M a t h . f l o o r ( ' )
floor = true
}
2021-08-03 13:59:46 +02:00
g . expr ( left )
match op {
. plus_assign {
g . write ( ' + ' )
}
. minus_assign {
g . write ( ' - ' )
}
. mult_assign {
g . write ( ' * ' )
}
. div_assign {
g . write ( ' / ' )
}
. mod_assign {
g . write ( ' % ' )
}
. xor_assign {
g . write ( ' ^ ' )
}
. and_assign {
g . write ( ' & ' )
}
. right_shift_assign {
2021-08-04 11:46:24 +02:00
g . write ( ' > > ' )
2021-08-03 13:59:46 +02:00
}
. left_shift_assign {
2021-08-04 11:46:24 +02:00
g . write ( ' < < ' )
2021-08-03 13:59:46 +02:00
}
. or_assign {
g . write ( ' | ' )
}
else {
panic ( ' u n e x p e c t e d o p $ op ' )
}
}
} else {
2021-09-26 06:33:53 +02:00
if op == . assign && array_set {
} else {
g . write ( ' $ op ' )
}
2021-08-03 13:59:46 +02:00
}
2021-03-04 14:02:16 +01:00
// TODO: Multiple types??
2021-11-25 15:49:53 +01:00
should_cast := if stmt . left_types . len == 0 {
false
} else {
2021-03-14 08:37:38 +01:00
( g . table . type_kind ( stmt . left_types . first ( ) ) in js . shallow_equatables )
2021-11-25 15:49:53 +01:00
&& ( g . cast_stack . len <= 0 || stmt . left_types . first ( ) ! = g . cast_stack . last ( ) )
}
2021-03-14 07:20:01 +01:00
2021-03-04 14:02:16 +01:00
if should_cast {
g . cast_stack << stmt . left_types . first ( )
2021-08-15 17:09:51 +02:00
g . write ( ' n e w ' )
2021-03-04 14:02:16 +01:00
g . write ( ' $ { g . typ ( stmt . left_types . first ( ) ) } ( ' )
}
2020-06-16 13:20:16 +02:00
g . expr ( val )
2021-07-14 10:43:48 +02:00
if is_ptr {
g . write ( ' . v a l ' )
}
2021-03-04 14:02:16 +01:00
if should_cast {
g . write ( ' ) ' )
g . cast_stack . delete_last ( )
}
2021-09-29 14:33:14 +02:00
if is_assign && array_set {
g . write ( ' ) ' )
}
2021-12-23 10:36:42 +01:00
if floor {
g . write ( ' ) ' )
}
2020-04-15 23:16:49 +02:00
}
2021-12-15 14:47:34 +01:00
if array_set && ! map_set {
2021-09-26 06:33:53 +02:00
g . write ( ' ) ' )
}
2021-08-25 13:40:53 +02:00
if semicolon {
if g . inside_loop {
g . write ( ' ; ' )
} else {
g . writeln ( ' ; ' )
}
2020-04-15 23:16:49 +02:00
}
}
}
}
2021-04-02 00:57:09 +02:00
fn ( mut g JsGen ) gen_attrs ( attrs [ ] ast . Attr ) {
2020-08-04 20:10:22 +02:00
for attr in attrs {
g . writeln ( ' / * [ $ attr . name ] * / ' )
}
2020-04-15 23:16:49 +02:00
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_block ( it ast . Block ) {
2020-04-15 23:16:49 +02:00
g . writeln ( ' { ' )
2021-09-16 13:00:15 +02:00
g . inc_indent ( )
2020-04-15 23:16:49 +02:00
g . stmts ( it . stmts )
2021-09-16 13:00:15 +02:00
g . dec_indent ( )
2020-04-15 23:16:49 +02:00
g . writeln ( ' } ' )
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_branch_stmt ( it ast . BranchStmt ) {
2020-04-15 23:16:49 +02:00
// continue or break
2022-01-14 17:43:18 +01:00
if g . inside_or {
match it . kind {
. key_break {
g . writeln ( ' t h r o w n e w B r e a k E x c e p t i o n ( ) ; ' )
}
. key_continue {
g . writeln ( ' t h r o w n e w C o n t i n u e E x c e p t i o n ( ) ; ' )
}
else {
verror ( ' u n e x p e c t e d b r a n c h s t m t : $ it . kind ' )
}
}
return
}
2020-11-04 12:34:12 +01:00
g . write ( it . kind . str ( ) )
2020-04-15 23:16:49 +02:00
g . writeln ( ' ; ' )
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_const_decl ( it ast . ConstDecl ) {
2020-05-31 20:48:31 +02:00
for field in it . fields {
g . doc . gen_const ( g . typ ( field . typ ) )
2020-07-17 19:13:22 +02:00
if field . is_pub {
g . push_pub_var ( field . name )
}
2021-10-22 21:03:19 +02:00
if field . expr is ast . StringInterLiteral || field . expr is ast . StringLiteral
|| field . expr is ast . IntegerLiteral || field . expr is ast . FloatLiteral
|| field . expr is ast . BoolLiteral {
g . write ( ' c o n s t $ { g . js_name ( field . name ) } = ' )
g . expr ( field . expr )
} else {
g . write ( ' l e t $ { g . js_name ( field . name ) } = ' )
g . write ( ' u n d e f i n e d ' )
g . init_global [ g . ns . name ] [ g . js_name ( field . name ) ] = field . expr
}
2020-05-31 20:48:31 +02:00
g . writeln ( ' ; ' )
2020-04-15 23:16:49 +02:00
}
2020-05-31 20:48:31 +02:00
g . writeln ( ' ' )
2020-04-15 23:16:49 +02:00
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_defer_stmts ( ) {
2020-04-15 23:16:49 +02:00
g . writeln ( ' ( f u n c t i o n d e f e r ( ) { ' )
for defer_stmt in g . defer_stmts {
g . stmts ( defer_stmt . stmts )
}
2020-05-17 21:39:01 +02:00
g . defer_stmts = [ ]
2020-04-15 23:16:49 +02:00
g . writeln ( ' } ) ( ) ; ' )
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_enum_decl ( it ast . EnumDecl ) {
2020-05-31 20:48:31 +02:00
g . doc . gen_enum ( )
g . writeln ( ' c o n s t $ { g . js_name ( it . name ) } = { ' )
2020-04-15 23:16:49 +02:00
g . inc_indent ( )
2020-05-31 20:48:31 +02:00
mut i := 0
for field in it . fields {
2020-04-15 23:16:49 +02:00
g . write ( ' $ field . name : ' )
2020-05-31 20:48:31 +02:00
if field . has_expr && field . expr is ast . IntegerLiteral {
2021-06-17 11:27:31 +02:00
i = field . expr . val . int ( )
2020-04-15 23:16:49 +02:00
}
2021-04-27 00:41:42 +02:00
g . writeln ( ' $ i , ' )
i ++
2020-04-15 23:16:49 +02:00
}
g . dec_indent ( )
2020-05-31 20:48:31 +02:00
g . writeln ( ' } ; ' )
2020-04-15 23:16:49 +02:00
if it . is_pub {
g . push_pub_var ( it . name )
}
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_expr_stmt ( it ast . ExprStmt ) {
2020-04-15 23:16:49 +02:00
g . expr ( it . expr )
2021-08-29 13:27:17 +02:00
if ! it . is_expr && it . expr ! is ast . IfExpr && ! g . inside_ternary && ! g . inside_if_optional {
2020-07-17 19:13:22 +02:00
g . writeln ( ' ; ' )
}
2020-04-15 23:16:49 +02:00
}
2021-08-25 13:40:53 +02:00
fn ( mut g JsGen ) gen_expr_stmt_no_semi ( it ast . ExprStmt ) {
g . expr ( it . expr )
}
2021-09-08 19:30:46 +02:00
// cc_type whether to prefix 'struct' or not (C__Foo -> struct Foo)
fn ( mut g JsGen ) cc_type ( typ ast . Type , is_prefix_struct bool ) string {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( g . unwrap_generic ( typ ) )
2021-11-25 15:49:53 +01:00
mut styp := sym . cname . replace ( ' > ' , ' ' ) . replace ( ' < ' , ' ' )
2022-03-13 08:53:29 +01:00
match sym . info {
2021-09-08 19:30:46 +02:00
ast . Struct , ast . Interface , ast . SumType {
if sym . info . is_generic {
mut sgtyps := ' _ T '
for gt in sym . info . generic_types {
2021-12-19 17:25:18 +01:00
gts := g . table . sym ( g . unwrap_generic ( gt ) )
2021-09-08 19:30:46 +02:00
sgtyps += ' _ $ gts . cname '
}
styp += sgtyps
}
}
else { }
}
2021-10-24 14:56:44 +02:00
if styp . starts_with ( ' J S _ _ ' ) {
styp = styp [ 4 .. ]
}
2021-09-08 19:30:46 +02:00
return styp
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_for_c_stmt ( it ast . ForCStmt ) {
2020-04-15 23:16:49 +02:00
g . inside_loop = true
g . write ( ' f o r ( ' )
if it . has_init {
g . stmt ( it . init )
} else {
g . write ( ' ; ' )
}
if it . has_cond {
2021-07-30 10:17:11 +02:00
g . write ( ' + ' ) // convert to number or boolean
2020-04-15 23:16:49 +02:00
g . expr ( it . cond )
}
g . write ( ' ; ' )
if it . has_inc {
2021-08-25 13:40:53 +02:00
g . stmt_no_semi ( it . inc )
2020-04-15 23:16:49 +02:00
}
g . writeln ( ' ) { ' )
2022-01-15 08:55:03 +01:00
g . inc_indent ( )
2022-01-14 17:43:18 +01:00
g . writeln ( ' t r y { ' )
2022-01-15 08:55:03 +01:00
g . inc_indent ( )
2020-04-15 23:16:49 +02:00
g . stmts ( it . stmts )
2022-01-15 08:55:03 +01:00
g . dec_indent ( )
2022-01-14 17:43:18 +01:00
g . writeln ( ' } c a t c h ( e ) { ' )
g . writeln ( ' i f ( e i n s t a n c e o f B r e a k E x c e p t i o n ) { b r e a k ; } ' )
g . writeln ( ' e l s e i f ( e i n s t a n c e o f C o n t i n u e E x c e p t i o n ) { c o n t i n u e ; } ' )
g . writeln ( ' e l s e { t h r o w e ; } } ' )
2022-01-15 08:55:03 +01:00
g . dec_indent ( )
2020-04-15 23:16:49 +02:00
g . writeln ( ' } ' )
2022-01-15 08:55:03 +01:00
2020-04-15 23:16:49 +02:00
g . inside_loop = false
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_for_in_stmt ( it ast . ForInStmt ) {
2020-04-15 23:16:49 +02:00
if it . is_range {
// `for x in 1..10 {`
2020-06-09 09:45:50 +02:00
mut i := it . val_var
2020-07-17 19:13:22 +02:00
if i in [ ' ' , ' _ ' ] {
i = g . new_tmp_var ( )
}
2020-04-15 23:16:49 +02:00
g . inside_loop = true
g . write ( ' f o r ( l e t $ i = ' )
g . expr ( it . cond )
g . write ( ' ; $ i < ' )
g . expr ( it . high )
2021-09-08 19:30:46 +02:00
g . writeln ( ' ; $ i = n e w i n t ( $ i + 1 ) ) { ' )
2020-04-15 23:16:49 +02:00
g . inside_loop = false
2022-01-15 08:55:03 +01:00
g . inc_indent ( )
2022-01-14 17:43:18 +01:00
g . writeln ( ' t r y { ' )
2022-01-15 08:55:03 +01:00
g . inc_indent ( )
2020-04-15 23:16:49 +02:00
g . stmts ( it . stmts )
2022-01-15 08:55:03 +01:00
g . dec_indent ( )
2022-01-14 17:43:18 +01:00
g . writeln ( ' } c a t c h ( e ) { ' )
g . writeln ( ' i f ( e i n s t a n c e o f B r e a k E x c e p t i o n ) { b r e a k ; } ' )
g . writeln ( ' e l s e i f ( e i n s t a n c e o f C o n t i n u e E x c e p t i o n ) { c o n t i n u e ; } ' )
g . writeln ( ' e l s e { t h r o w e ; } } ' )
2022-01-15 08:55:03 +01:00
g . dec_indent ( )
2020-04-15 23:16:49 +02:00
g . writeln ( ' } ' )
2020-06-09 09:45:50 +02:00
} else if it . kind in [ . array , . string ] || it . cond_type . has_flag ( . variadic ) {
2020-04-15 23:16:49 +02:00
// `for num in nums {`
2021-03-14 07:20:01 +01:00
val := if it . val_var in [ ' ' , ' _ ' ] { ' _ ' } else { it . val_var }
2020-04-22 01:52:56 +02:00
// styp := g.typ(it.val_type)
2021-03-14 07:20:01 +01:00
if it . key_var . len > 0 {
g . write ( ' f o r ( c o n s t [ $ it . key_var , $ val ] o f ' )
2021-03-04 14:02:16 +01:00
if it . kind == . string {
2021-03-14 07:20:01 +01:00
g . write ( ' A r r a y . f r o m ( ' )
g . expr ( it . cond )
2021-07-24 14:35:17 +02:00
if it . cond_type . is_ptr ( ) {
g . write ( ' . v a l u e O f ( ) ' )
}
2021-04-20 19:18:53 +02:00
g . write ( ' . s t r . s p l i t ( \' \' ) . e n t r i e s ( ) , ( [ $ it . key_var , $ val ] ) = > [ $ it . key_var , ' )
2021-09-08 19:30:46 +02:00
g . write ( ' n e w ' )
2022-04-15 13:58:56 +02:00
g . write ( ' u 8 ( $ val ) ] ) ' )
2021-03-14 07:20:01 +01:00
} else {
g . expr ( it . cond )
2021-07-24 14:35:17 +02:00
if it . cond_type . is_ptr ( ) {
g . write ( ' . v a l u e O f ( ) ' )
}
2021-03-14 07:20:01 +01:00
g . write ( ' . e n t r i e s ( ) ' )
2021-03-04 14:02:16 +01:00
}
2021-03-14 07:20:01 +01:00
} else {
g . write ( ' f o r ( c o n s t $ val o f ' )
2020-06-09 09:45:50 +02:00
g . expr ( it . cond )
2021-07-24 14:35:17 +02:00
if it . cond_type . is_ptr ( ) {
g . write ( ' . v a l u e O f ( ) ' )
}
2021-03-06 18:09:28 +01:00
if it . kind == . string {
2021-03-14 08:37:38 +01:00
g . write ( " . s t r . s p l i t ( ' ' ) " )
2021-03-14 07:20:01 +01:00
}
// cast characters to bytes
if val ! in [ ' ' , ' _ ' ] && it . kind == . string {
g . write ( ' . m a p ( c = > ' )
2021-09-08 19:30:46 +02:00
g . write ( ' n e w ' )
2022-04-15 13:58:56 +02:00
g . write ( ' u 8 ( c ) ) ' )
2021-03-06 18:09:28 +01:00
}
2020-06-09 09:45:50 +02:00
}
2021-03-14 07:20:01 +01:00
g . writeln ( ' ) { ' )
2022-01-15 08:55:03 +01:00
g . inc_indent ( )
2022-01-14 17:43:18 +01:00
g . writeln ( ' t r y { ' )
2022-01-15 08:55:03 +01:00
g . inc_indent ( )
2020-04-15 23:16:49 +02:00
g . stmts ( it . stmts )
2022-01-15 08:55:03 +01:00
g . dec_indent ( )
2022-01-14 17:43:18 +01:00
g . writeln ( ' } c a t c h ( e ) { ' )
g . writeln ( ' i f ( e i n s t a n c e o f B r e a k E x c e p t i o n ) { b r e a k ; } ' )
g . writeln ( ' e l s e i f ( e i n s t a n c e o f C o n t i n u e E x c e p t i o n ) { c o n t i n u e ; } ' )
g . writeln ( ' e l s e { t h r o w e ; } } ' )
2022-01-15 08:55:03 +01:00
g . dec_indent ( )
2020-04-15 23:16:49 +02:00
g . writeln ( ' } ' )
} else if it . kind == . map {
// `for key, val in map[string]int {`
2020-04-22 01:52:56 +02:00
// key_styp := g.typ(it.key_type)
// val_styp := g.typ(it.val_type)
2020-06-09 09:45:50 +02:00
key := if it . key_var in [ ' ' , ' _ ' ] { ' ' } else { it . key_var }
val := if it . val_var in [ ' ' , ' _ ' ] { ' ' } else { it . val_var }
2021-12-15 14:47:34 +01:00
tmp := g . new_tmp_var ( )
tmp2 := g . new_tmp_var ( )
if g . pref . output_es5 {
tmp3 := g . new_tmp_var ( )
g . write ( ' l e t $ tmp2 = ' )
g . expr ( it . cond )
if it . cond_type . is_ptr ( ) {
g . write ( ' . v a l u e O f ( ) ' )
}
g . writeln ( ' ; ' )
g . write ( ' f o r ( v a r $ tmp3 = 0 ; $ tmp3 < O b j e c t . k e y s ( $ { tmp2 } . m a p ) . l e n g t h ; $ tmp3 + + ) ' )
g . write ( ' { ' )
g . writeln ( ' \t l e t $ tmp = O b j e c t . k e y s ( $ { tmp2 } . m a p ) ' )
g . writeln ( ' \t l e t $ key = $ tmp [ $ tmp3 ] ; ' )
g . writeln ( ' \t l e t $ val = $ { tmp2 } . m a p [ $ tmp [ $ tmp3 ] ] ; ' )
g . inc_indent ( )
2022-01-14 17:43:18 +01:00
g . writeln ( ' t r y { ' )
2021-12-15 14:47:34 +01:00
g . stmts ( it . stmts )
2022-01-14 17:43:18 +01:00
g . writeln ( ' } c a t c h ( e ) { ' )
g . writeln ( ' i f ( e i n s t a n c e o f B r e a k E x c e p t i o n ) { b r e a k ; } ' )
g . writeln ( ' e l s e i f ( e i n s t a n c e o f C o n t i n u e E x c e p t i o n ) { c o n t i n u e ; } ' )
g . writeln ( ' e l s e { t h r o w e ; } } ' )
2021-12-15 14:47:34 +01:00
g . dec_indent ( )
g . writeln ( ' } ' )
} else {
g . write ( ' l e t $ tmp = ' )
g . expr ( it . cond )
if it . cond_type . is_ptr ( ) {
g . write ( ' . v a l u e O f ( ) ' )
}
g . writeln ( ' ; ' )
g . writeln ( ' f o r ( v a r $ tmp2 i n $ { tmp } . m a p ) { ' )
g . inc_indent ( )
g . writeln ( ' l e t $ val = $ { tmp } . m a p [ $ tmp2 ] ; ' )
g . writeln ( ' l e t $ key = $ tmp2 ; ' )
2022-01-15 08:55:03 +01:00
2022-01-14 17:43:18 +01:00
g . writeln ( ' t r y { ' )
2022-01-15 08:55:03 +01:00
g . inc_indent ( )
2021-12-15 14:47:34 +01:00
g . stmts ( it . stmts )
2022-01-15 08:55:03 +01:00
g . dec_indent ( )
2022-01-14 17:43:18 +01:00
g . writeln ( ' } c a t c h ( e ) { ' )
g . writeln ( ' i f ( e i n s t a n c e o f B r e a k E x c e p t i o n ) { b r e a k ; } ' )
g . writeln ( ' e l s e i f ( e i n s t a n c e o f C o n t i n u e E x c e p t i o n ) { c o n t i n u e ; } ' )
g . writeln ( ' e l s e { t h r o w e ; } } ' )
2021-12-15 14:47:34 +01:00
g . dec_indent ( )
g . writeln ( ' } ' )
2021-07-24 14:35:17 +02:00
}
2020-04-15 23:16:49 +02:00
}
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_for_stmt ( it ast . ForStmt ) {
2020-04-15 23:16:49 +02:00
g . write ( ' w h i l e ( ' )
if it . is_inf {
g . write ( ' t r u e ' )
} else {
2021-07-30 10:17:11 +02:00
g . write ( ' + ' ) // convert expr to number or boolean
2020-04-15 23:16:49 +02:00
g . expr ( it . cond )
}
g . writeln ( ' ) { ' )
2022-01-15 08:55:03 +01:00
g . inc_indent ( )
2022-01-14 17:43:18 +01:00
g . writeln ( ' t r y { ' )
2022-01-15 08:55:03 +01:00
g . inc_indent ( )
2020-04-15 23:16:49 +02:00
g . stmts ( it . stmts )
2022-01-15 08:55:03 +01:00
g . dec_indent ( )
2022-01-14 17:43:18 +01:00
g . writeln ( ' } c a t c h ( e ) { ' )
g . writeln ( ' i f ( e i n s t a n c e o f B r e a k E x c e p t i o n ) { b r e a k ; } ' )
g . writeln ( ' e l s e i f ( e i n s t a n c e o f C o n t i n u e E x c e p t i o n ) { c o n t i n u e ; } ' )
g . writeln ( ' e l s e { t h r o w e ; } } ' )
2022-01-15 08:55:03 +01:00
g . dec_indent ( )
2020-04-15 23:16:49 +02:00
g . writeln ( ' } ' )
}
2021-04-11 23:56:25 +02:00
fn ( mut g JsGen ) gen_go_expr ( node ast . GoExpr ) {
2021-12-15 14:47:34 +01:00
if g . pref . output_es5 {
verror ( ' N o s u p p o r t f o r g o r o u t i n e s o n E S 5 o u t p u t ' )
return
}
2021-12-07 10:11:54 +01:00
g . writeln ( ' n e w _ v _ P r o m i s e ( { p r o m i s e : n e w P r o m i s e ( f u n c t i o n ( r e s o l v e ) { ' )
2021-01-15 13:45:26 +01:00
g . inc_indent ( )
2021-11-29 14:32:29 +01:00
g . write ( ' r e s o l v e ( ' )
g . expr ( node . call_expr )
g . write ( ' ) ; ' )
2021-01-15 13:45:26 +01:00
g . dec_indent ( )
2021-12-07 10:11:54 +01:00
g . writeln ( ' } ) } ) ; ' )
2020-04-15 23:16:49 +02:00
}
2020-05-24 22:49:01 +02:00
fn ( mut g JsGen ) gen_import_stmt ( it ast . Import ) {
2020-12-07 18:36:22 +01:00
g . ns . imports [ it . mod ] = it . alias
2020-04-15 23:16:49 +02:00
}
2020-06-20 13:22:49 +02:00
fn ( mut g JsGen ) gen_interface_decl ( it ast . InterfaceDecl ) {
2021-11-11 13:36:32 +01:00
if it . language != . v {
// JS interfaces do not need codegen
return
}
2020-06-20 13:22:49 +02:00
// JS is dynamically typed, so we don't need any codegen at all
// We just need the JSDoc so TypeScript type checking works
g . doc . gen_interface ( it )
// This is a hack to make the interface's type accessible outside its namespace
// TODO: interfaces are always `pub`?
name := g . js_name ( it . name )
2021-08-30 19:47:18 +02:00
g . push_pub_var ( ' / * * @ t y p e $ name * / \n \t \t $ name ' )
2021-10-29 10:23:40 +02:00
g . writeln ( ' f u n c t i o n $ { g . js_name ( it . name ) } ( a r g ) { r e t u r n n e w \$ r e f ( a r g ) ; } ' )
2020-06-20 13:22:49 +02:00
}
2021-08-29 13:27:17 +02:00
fn ( mut g JsGen ) gen_optional_error ( expr ast . Expr ) {
2022-04-15 13:58:56 +02:00
g . write ( ' n e w O p t i o n ( { s t a t e : n e w u 8 ( 2 ) , e r r : ' )
2021-08-29 13:27:17 +02:00
g . expr ( expr )
g . write ( ' } ) ' )
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_return_stmt ( it ast . Return ) {
2021-08-29 13:27:17 +02:00
node := it
2021-12-19 17:25:18 +01:00
// sym := g.table.sym(g.fn_decl.return_type)
2021-08-29 13:27:17 +02:00
fn_return_is_optional := g . fn_decl . return_type . has_flag ( . optional )
if node . exprs . len == 0 {
if fn_return_is_optional {
2022-01-15 08:55:03 +01:00
if g . inside_or {
g . writeln ( ' t h r o w n e w R e t u r n E x c e p t i o n ( { s t a t e : n e w i n t ( 0 ) } ) ; ' )
} else {
g . writeln ( ' r e t u r n { s t a t e : n e w i n t ( 0 ) } ' )
}
2021-08-29 13:27:17 +02:00
} else {
2022-01-15 08:55:03 +01:00
if g . inside_or {
g . writeln ( ' t h r o w n e w R e t u r n E x c e p t i o n ( u n d e f i n e d ) ; ' )
} else {
g . writeln ( ' r e t u r n ; ' )
}
2021-08-29 13:27:17 +02:00
}
2020-05-24 22:49:01 +02:00
return
}
2021-08-29 13:27:17 +02:00
if fn_return_is_optional {
optional_none := node . exprs [ 0 ] is ast . None
ftyp := g . typ ( node . types [ 0 ] )
mut is_regular_option := ftyp == ' O p t i o n '
if optional_none || is_regular_option || node . types [ 0 ] == ast . error_type_idx {
if ! isnil ( g . fn_decl ) && g . fn_decl . is_test {
test_error_var := g . new_tmp_var ( )
g . writeln ( ' l e t $ test_error_var = " T O D O " ; ' )
g . writeln ( ' r e t u r n $ test_error_var ; ' )
return
}
2022-01-15 08:55:03 +01:00
if ! g . inside_or {
g . write ( ' r e t u r n ' )
} else {
g . write ( ' t h r o w n e w R e t u r n E x c e p t i o n ( ' )
}
2021-08-29 13:27:17 +02:00
g . gen_optional_error ( it . exprs [ 0 ] )
2022-01-15 08:55:03 +01:00
if g . inside_or {
g . writeln ( ' ) ' )
}
2021-08-29 13:27:17 +02:00
g . writeln ( ' ; ' )
return
}
}
2021-09-03 11:16:07 +02:00
if fn_return_is_optional {
tmp := g . new_tmp_var ( )
g . write ( ' c o n s t $ tmp = n e w ' )
2021-09-08 19:30:46 +02:00
2021-09-03 11:16:07 +02:00
g . writeln ( ' O p t i o n ( { } ) ; ' )
2022-04-15 13:58:56 +02:00
g . write ( ' $ { tmp } . s t a t e = n e w u 8 ( 0 ) ; ' )
2021-09-03 11:16:07 +02:00
g . write ( ' $ { tmp } . d a t a = ' )
if it . exprs . len == 1 {
g . expr ( it . exprs [ 0 ] )
} else { // Multi return
g . gen_array_init_values ( it . exprs )
}
g . writeln ( ' ' )
2022-01-15 08:55:03 +01:00
if g . inside_or {
g . write ( ' t h r o w n e w R e t u r n E x c e p t i o n ( $ tmp ) ; ' )
} else {
g . write ( ' r e t u r n $ tmp ; ' )
}
2021-09-03 11:16:07 +02:00
return
}
2022-01-15 08:55:03 +01:00
if ! g . inside_or {
g . write ( ' r e t u r n ' )
} else {
g . write ( ' t h r o w n e w R e t u r n E x c e p t i o n ( ' )
}
2020-05-24 22:49:01 +02:00
if it . exprs . len == 1 {
2020-05-20 16:57:42 +02:00
g . expr ( it . exprs [ 0 ] )
2020-06-20 13:22:49 +02:00
} else { // Multi return
g . gen_array_init_values ( it . exprs )
2020-04-15 23:16:49 +02:00
}
2022-01-15 08:55:03 +01:00
if g . inside_or {
g . writeln ( ' ) ' )
}
2020-04-15 23:16:49 +02:00
g . writeln ( ' ; ' )
}
2020-05-28 14:38:10 +02:00
fn ( mut g JsGen ) gen_hash_stmt ( it ast . HashStmt ) {
g . writeln ( it . val )
}
2021-10-28 18:31:56 +02:00
fn ( mut g JsGen ) gen_sumtype_decl ( it ast . SumTypeDecl ) {
name := g . js_name ( it . name )
g . push_pub_var ( ' / * * @ t y p e $ name * / \n \t \t $ name ' )
g . writeln ( ' f u n c t i o n $ { g . js_name ( it . name ) } ( a r g ) { r e t u r n a r g ; } ' )
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_struct_decl ( node ast . StructDecl ) {
2020-12-08 17:49:20 +01:00
mut name := node . name
if name . starts_with ( ' J S . ' ) {
return
}
2021-01-30 09:42:18 +01:00
if name in js . v_types && g . ns . name == ' b u i l t i n ' {
2020-08-04 20:10:22 +02:00
return
}
2020-12-08 17:49:20 +01:00
js_name := g . js_name ( name )
2020-08-04 20:10:22 +02:00
g . gen_attrs ( node . attrs )
2020-05-31 20:48:31 +02:00
g . doc . gen_fac_fn ( node . fields )
2021-12-21 19:23:40 +01:00
if g . pref . output_es5 {
obj := g . new_tmp_var ( )
g . writeln ( ' f u n c t i o n $ { js_name } ( $ obj ) { ' )
g . inc_indent ( )
g . writeln ( ' i f ( $ obj = = = u n d e f i n e d ) { o b j = { } ; } ' )
for field in node . fields {
mut keep := true
for attr in field . attrs {
if attr . name == ' n o i n i t ' {
keep = false
}
2021-10-24 14:56:44 +02:00
}
2021-12-21 19:23:40 +01:00
if keep {
g . writeln ( ' i f ( $ { obj } . $ field . name = = = u n d e f i n e d ) { ' )
g . write ( ' $ { obj } . $ field . name = ' )
if field . has_default_expr {
g . expr ( field . default_expr )
} else {
g . write ( ' $ { g . to_js_typ_val ( field . typ ) } ' )
}
g . writeln ( ' \n } ' )
}
g . writeln ( ' v a r $ field . name = $ { obj } . $ field . name ; ' )
2021-10-24 14:56:44 +02:00
}
2021-12-21 19:23:40 +01:00
g . dec_indent ( )
} else {
g . write ( ' f u n c t i o n $ { js_name } ( { ' )
for i , field in node . fields {
g . write ( ' $ field . name ' )
mut keep := true
for attr in field . attrs {
if attr . name == ' n o i n i t ' {
keep = false
}
}
if keep {
g . write ( ' = ' )
if field . has_default_expr {
g . expr ( field . default_expr )
} else {
g . write ( ' $ { g . to_js_typ_val ( field . typ ) } ' )
}
}
if i < node . fields . len - 1 {
g . write ( ' , ' )
2021-10-24 14:56:44 +02:00
}
2020-05-21 22:36:06 +02:00
}
2021-12-21 19:23:40 +01:00
g . writeln ( ' } ) { ' )
2020-05-21 22:36:06 +02:00
}
2020-04-15 23:16:49 +02:00
g . inc_indent ( )
for field in node . fields {
2020-05-21 22:36:06 +02:00
g . writeln ( ' t h i s . $ field . name = $ field . name ' )
2020-04-15 23:16:49 +02:00
}
g . dec_indent ( )
2020-05-21 22:36:06 +02:00
g . writeln ( ' } ; ' )
2020-12-08 17:49:20 +01:00
g . writeln ( ' $ { js_name } . p r o t o t y p e = { ' )
2020-05-21 22:36:06 +02:00
g . inc_indent ( )
2021-04-26 08:56:03 +02:00
for embed in node . embeds {
etyp := g . typ ( embed . typ )
2021-10-24 14:56:44 +02:00
g . writeln ( ' . . . $ { g . js_name ( etyp ) } . p r o t o t y p e , ' )
2021-04-26 08:56:03 +02:00
}
2021-11-11 13:36:32 +01:00
for iface , iface_types in g . table . iface_types {
if iface . starts_with ( ' J S . ' ) {
for ty in iface_types {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( ty )
2021-11-11 13:36:32 +01:00
if sym . name == node . name {
g . writeln ( ' . . . $ { g . js_name ( iface ) } . p r o t o t y p e , ' )
}
}
}
}
2020-12-08 17:49:20 +01:00
fns := g . method_fn_decls [ name ]
// gen toString method
fn_names := fns . map ( it . name )
2021-02-09 23:00:43 +01:00
if ' t o S t r i n g ' ! in fn_names {
2021-12-22 08:20:45 +01:00
if g . pref . output_es5 {
g . writeln ( ' t o S t r i n g : ( f u n c t i o n ( ) { ' )
} else {
g . writeln ( ' t o S t r i n g ( ) { ' )
}
2020-12-08 17:49:20 +01:00
g . inc_indent ( )
2020-12-29 16:14:08 +01:00
g . write ( ' r e t u r n ` $ js_name { ' )
2020-12-08 17:49:20 +01:00
for i , field in node . fields {
2021-03-14 08:37:38 +01:00
if i == 0 {
g . write ( ' ' )
} else {
g . write ( ' , ' )
}
2020-12-08 17:49:20 +01:00
match g . typ ( field . typ ) . split ( ' . ' ) . last ( ) {
2020-12-29 16:14:08 +01:00
' s t r i n g ' { g . write ( ' $ field . name : " \$ { t h i s [ " $ field . name " ] . t o S t r i n g ( ) } " ' ) }
else { g . write ( ' $ field . name : \$ { t h i s [ " $ field . name " ] . t o S t r i n g ( ) } ' ) }
2020-12-08 17:49:20 +01:00
}
2020-07-17 19:13:22 +02:00
}
2020-12-08 17:49:20 +01:00
g . writeln ( ' } ` ' )
g . dec_indent ( )
2021-12-22 08:20:45 +01:00
if g . pref . output_es5 {
g . writeln ( ' } ) . b i n d ( t h i s ) , ' )
} else {
g . writeln ( ' } , ' )
}
2020-04-15 23:16:49 +02:00
}
2021-09-08 19:30:46 +02:00
for field in node . fields {
typ := g . typ ( field . typ )
g . doc . gen_typ ( typ )
2021-10-24 14:56:44 +02:00
mut keep := true
for attr in field . attrs {
if attr . name == ' n o i n i t ' {
keep = false
}
}
if keep {
g . write ( ' $ field . name : $ { g . to_js_typ_val ( field . typ ) } ' )
g . writeln ( ' , ' )
}
2021-09-08 19:30:46 +02:00
}
2021-12-22 08:20:45 +01:00
if g . pref . output_es5 {
g . writeln ( ' \$ t o J S : ( f u n c t i o n ( ) { r e t u r n t h i s ; } ) . b i n d ( t h i s ) ' )
} else {
g . writeln ( ' \$ t o J S ( ) { r e t u r n t h i s ; } ' )
}
2020-05-21 22:36:06 +02:00
g . writeln ( ' } ; \n ' )
2021-09-08 19:30:46 +02:00
g . dec_indent ( )
2020-04-15 23:16:49 +02:00
if node . is_pub {
2020-12-08 17:49:20 +01:00
g . push_pub_var ( name )
2020-04-15 23:16:49 +02:00
}
}
2020-05-24 22:49:01 +02:00
fn ( mut g JsGen ) gen_array_init_expr ( it ast . ArrayInit ) {
2022-03-06 18:01:22 +01:00
// Note: Fixed arrays and regular arrays are handled the same, since fixed arrays:
2020-06-20 13:22:49 +02:00
// 1) Are only available for number types
// 2) Give the code unnecessary complexity
// 3) Have several limitations like missing most `Array.prototype` methods
// 4) Modern engines can optimize regular arrays into typed arrays anyways,
2020-08-25 18:15:19 +02:00
// offering similar performance
2021-09-26 06:33:53 +02:00
g . write ( ' n e w a r r a y ( n e w a r r a y _ b u f f e r ( { a r r : ' )
2021-07-15 16:36:53 +02:00
g . inc_indent ( )
2021-07-30 10:17:11 +02:00
2020-06-20 13:22:49 +02:00
if it . has_len {
t1 := g . new_tmp_var ( )
2021-09-26 06:33:53 +02:00
g . writeln ( ' ( f u n c t i o n ( l e n g t h ) { ' )
2020-06-20 13:22:49 +02:00
g . inc_indent ( )
g . writeln ( ' c o n s t $ t1 = [ ] ; ' )
2021-10-15 02:57:49 +02:00
g . write ( ' f o r ( l e t i t = 0 ; i t < l e n g t h ' )
g . writeln ( ' ; i t + + ) { ' )
2020-06-20 13:22:49 +02:00
g . inc_indent ( )
g . write ( ' $ { t1 } . p u s h ( ' )
if it . has_default {
g . expr ( it . default_expr )
} else {
// Fill the array with the default values for its type
t := g . to_js_typ_val ( it . elem_type )
g . write ( t )
2020-04-15 23:16:49 +02:00
}
2020-06-20 13:22:49 +02:00
g . writeln ( ' ) ; ' )
g . dec_indent ( )
g . writeln ( ' } ; ' )
g . writeln ( ' r e t u r n $ t1 ; ' )
g . dec_indent ( )
2021-09-26 06:33:53 +02:00
g . write ( ' } ) ( ' )
g . expr ( it . len_expr )
g . write ( ' ) , l e n : n e w i n t ( ' )
g . expr ( it . len_expr )
g . write ( ' ) ' )
g . write ( ' , c a p : n e w i n t ( ' )
g . expr ( it . len_expr )
g . write ( ' ) ' )
2021-07-30 10:17:11 +02:00
} else if it . is_fixed && it . exprs . len == 1 {
2022-04-15 17:35:56 +02:00
// [100]u8 codegen
2021-07-30 10:17:11 +02:00
t1 := g . new_tmp_var ( )
t2 := g . new_tmp_var ( )
g . writeln ( ' ( f u n c t i o n ( ) { ' )
g . inc_indent ( )
g . writeln ( ' c o n s t $ t1 = [ ] ; ' )
g . write ( ' f o r ( l e t $ t2 = 0 ; $ t2 < ' )
g . expr ( it . exprs [ 0 ] )
g . writeln ( ' ; $ t2 + + ) { ' )
g . inc_indent ( )
g . write ( ' $ { t1 } . p u s h ( ' )
if it . has_default {
g . expr ( it . default_expr )
} else {
// Fill the array with the default values for its type
t := g . to_js_typ_val ( it . elem_type )
g . write ( t )
}
g . writeln ( ' ) ; ' )
g . dec_indent ( )
g . writeln ( ' } ; ' )
g . writeln ( ' r e t u r n $ t1 ; ' )
g . dec_indent ( )
2021-09-26 06:33:53 +02:00
g . write ( ' } ) ( ) , l e n : n e w i n t ( ' )
g . expr ( it . exprs [ 0 ] )
g . write ( ' ) , c a p : n e w i n t ( ' )
g . expr ( it . exprs [ 0 ] )
g . write ( ' ) ' )
2020-06-20 13:22:49 +02:00
} else {
2021-09-29 14:33:14 +02:00
styp := g . typ ( it . elem_type )
c := if styp in js . v_types {
g . gen_array_init_values_prim ( it . exprs , styp )
} else {
g . gen_array_init_values ( it . exprs )
}
2021-09-26 06:33:53 +02:00
g . write ( ' , l e n : n e w i n t ( $ c ) , c a p : n e w i n t ( $ c ) ' )
2020-06-20 13:22:49 +02:00
}
2021-07-15 16:36:53 +02:00
g . dec_indent ( )
2021-09-26 06:33:53 +02:00
g . write ( ' } ) ) ' )
2020-06-20 13:22:49 +02:00
}
2021-09-26 06:33:53 +02:00
fn ( mut g JsGen ) gen_array_init_values ( exprs [ ] ast . Expr ) int {
2020-06-20 13:22:49 +02:00
g . write ( ' [ ' )
2021-09-26 06:33:53 +02:00
mut c := 0
2020-06-20 13:22:49 +02:00
for i , expr in exprs {
g . expr ( expr )
if i < exprs . len - 1 {
g . write ( ' , ' )
}
2021-09-26 06:33:53 +02:00
c ++
2020-06-20 13:22:49 +02:00
}
g . write ( ' ] ' )
2021-09-26 06:33:53 +02:00
return c
2020-04-15 23:16:49 +02:00
}
2021-09-29 14:33:14 +02:00
fn ( mut g JsGen ) gen_array_init_values_prim ( exprs [ ] ast . Expr , typ string ) int {
g . write ( ' [ ' )
mut c := 0
for i , expr in exprs {
g . write ( ' n e w $ { typ } ( ' )
g . expr ( expr )
g . write ( ' ) ' )
if i < exprs . len - 1 {
g . write ( ' , ' )
}
c ++
}
g . write ( ' ] ' )
return c
}
2020-05-24 22:49:01 +02:00
fn ( mut g JsGen ) gen_ident ( node ast . Ident ) {
2020-06-09 09:45:50 +02:00
mut name := g . js_name ( node . name )
2020-07-17 19:13:22 +02:00
if node . kind == . blank_ident || name in [ ' ' , ' _ ' ] {
2020-06-09 09:45:50 +02:00
name = g . new_tmp_var ( )
}
2020-05-24 22:49:01 +02:00
// TODO `is`
// TODO handle optionals
g . write ( name )
2021-07-13 21:52:01 +02:00
2020-12-08 17:49:20 +01:00
// TODO: Generate .val for basic types
2020-04-15 23:16:49 +02:00
}
2020-07-04 12:44:25 +02:00
fn ( mut g JsGen ) gen_lock_expr ( node ast . LockExpr ) {
// TODO: implement this
}
2021-08-22 18:36:49 +02:00
fn ( mut g JsGen ) need_tmp_var_in_match ( node ast . MatchExpr ) bool {
if node . is_expr && node . return_type != ast . void_type && node . return_type != 0 {
2021-12-19 17:25:18 +01:00
cond_sym := g . table . final_sym ( node . cond_type )
sym := g . table . sym ( node . return_type )
2021-08-22 18:36:49 +02:00
if sym . kind == . multi_return {
return false
}
2021-10-18 09:59:52 +02:00
if cond_sym . kind == . enum_ && node . branches . len > 5 {
return true
}
2021-08-22 18:36:49 +02:00
for branch in node . branches {
if branch . stmts . len > 1 {
return true
}
if branch . stmts . len == 1 {
if branch . stmts [ 0 ] is ast . ExprStmt {
stmt := branch . stmts [ 0 ] as ast . ExprStmt
2021-09-17 20:59:16 +02:00
if stmt . expr in [ ast . CallExpr , ast . IfExpr , ast . MatchExpr ]
|| ( stmt . expr is ast . IndexExpr
2021-08-22 18:36:49 +02:00
&& ( stmt . expr as ast . IndexExpr ) . or_expr . kind != . absent ) {
return true
}
}
}
}
}
return false
}
2021-08-23 13:25:02 +02:00
fn ( mut g JsGen ) match_expr_classic ( node ast . MatchExpr , is_expr bool , cond_var MatchCond , tmp_var string ) {
2021-12-19 17:25:18 +01:00
type_sym := g . table . sym ( node . cond_type )
2021-08-22 18:36:49 +02:00
for j , branch in node . branches {
is_last := j == node . branches . len - 1
if branch . is_else || ( node . is_expr && is_last && tmp_var . len == 0 ) {
if node . branches . len > 1 {
if is_expr && tmp_var . len == 0 {
// TODO too many branches. maybe separate ?: matches
g . write ( ' : ' )
} else {
g . writeln ( ' ' )
g . write_v_source_line_info ( branch . pos )
g . writeln ( ' e l s e { ' )
}
}
} else {
if j > 0 {
if is_expr && tmp_var . len == 0 {
g . write ( ' : ' )
} else {
g . writeln ( ' ' )
g . write_v_source_line_info ( branch . pos )
g . write ( ' e l s e ' )
}
}
if is_expr && tmp_var . len == 0 {
g . write ( ' ( ' )
} else {
if j == 0 {
g . writeln ( ' ' )
}
g . write_v_source_line_info ( branch . pos )
g . write ( ' i f ( ' )
}
for i , expr in branch . exprs {
if i > 0 {
g . write ( ' | | ' )
}
match type_sym . kind {
. array {
2021-10-12 08:52:16 +02:00
ptr_typ := g . gen_array_equality_fn ( node . cond_type )
g . write ( ' $ { ptr_typ } _ a r r _ e q ( ' )
2021-08-23 13:25:02 +02:00
g . match_cond ( cond_var )
g . write ( ' , ' )
2021-08-22 18:36:49 +02:00
g . expr ( expr )
2021-10-12 08:52:16 +02:00
g . write ( ' ) . v a l ' )
2021-08-22 18:36:49 +02:00
}
. array_fixed {
2021-10-12 08:52:16 +02:00
ptr_typ := g . gen_fixed_array_equality_fn ( node . cond_type )
g . write ( ' $ { ptr_typ } _ a r r _ e q ( ' )
2021-08-23 13:25:02 +02:00
g . match_cond ( cond_var )
g . write ( ' , ' )
2021-08-22 18:36:49 +02:00
g . expr ( expr )
2021-10-12 08:52:16 +02:00
g . write ( ' ) . v a l ' )
2021-08-22 18:36:49 +02:00
}
. map {
2021-10-12 08:52:16 +02:00
ptr_typ := g . gen_map_equality_fn ( node . cond_type )
g . write ( ' $ { ptr_typ } _ m a p _ e q ( ' )
2021-08-23 13:25:02 +02:00
g . match_cond ( cond_var )
g . write ( ' , ' )
2021-08-22 18:36:49 +02:00
g . expr ( expr )
2021-10-12 08:52:16 +02:00
g . write ( ' ) . v a l ' )
2021-08-22 18:36:49 +02:00
}
. string {
2021-08-23 13:25:02 +02:00
g . match_cond ( cond_var )
2021-10-12 08:52:16 +02:00
g . write ( ' . s t r = = = ' )
2021-08-22 18:36:49 +02:00
g . expr ( expr )
2021-10-12 08:52:16 +02:00
g . write ( ' . s t r ' )
2021-08-22 18:36:49 +02:00
}
. struct_ {
2021-10-12 08:52:16 +02:00
ptr_typ := g . gen_struct_equality_fn ( node . cond_type )
g . write ( ' $ { ptr_typ } _ s t r u c t _ e q ( ' )
g . match_cond ( cond_var )
g . write ( ' , ' )
g . expr ( expr )
g . write ( ' ) . v a l ' )
}
. sum_type {
ptr_typ := g . gen_sumtype_equality_fn ( node . cond_type )
g . write ( ' $ { ptr_typ } _ s u m t y p e _ e q ( ' )
2021-08-23 13:25:02 +02:00
g . match_cond ( cond_var )
g . write ( ' , ' )
2021-08-22 18:36:49 +02:00
g . expr ( expr )
2021-10-12 08:52:16 +02:00
g . write ( ' ) . v a l ' )
}
. alias {
ptr_typ := g . gen_alias_equality_fn ( node . cond_type )
g . write ( ' $ { ptr_typ } _ a l i a s _ e q ( ' )
g . match_cond ( cond_var )
g . write ( ' , ' )
g . expr ( expr )
g . write ( ' ) . v a l ' )
2021-08-22 18:36:49 +02:00
}
else {
2021-12-04 17:46:41 +01:00
has_operator_overloading := g . table . has_method ( type_sym , ' = = ' )
2021-10-12 08:52:16 +02:00
if has_operator_overloading {
left := g . unwrap ( node . cond_type )
g . write ( g . typ ( left . unaliased . set_nr_muls ( 0 ) ) )
g . write ( ' _ _ e q ( ' )
g . match_cond ( cond_var )
g . gen_deref_ptr ( node . cond_type )
g . write ( ' , ' )
g . expr ( expr )
g . write ( ' ) ' )
g . write ( ' . v a l u e O f ( ) ' )
} else if expr is ast . RangeExpr {
2021-08-22 18:36:49 +02:00
// if type is unsigned and low is 0, check is unneeded
mut skip_low := false
if expr . low is ast . IntegerLiteral {
if node . cond_type in [ ast . u16_type , ast . u32_type , ast . u64_type ]
&& expr . low . val == ' 0 ' {
skip_low = true
}
}
g . write ( ' ( ' )
if ! skip_low {
2021-08-23 13:25:02 +02:00
g . match_cond ( cond_var )
g . write ( ' > = ' )
2021-08-22 18:36:49 +02:00
g . expr ( expr . low )
g . write ( ' & & ' )
}
2021-08-23 13:25:02 +02:00
g . match_cond ( cond_var )
g . write ( ' < = ' )
2021-08-22 18:36:49 +02:00
g . expr ( expr . high )
g . write ( ' ) ' )
} else {
2021-08-23 13:25:02 +02:00
g . write ( ' v E q ( ' )
g . match_cond ( cond_var )
g . write ( ' , ' )
2021-08-22 18:36:49 +02:00
g . expr ( expr )
g . write ( ' ) ' )
}
}
}
}
if is_expr && tmp_var . len == 0 {
g . write ( ' ) ? ' )
} else {
g . writeln ( ' ) { ' )
}
}
g . stmts_with_tmp_var ( branch . stmts , tmp_var )
if ! g . inside_ternary && node . branches . len >= 1 {
g . write ( ' } ' )
}
}
}
2021-08-23 13:25:02 +02:00
type MatchCond = CondExpr | CondString
struct CondString {
s string
}
struct CondExpr {
expr ast . Expr
}
fn ( mut g JsGen ) match_cond ( cond MatchCond ) {
match cond {
CondString {
2021-10-19 16:02:22 +02:00
g . write ( cond . s )
2021-08-23 13:25:02 +02:00
}
CondExpr {
g . expr ( cond . expr )
}
}
}
2021-08-22 18:36:49 +02:00
fn ( mut g JsGen ) match_expr ( node ast . MatchExpr ) {
if node . cond_type == 0 {
g . writeln ( ' / / m a t c h 0 ' )
return
}
prev := g . inside_ternary
need_tmp_var := g . need_tmp_var_in_match ( node )
is_expr := ( node . is_expr && node . return_type != ast . void_type ) || g . inside_ternary
2021-08-23 13:25:02 +02:00
mut cond_var := MatchCond ( CondString { ' ' } )
2021-08-22 18:36:49 +02:00
mut tmp_var := ' '
2021-10-18 09:59:52 +02:00
mut cur_line := ' '
2021-08-22 18:36:49 +02:00
if is_expr && ! need_tmp_var {
g . inside_ternary = true
}
2022-04-25 07:11:44 +02:00
if node . cond in [ ast . Ident , ast . SelectorExpr , ast . IntegerLiteral , ast . StringLiteral , ast . FloatLiteral ,
ast . CallExpr , ast . EnumVal ] {
2021-08-23 13:25:02 +02:00
cond_var = CondExpr { node . cond }
} else {
s := g . new_tmp_var ( )
cond_var = CondString { s }
g . write ( ' l e t $ s = ' )
g . expr ( node . cond )
g . writeln ( ' ; ' )
}
2021-08-22 18:36:49 +02:00
if need_tmp_var {
2021-10-18 09:59:52 +02:00
g . empty_line = true
cur_line = g . out . cut_to ( g . stmt_start_pos ) . trim_left ( ' \t ' )
2021-08-22 18:36:49 +02:00
tmp_var = g . new_tmp_var ( )
g . writeln ( ' l e t $ tmp_var = u n d e f i n e d ; ' )
}
if is_expr && ! need_tmp_var {
g . write ( ' ( ' )
}
2021-12-19 17:25:18 +01:00
typ := g . table . final_sym ( node . cond_type )
2021-08-22 18:36:49 +02:00
if node . is_sum_type {
g . match_expr_sumtype ( node , is_expr , cond_var , tmp_var )
2021-10-18 09:59:52 +02:00
} else if typ . kind == . enum_ && ! g . inside_loop && node . branches . len > 5 && g . fn_decl != 0 { // do not optimize while in top-level
2021-10-19 16:02:22 +02:00
g . match_expr_switch ( node , is_expr , cond_var , tmp_var , typ )
2021-08-22 18:36:49 +02:00
} else {
g . match_expr_classic ( node , is_expr , cond_var , tmp_var )
}
2021-10-18 09:59:52 +02:00
g . write ( cur_line )
2021-08-22 18:36:49 +02:00
if need_tmp_var {
g . write ( ' $ tmp_var ' )
}
if is_expr && ! need_tmp_var {
g . write ( ' ) ' )
g . inside_ternary = prev
}
}
fn ( mut g JsGen ) stmts_with_tmp_var ( stmts [ ] ast . Stmt , tmp_var string ) {
g . inc_indent ( )
if g . inside_ternary {
g . write ( ' ( ' )
}
2021-10-12 08:52:16 +02:00
prev := g . inside_ternary
2021-08-22 18:36:49 +02:00
for i , stmt in stmts {
if i == stmts . len - 1 && tmp_var != ' ' {
2021-08-29 13:27:17 +02:00
if g . inside_if_optional {
if stmt is ast . ExprStmt {
if stmt . typ == ast . error_type_idx || stmt . expr is ast . None {
g . writeln ( ' $ { tmp_var } . s t a t e = 2 ; ' )
g . write ( ' $ { tmp_var } . e r r = ' )
g . expr ( stmt . expr )
g . writeln ( ' ; ' )
} else {
2021-09-08 19:30:46 +02:00
g . write ( ' o p t _ o k ( ' )
2021-08-29 13:27:17 +02:00
g . stmt ( stmt )
g . writeln ( ' , $ tmp_var ) ; ' )
}
}
} else {
g . write ( ' $ tmp_var = ' )
g . stmt ( stmt )
g . writeln ( ' ' )
}
2021-08-22 18:36:49 +02:00
} else {
g . stmt ( stmt )
2021-08-29 13:27:17 +02:00
if g . inside_if_optional && stmt is ast . ExprStmt {
g . writeln ( ' ; ' )
}
2021-08-22 18:36:49 +02:00
}
if g . inside_ternary && i < stmts . len - 1 {
g . write ( ' , ' )
}
}
g . dec_indent ( )
2021-10-12 08:52:16 +02:00
g . inside_ternary = prev
2021-08-22 18:36:49 +02:00
if g . inside_ternary {
g . write ( ' ) ' )
}
}
2021-08-23 13:25:02 +02:00
fn ( mut g JsGen ) match_expr_sumtype ( node ast . MatchExpr , is_expr bool , cond_var MatchCond , tmp_var string ) {
2021-08-22 18:36:49 +02:00
for j , branch in node . branches {
mut sumtype_index := 0
for {
is_last := j == node . branches . len - 1
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( node . cond_type )
2021-08-22 18:36:49 +02:00
if branch . is_else || ( node . is_expr && is_last && tmp_var . len == 0 ) {
if is_expr && tmp_var . len == 0 {
g . write ( ' : ' )
} else {
g . writeln ( ' ' )
g . writeln ( ' e l s e { ' )
}
} else {
if j > 0 || sumtype_index > 0 {
if is_expr && tmp_var . len == 0 {
g . write ( ' : ' )
} else {
g . write ( ' e l s e ' )
}
}
if is_expr && tmp_var . len == 0 {
g . write ( ' ( ' )
} else {
g . write ( ' i f ( ' )
}
2021-11-20 20:28:11 +01:00
if sym . kind == . sum_type || sym . kind == . interface_ {
x := branch . exprs [ sumtype_index ]
if x is ast . TypeNode {
typ := g . unwrap_generic ( x . typ )
2021-12-19 17:25:18 +01:00
tsym := g . table . sym ( typ )
2021-11-20 20:28:11 +01:00
if tsym . language == . js && ( tsym . name == ' J S . N u m b e r '
|| tsym . name == ' J S . B o o l e a n ' || tsym . name == ' J S . S t r i n g ' ) {
g . write ( ' t y p e o f ' )
}
}
}
2021-08-23 13:25:02 +02:00
g . match_cond ( cond_var )
2021-08-22 18:36:49 +02:00
if sym . kind == . sum_type {
2021-11-20 20:28:11 +01:00
x := branch . exprs [ sumtype_index ]
if x is ast . TypeNode {
typ := g . unwrap_generic ( x . typ )
2021-12-19 17:25:18 +01:00
tsym := g . table . sym ( typ )
2021-11-20 20:28:11 +01:00
if tsym . language == . js && ( tsym . name == ' J S . N u m b e r '
|| tsym . name == ' J S . B o o l e a n ' || tsym . name == ' J S . S t r i n g ' ) {
g . write ( ' = = = " $ { tsym . name [ 3 .. ] . to_lower ( ) } " ' )
} else {
g . write ( ' i n s t a n c e o f ' )
g . expr ( branch . exprs [ sumtype_index ] )
}
} else {
g . write ( ' i n s t a n c e o f ' )
g . expr ( branch . exprs [ sumtype_index ] )
}
2021-08-30 19:47:18 +02:00
} else if sym . kind == . interface_ {
2021-11-11 13:36:32 +01:00
if ! sym . name . starts_with ( ' J S . ' ) {
g . write ( ' . v a l ' )
}
2021-11-20 20:28:11 +01:00
x := branch . exprs [ sumtype_index ]
if x is ast . TypeNode {
typ := g . unwrap_generic ( x . typ )
2021-12-19 17:25:18 +01:00
tsym := g . table . sym ( typ )
2021-11-20 20:28:11 +01:00
if tsym . language == . js && ( tsym . name == ' N u m b e r '
|| tsym . name == ' B o o l e a n ' || tsym . name == ' S t r i n g ' ) {
g . write ( ' = = = $ tsym . name . to_lower ( ) ' )
} else {
g . write ( ' i n s t a n c e o f ' )
g . expr ( branch . exprs [ sumtype_index ] )
}
2021-08-30 19:47:18 +02:00
} else {
g . write ( ' i n s t a n c e o f ' )
g . write ( ' N o n e _ _ ' )
}
2021-08-22 18:36:49 +02:00
}
if is_expr && tmp_var . len == 0 {
g . write ( ' ) ? ' )
} else {
g . writeln ( ' ) { ' )
}
}
g . stmts_with_tmp_var ( branch . stmts , tmp_var )
if ! g . inside_ternary {
g . writeln ( ' } ' )
}
sumtype_index ++
if branch . exprs . len == 0 || sumtype_index == branch . exprs . len {
break
}
}
}
}
2021-10-19 16:02:22 +02:00
fn ( mut g JsGen ) match_expr_switch ( node ast . MatchExpr , is_expr bool , cond_var MatchCond , tmp_var string , enum_typ ast . TypeSymbol ) {
mut range_branches := [ ] ast . MatchBranch { cap : node . branches . len } // branches have RangeExpr cannot emit as switch case branch, we handle it in default branch
mut default_generated := false
2021-10-18 09:59:52 +02:00
g . empty_line = true
g . write ( ' s w i t c h ( ' )
g . match_cond ( cond_var )
g . writeln ( ' ) { ' )
g . inc_indent ( )
for branch in node . branches {
if branch . is_else {
g . writeln ( ' d e f a u l t : ' )
2021-10-19 16:02:22 +02:00
default_generated = true
if range_branches . len > 0 {
g . inc_indent ( )
for range_branch in range_branches {
g . write ( ' i f ( ' )
for i , expr in range_branch . exprs {
if i > 0 {
g . write ( ' | | ' )
}
if expr is ast . RangeExpr {
// if type is unsigned and low is 0, check is unneeded
mut skip_low := false
if expr . low is ast . IntegerLiteral {
if node . cond_type in [ ast . u16_type , ast . u32_type , ast . u64_type ]
&& expr . low . val == ' 0 ' {
skip_low = true
}
}
g . write ( ' ( ' )
if ! skip_low {
g . match_cond ( cond_var )
g . write ( ' > = ' )
g . expr ( expr . low )
g . write ( ' & & ' )
}
g . match_cond ( cond_var )
g . write ( ' < = ' )
g . expr ( expr . high )
g . write ( ' ) ' )
} else {
g . match_cond ( cond_var )
g . write ( ' = = ( ' )
g . expr ( expr )
g . write ( ' ) ' )
}
}
g . writeln ( ' ) { ' )
g . stmts_with_tmp_var ( range_branch . stmts , tmp_var )
2021-10-21 21:30:05 +02:00
g . writeln ( ' b r e a k ; ' )
2021-10-19 16:02:22 +02:00
g . writeln ( ' } ' )
}
g . dec_indent ( )
}
2021-10-18 09:59:52 +02:00
} else {
2021-10-19 16:02:22 +02:00
if branch . exprs . any ( it is ast . RangeExpr ) {
range_branches << branch
continue
}
2021-10-18 09:59:52 +02:00
for expr in branch . exprs {
2021-10-19 16:02:22 +02:00
if expr is ast . EnumVal {
g . write ( ' c a s e ' )
g . expr ( expr )
g . writeln ( ' : ' )
}
2021-10-18 09:59:52 +02:00
}
}
g . inc_indent ( )
g . writeln ( ' { ' )
g . stmts_with_tmp_var ( branch . stmts , tmp_var )
g . writeln ( ' } b r e a k ; ' )
g . dec_indent ( )
}
2021-10-19 16:02:22 +02:00
if range_branches . len > 0 && ! default_generated {
g . writeln ( ' d e f a u l t : ' )
g . inc_indent ( )
for range_branch in range_branches {
g . write ( ' i f ( ' )
for i , expr in range_branch . exprs {
if i > 0 {
g . write ( ' | | ' )
}
if expr is ast . RangeExpr {
// if type is unsigned and low is 0, check is unneeded
mut skip_low := false
if expr . low is ast . IntegerLiteral {
if node . cond_type in [ ast . u16_type , ast . u32_type , ast . u64_type ]
&& expr . low . val == ' 0 ' {
skip_low = true
}
}
g . write ( ' ( ' )
if ! skip_low {
g . match_cond ( cond_var )
g . write ( ' > = ' )
g . expr ( expr . low )
g . write ( ' & & ' )
}
g . match_cond ( cond_var )
g . write ( ' < = ' )
g . expr ( expr . high )
g . write ( ' ) ' )
} else {
g . match_cond ( cond_var )
g . write ( ' = = ( ' )
g . expr ( expr )
g . write ( ' ) ' )
}
}
g . writeln ( ' ) { ' )
g . stmts_with_tmp_var ( range_branch . stmts , tmp_var )
2021-10-21 21:30:05 +02:00
g . writeln ( ' b r e a k ; ' )
2021-10-19 16:02:22 +02:00
g . writeln ( ' } ' )
}
g . dec_indent ( )
}
2021-10-18 09:59:52 +02:00
g . dec_indent ( )
g . writeln ( ' } ' )
}
2021-08-29 13:27:17 +02:00
fn ( mut g JsGen ) need_tmp_var_in_if ( node ast . IfExpr ) bool {
if node . is_expr && g . inside_ternary {
if node . typ . has_flag ( . optional ) {
return true
}
for branch in node . branches {
if branch . cond is ast . IfGuardExpr || branch . stmts . len > 1 {
return true
}
if branch . stmts . len == 1 {
if branch . stmts [ 0 ] is ast . ExprStmt {
stmt := branch . stmts [ 0 ] as ast . ExprStmt
if stmt . expr is ast . CallExpr {
if stmt . expr . is_method {
2021-12-19 17:25:18 +01:00
left_sym := g . table . sym ( stmt . expr . receiver_type )
2021-08-29 13:27:17 +02:00
if left_sym . kind in [ . array , . array_fixed , . map ] {
return true
}
}
}
}
}
}
}
return false
}
2020-05-17 13:51:18 +02:00
fn ( mut g JsGen ) gen_if_expr ( node ast . IfExpr ) {
2021-07-19 14:55:03 +02:00
if node . is_comptime {
2021-11-15 14:47:29 +01:00
g . comptime_if ( node )
2021-07-19 14:55:03 +02:00
return
}
2021-08-29 13:27:17 +02:00
// For simpe if expressions we can use C's `?:`
// `if x > 0 { 1 } else { 2 }` => `(x > 0) ? (1) : (2)`
// For if expressions with multiple statements or another if expression inside, it's much
// easier to use a temp var, than do C tricks with commas, introduce special vars etc
// (as it used to be done).
// Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
needs_tmp_var := g . need_tmp_var_in_if ( node )
tmp := if needs_tmp_var { g . new_tmp_var ( ) } else { ' ' }
if needs_tmp_var {
if node . typ . has_flag ( . optional ) {
g . inside_if_optional = true
}
g . writeln ( ' l e t $ tmp ; / * i f p r e p e n d * / ' )
} else if node . is_expr || g . inside_ternary {
2020-04-15 23:16:49 +02:00
g . write ( ' ( ' )
2021-08-29 13:27:17 +02:00
prev := g . inside_ternary
2020-04-15 23:16:49 +02:00
g . inside_ternary = true
for i , branch in node . branches {
if i > 0 {
g . write ( ' : ' )
}
if i < node . branches . len - 1 || ! node . has_else {
2021-07-30 10:17:11 +02:00
g . write ( ' ( ' )
2020-04-15 23:16:49 +02:00
g . expr ( branch . cond )
2021-08-29 13:27:17 +02:00
g . write ( ' ) . v a l u e O f ( ) ' )
2020-04-15 23:16:49 +02:00
g . write ( ' ? ' )
}
g . stmts ( branch . stmts )
}
2021-08-29 13:27:17 +02:00
g . inside_ternary = prev
2020-04-15 23:16:49 +02:00
g . write ( ' ) ' )
2021-08-29 13:27:17 +02:00
return
}
mut is_guard := false
mut guard_idx := 0
mut guard_vars := [ ] string { }
for i , branch in node . branches {
cond := branch . cond
if cond is ast . IfGuardExpr {
if ! is_guard {
is_guard = true
guard_idx = i
guard_vars = [ ] string { len : node . branches . len }
}
if cond . expr ! is ast . IndexExpr && cond . expr ! is ast . PrefixExpr {
var_name := g . new_tmp_var ( )
guard_vars [ i ] = var_name
g . writeln ( ' l e t $ var_name ; ' )
} else {
guard_vars [ i ] = ' '
}
}
}
for i , branch in node . branches {
if i > 0 {
g . write ( ' } e l s e ' )
}
// if last branch is `else {`
if i == node . branches . len - 1 && node . has_else {
g . writeln ( ' { ' )
// define `err` only for simple `if val := opt {...} else {`
if is_guard && guard_idx == i - 1 {
cvar_name := guard_vars [ guard_idx ]
g . writeln ( ' \t l e t e r r = $ { cvar_name } . e r r ; ' )
}
} else {
match branch . cond {
ast . IfGuardExpr {
mut var_name := guard_vars [ i ]
mut short_opt := false
if var_name == ' ' {
short_opt = true // we don't need a further tmp, so use the one we'll get later
var_name = g . new_tmp_var ( )
guard_vars [ i ] = var_name // for `else`
g . tmp_count --
g . writeln ( ' i f ( $ { var_name } . s t a t e = = 0 ) { ' )
} else {
g . write ( ' i f ( $ var_name = ' )
g . expr ( branch . cond . expr )
g . writeln ( ' , $ { var_name } . s t a t e = = 0 ) { ' )
2020-04-15 23:16:49 +02:00
}
2022-01-25 13:36:33 +01:00
if short_opt || branch . cond . vars [ 0 ] . name != ' _ ' {
2021-08-29 13:27:17 +02:00
if short_opt {
2022-01-25 13:36:33 +01:00
cond_var_name := if branch . cond . vars [ 0 ] . name == ' _ ' {
2021-08-29 13:27:17 +02:00
' _ d u m m y _ $ { g . tmp_count + 1 } '
} else {
2022-01-25 13:36:33 +01:00
branch . cond . vars [ 0 ] . name
2021-08-29 13:27:17 +02:00
}
g . write ( ' \t l e t $ cond_var_name = ' )
g . expr ( branch . cond . expr )
g . writeln ( ' ; ' )
2020-11-21 15:40:37 +01:00
} else {
2022-01-25 13:36:33 +01:00
g . writeln ( ' \t l e t $ branch . cond . vars [ 0 ] . n a m e = $ { var_name } . d a t a ; ' )
2020-11-21 15:40:37 +01:00
}
2020-04-15 23:16:49 +02:00
}
}
2021-08-29 13:27:17 +02:00
else {
g . write ( ' i f ( ( ' )
g . expr ( branch . cond )
g . writeln ( ' ) . v a l u e O f ( ) ) { ' )
}
2020-04-15 23:16:49 +02:00
}
}
2022-01-15 08:55:03 +01:00
g . inc_indent ( )
2021-08-29 13:27:17 +02:00
if needs_tmp_var {
g . stmts_with_tmp_var ( branch . stmts , tmp )
} else {
g . stmts ( branch . stmts )
2020-08-25 18:15:19 +02:00
}
2022-01-15 08:55:03 +01:00
g . dec_indent ( )
2021-08-29 13:27:17 +02:00
}
2021-10-11 13:29:17 +02:00
if node . branches . len > 0 {
g . writeln ( ' } ' )
}
2021-08-29 13:27:17 +02:00
if needs_tmp_var {
g . write ( ' $ tmp ' )
}
if node . typ . has_flag ( . optional ) {
g . inside_if_optional = false
2020-04-15 23:16:49 +02:00
}
}
2020-07-08 15:46:58 +02:00
fn ( mut g JsGen ) gen_index_expr ( expr ast . IndexExpr ) {
2021-12-19 17:25:18 +01:00
left_typ := g . table . sym ( expr . left_type )
2020-05-24 22:49:01 +02:00
// TODO: Handle splice setting if it's implemented
2020-07-08 15:46:58 +02:00
if expr . index is ast . RangeExpr {
2021-10-04 17:28:30 +02:00
if left_typ . kind == . string {
2021-09-08 19:30:46 +02:00
g . write ( ' s t r i n g _ s l i c e ( ' )
2021-10-04 17:28:30 +02:00
} else {
g . write ( ' a r r a y _ s l i c e ( ' )
2021-09-08 19:30:46 +02:00
}
2020-07-08 15:46:58 +02:00
g . expr ( expr . left )
2021-07-24 14:35:17 +02:00
if expr . left_type . is_ptr ( ) {
2021-07-30 10:17:11 +02:00
g . write ( ' . v a l u e O f ( ) ' )
2021-07-24 14:35:17 +02:00
}
2021-09-08 19:30:46 +02:00
g . write ( ' , ' )
2021-09-29 14:33:14 +02:00
2020-11-21 00:05:57 +01:00
if expr . index . has_low {
g . expr ( expr . index . low )
2020-05-24 22:49:01 +02:00
} else {
2021-09-29 14:33:14 +02:00
g . write ( ' n e w i n t ( 0 ) ' )
2020-05-24 22:49:01 +02:00
}
g . write ( ' , ' )
2020-11-21 00:05:57 +01:00
if expr . index . has_high {
g . expr ( expr . index . high )
2020-05-24 22:49:01 +02:00
} else {
2020-07-08 15:46:58 +02:00
g . expr ( expr . left )
2021-07-24 14:35:17 +02:00
if expr . left_type . is_ptr ( ) {
2021-07-30 10:17:11 +02:00
g . write ( ' . v a l u e O f ( ) ' )
2021-07-24 14:35:17 +02:00
}
2021-09-08 19:30:46 +02:00
g . write ( ' . l e n ' )
2020-05-24 22:49:01 +02:00
}
g . write ( ' ) ' )
2020-05-31 20:48:31 +02:00
} else if left_typ . kind == . map {
2020-07-08 15:46:58 +02:00
g . expr ( expr . left )
2021-10-01 20:23:49 +02:00
2020-07-08 15:46:58 +02:00
if expr . is_setter {
2020-05-31 20:48:31 +02:00
g . inside_map_set = true
2021-10-03 09:08:21 +02:00
g . write ( ' . g e t O r S e t ( ' )
2020-05-31 20:48:31 +02:00
} else {
2021-12-15 14:47:34 +01:00
g . write ( ' . g e t ( ' )
2020-05-31 20:48:31 +02:00
}
2020-07-08 15:46:58 +02:00
g . expr ( expr . index )
2021-10-01 20:23:49 +02:00
g . write ( ' . \$ t o J S ( ) ' )
2021-10-03 09:08:21 +02:00
if expr . is_setter {
// g.write(', ${g.to_js_typ_val(left_typ.)')
match left_typ . info {
ast . Map {
g . write ( ' , $ { g . to_js_typ_val ( left_typ . info . value_type ) } ' )
}
else {
verror ( ' u n r e a c h a b l e ' )
}
}
2020-07-17 19:13:22 +02:00
}
2021-10-03 09:08:21 +02:00
g . write ( ' ) ' )
2020-05-31 20:48:31 +02:00
} else if left_typ . kind == . string {
2020-07-08 15:46:58 +02:00
if expr . is_setter {
2020-05-31 20:48:31 +02:00
// TODO: What's the best way to do this?
// 'string'[3] = `o`
} else {
2021-07-18 08:00:20 +02:00
// TODO: Maybe use u16 there? JS String returns values up to 2^16-1
2022-04-15 13:58:56 +02:00
g . write ( ' n e w u 8 ( ' )
2020-07-08 15:46:58 +02:00
g . expr ( expr . left )
2021-07-24 14:35:17 +02:00
if expr . left_type . is_ptr ( ) {
2021-07-30 10:17:11 +02:00
g . write ( ' . v a l u e O f ( ) ' )
2021-07-24 14:35:17 +02:00
}
2020-12-08 17:49:20 +01:00
g . write ( ' . s t r . c h a r C o d e A t ( ' )
2020-07-08 15:46:58 +02:00
g . expr ( expr . index )
2021-07-18 08:00:20 +02:00
g . write ( ' ) ) ' )
2020-05-31 20:48:31 +02:00
}
2020-05-24 22:49:01 +02:00
} else {
2020-05-31 20:48:31 +02:00
// TODO Does this cover all cases?
2020-07-08 15:46:58 +02:00
g . expr ( expr . left )
2021-07-24 14:35:17 +02:00
if expr . left_type . is_ptr ( ) {
2021-07-30 10:17:11 +02:00
g . write ( ' . v a l u e O f ( ) ' )
2021-07-24 14:35:17 +02:00
}
2021-09-26 06:33:53 +02:00
g . write ( ' . a r r . g e t ( ' )
g . write ( ' n e w i n t ( ' )
2021-04-02 00:57:09 +02:00
g . cast_stack << ast . int_type_idx
2020-07-08 15:46:58 +02:00
g . expr ( expr . index )
2021-09-16 13:00:15 +02:00
g . write ( ' . v a l u e O f ( ) ' )
2021-03-04 14:02:16 +01:00
g . cast_stack . delete_last ( )
2021-09-26 06:33:53 +02:00
g . write ( ' ) ) ' )
2020-05-24 22:49:01 +02:00
}
}
2021-08-18 10:33:37 +02:00
fn ( mut g JsGen ) gen_deref_ptr ( ty ast . Type ) {
mut t := ty
for t . is_ptr ( ) {
2021-10-27 22:18:09 +02:00
g . write ( ' . v a l u e O f ( ) ' )
2021-08-18 10:33:37 +02:00
t = t . deref ( )
}
}
2021-09-26 06:33:53 +02:00
fn ( mut g JsGen ) expr_string ( expr ast . Expr ) string {
pos := g . out . len
g . expr ( expr )
return g . out . cut_to ( pos ) . trim_space ( )
}
2020-05-24 22:49:01 +02:00
fn ( mut g JsGen ) gen_infix_expr ( it ast . InfixExpr ) {
2021-12-19 17:25:18 +01:00
l_sym := g . table . final_sym ( it . left_type )
r_sym := g . table . final_sym ( it . right_type )
2021-03-04 14:02:16 +01:00
is_not := it . op in [ . not_in , . not_is , . ne ]
2021-03-06 18:09:28 +01:00
if is_not {
g . write ( ' ! ( ' )
}
2021-08-15 17:09:51 +02:00
is_arithmetic := it . op in [ token . Kind . plus , . minus , . mul , . div , . mod , . right_shift , . left_shift ,
. amp , . pipe , . xor ]
2021-08-28 15:57:33 +02:00
2021-12-15 14:47:34 +01:00
if ! g . pref . output_es5 && is_arithmetic && ( ( l_sym . kind == . i64 || l_sym . kind == . u64 )
2021-08-13 20:24:10 +02:00
|| ( r_sym . kind == . i64 || r_sym . kind == . u64 ) ) {
// if left or right is i64 or u64 we convert them to bigint to perform operation.
2021-08-28 15:57:33 +02:00
greater_typ := if l_sym . kind == . i64 || l_sym . kind == . u64 {
it . left_type
} else {
it . right_type
} // g.greater_typ(it.left_type, it.right_type)
2021-08-15 17:09:51 +02:00
g . write ( ' n e w ' )
2021-09-08 19:30:46 +02:00
2021-08-13 20:24:10 +02:00
g . write ( ' $ { g . typ ( greater_typ ) } ( ' )
g . cast_stack << greater_typ
g . write ( ' B i g I n t ( ( ' )
g . expr ( it . left )
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . left_type )
2021-08-13 20:24:10 +02:00
g . write ( ' ) . \$ t o J S ( ) ) ' )
g . write ( ' $ it . op ' )
g . write ( ' B i g I n t ( ( ' )
g . expr ( it . right )
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . right_type )
2021-08-13 20:24:10 +02:00
g . write ( ' ) . \$ t o J S ( ) ) ' )
g . cast_stack . delete_last ( )
g . write ( ' ) ' )
if is_not {
g . write ( ' ) ' )
}
return
}
2021-08-20 00:14:49 +02:00
if it . op == . logical_or || it . op == . and {
2021-09-08 19:30:46 +02:00
g . write ( ' n e w b o o l ( ' )
2021-08-20 00:14:49 +02:00
g . expr ( it . left )
g . write ( ' . v a l u e O f ( ) ' )
g . write ( it . op . str ( ) )
g . expr ( it . right )
g . write ( ' . v a l u e O f ( ) ' )
g . write ( ' ) ' )
} else if it . op == . eq || it . op == . ne {
2021-09-08 19:30:46 +02:00
node := it
left := g . unwrap ( node . left_type )
right := g . unwrap ( node . right_type )
2021-12-04 17:46:41 +01:00
has_operator_overloading := g . table . has_method ( left . sym , ' = = ' )
2021-09-08 19:30:46 +02:00
if has_operator_overloading
|| ( l_sym . kind in js . shallow_equatables && r_sym . kind in js . shallow_equatables ) {
if node . op == . ne {
g . write ( ' ! ' )
}
g . write ( g . typ ( left . unaliased . set_nr_muls ( 0 ) ) )
g . write ( ' _ _ e q ( ' )
g . expr ( node . left )
g . gen_deref_ptr ( left . typ )
g . write ( ' , ' )
g . expr ( node . right )
g . gen_deref_ptr ( right . typ )
g . write ( ' ) ' )
} else {
g . write ( ' v E q ( ' )
g . expr ( it . left )
g . gen_deref_ptr ( it . left_type )
g . write ( ' , ' )
g . expr ( it . right )
g . gen_deref_ptr ( it . right_type )
g . write ( ' ) ' )
2021-03-04 14:02:16 +01:00
}
} else if l_sym . kind == . array && it . op == . left_shift { // arr << 1
2021-09-29 14:33:14 +02:00
g . write ( ' a r r a y _ p u s h ( ' )
2020-05-25 18:34:42 +02:00
g . expr ( it . left )
2021-08-18 10:33:37 +02:00
mut ltyp := it . left_type
for ltyp . is_ptr ( ) {
g . write ( ' . v a l ' )
ltyp = ltyp . deref ( )
}
2021-09-26 06:33:53 +02:00
g . write ( ' . a r r . a r r , ' )
2021-07-28 12:01:00 +02:00
array_info := l_sym . info as ast . Array
2021-03-04 14:02:16 +01:00
// arr << [1, 2]
2021-07-28 12:01:00 +02:00
if r_sym . kind == . array && array_info . elem_type != it . right_type {
2020-07-17 19:13:22 +02:00
g . write ( ' . . . ' )
2020-08-25 18:15:19 +02:00
}
2020-05-25 18:34:42 +02:00
g . expr ( it . right )
g . write ( ' ) ' )
2020-06-20 13:22:49 +02:00
} else if r_sym . kind in [ . array , . map , . string ] && it . op in [ . key_in , . not_in ] {
2020-05-31 20:48:31 +02:00
g . expr ( it . right )
2021-08-18 10:33:37 +02:00
mut ltyp := it . right_type
for ltyp . is_ptr ( ) {
g . write ( ' . v a l ' )
ltyp = ltyp . deref ( )
}
2021-03-14 08:37:38 +01:00
if r_sym . kind == . map {
2021-07-15 16:36:53 +02:00
g . write ( ' . m a p . h a s ( ' )
2020-12-08 17:49:20 +01:00
} else if r_sym . kind == . string {
2021-03-14 08:37:38 +01:00
g . write ( ' . s t r . i n c l u d e s ( ' )
2020-07-17 19:13:22 +02:00
} else {
2021-07-30 10:17:11 +02:00
g . write ( ' . \$ i n c l u d e s ( ' )
2021-03-14 08:37:38 +01:00
}
2020-05-31 20:48:31 +02:00
g . expr ( it . left )
2021-03-06 18:09:28 +01:00
if l_sym . kind == . string {
g . write ( ' . s t r ' )
}
2020-05-31 20:48:31 +02:00
g . write ( ' ) ' )
2020-06-20 13:22:49 +02:00
} else if it . op in [ . key_is , . not_is ] { // foo is Foo
2020-05-25 18:34:42 +02:00
g . expr ( it . left )
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . left_type )
2020-06-20 13:22:49 +02:00
g . write ( ' i n s t a n c e o f ' )
g . write ( g . typ ( it . right_type ) )
2021-12-04 17:46:41 +01:00
} else if it . op in [ . lt , . gt , . ge , . le ] && g . table . has_method ( l_sym , ' < ' )
2021-08-13 09:06:59 +02:00
&& l_sym . kind == r_sym . kind {
if it . op in [ . le , . ge ] {
g . write ( ' ! ' )
}
if it . op in [ . lt , . ge ] {
g . expr ( it . left )
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . left_type )
2021-08-13 09:06:59 +02:00
g . write ( ' . \$ l t ( ' )
g . expr ( it . right )
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . right_type )
2021-08-13 09:06:59 +02:00
g . write ( ' ) ' )
} else {
g . expr ( it . right )
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . right_type )
2021-08-13 09:06:59 +02:00
g . write ( ' . \$ l t ( ' )
g . expr ( it . left )
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . left_type )
2021-08-13 09:06:59 +02:00
g . write ( ' ) ' )
}
2020-05-25 18:34:42 +02:00
} else {
2021-12-04 17:46:41 +01:00
has_operator_overloading := g . table . has_method ( l_sym , it . op . str ( ) )
2021-08-13 09:06:59 +02:00
if has_operator_overloading {
g . expr ( it . left )
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . left_type )
2021-08-13 09:06:59 +02:00
name := match it . op . str ( ) {
' + ' {
' \$ a d d '
}
' - ' {
' \$ s u b '
}
' / ' {
' \$ d i v '
}
' * ' {
' \$ m u l '
}
' % ' {
' \$ m o d '
}
else {
panic ( ' u n r e a c h a b l e ' )
' '
}
}
g . write ( ' . $ name ( ' )
g . expr ( it . right )
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . right_type )
2021-08-13 09:06:59 +02:00
g . write ( ' ) ' )
} else {
mut greater_typ := 0
// todo(playX): looks like this cast is always required to perform .eq operation on types.
if is_arithmetic {
greater_typ = g . greater_typ ( it . left_type , it . right_type )
if g . cast_stack . len > 0 {
// needs_cast = g.cast_stack.last() != greater_typ
}
2021-03-14 07:20:01 +01:00
}
2021-03-14 08:37:38 +01:00
2021-08-13 09:06:59 +02:00
if is_arithmetic {
2021-09-08 19:30:46 +02:00
g . write ( ' n e w ' )
2021-08-13 09:06:59 +02:00
g . write ( ' $ { g . typ ( greater_typ ) } ( ' )
g . cast_stack << greater_typ
2021-03-04 14:02:16 +01:00
}
2021-08-03 13:59:46 +02:00
2021-08-13 09:06:59 +02:00
g . expr ( it . left )
2021-09-08 19:30:46 +02:00
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . left_type )
2021-08-20 00:14:49 +02:00
// g.write('.val')
2021-08-13 09:06:59 +02:00
g . write ( ' $ it . op ' )
2021-08-03 13:59:46 +02:00
2021-08-13 09:06:59 +02:00
g . expr ( it . right )
2021-08-18 10:33:37 +02:00
g . gen_deref_ptr ( it . right_type )
2021-08-20 00:14:49 +02:00
// g.write('.val')
2021-03-04 14:02:16 +01:00
2021-08-13 09:06:59 +02:00
if is_arithmetic {
g . cast_stack . delete_last ( )
g . write ( ' ) ' )
}
2020-07-06 15:24:24 +02:00
}
2020-05-25 18:34:42 +02:00
}
2021-03-04 14:02:16 +01:00
2021-03-06 18:09:28 +01:00
if is_not {
g . write ( ' ) ' )
}
2020-04-15 23:16:49 +02:00
}
2021-04-02 00:57:09 +02:00
fn ( mut g JsGen ) greater_typ ( left ast . Type , right ast . Type ) ast . Type {
2020-12-08 17:49:20 +01:00
l := int ( left )
r := int ( right )
2020-12-29 16:14:08 +01:00
lr := [ l , r ]
2021-04-02 00:57:09 +02:00
if ast . string_type_idx in lr {
return ast . Type ( ast . string_type_idx )
2020-12-29 16:14:08 +01:00
}
2021-04-02 00:57:09 +02:00
should_float := ( l in ast . integer_type_idxs && r in ast . float_type_idxs )
|| ( r in ast . integer_type_idxs && l in ast . float_type_idxs )
2020-12-08 17:49:20 +01:00
if should_float {
2021-04-02 00:57:09 +02:00
if ast . f64_type_idx in lr {
return ast . Type ( ast . f64_type_idx )
2020-12-29 16:14:08 +01:00
}
2021-04-02 00:57:09 +02:00
if ast . f32_type_idx in lr {
return ast . Type ( ast . f32_type_idx )
2020-12-29 16:14:08 +01:00
}
2021-04-02 00:57:09 +02:00
return ast . Type ( ast . float_literal_type )
2020-12-08 17:49:20 +01:00
}
2021-04-02 00:57:09 +02:00
should_int := ( l in ast . integer_type_idxs && r in ast . integer_type_idxs )
2020-12-08 17:49:20 +01:00
if should_int {
2021-08-13 20:24:10 +02:00
if ast . u64_type_idx in lr {
return ast . Type ( ast . u64_type_idx )
}
2020-12-08 17:49:20 +01:00
// just guessing this order
2021-04-02 00:57:09 +02:00
if ast . i64_type_idx in lr {
return ast . Type ( ast . i64_type_idx )
2020-12-29 16:14:08 +01:00
}
2021-04-02 00:57:09 +02:00
if ast . u32_type_idx in lr {
return ast . Type ( ast . u32_type_idx )
2020-12-29 16:14:08 +01:00
}
2021-04-02 00:57:09 +02:00
if ast . int_type_idx in lr {
return ast . Type ( ast . int_type_idx )
2020-12-29 16:14:08 +01:00
}
2021-04-02 00:57:09 +02:00
if ast . u16_type_idx in lr {
return ast . Type ( ast . u16_type_idx )
2020-12-29 16:14:08 +01:00
}
2021-04-02 00:57:09 +02:00
if ast . i16_type_idx in lr {
return ast . Type ( ast . i16_type_idx )
2020-12-29 16:14:08 +01:00
}
2021-04-02 00:57:09 +02:00
if ast . byte_type_idx in lr {
return ast . Type ( ast . byte_type_idx )
2020-12-29 16:14:08 +01:00
}
2021-04-02 00:57:09 +02:00
if ast . i8_type_idx in lr {
return ast . Type ( ast . i8_type_idx )
2020-12-29 16:14:08 +01:00
}
2021-04-02 00:57:09 +02:00
return ast . Type ( ast . int_literal_type_idx )
2020-12-08 17:49:20 +01:00
}
2021-04-02 00:57:09 +02:00
return ast . Type ( l )
2020-12-08 17:49:20 +01:00
}
2020-05-24 22:49:01 +02:00
fn ( mut g JsGen ) gen_map_init_expr ( it ast . MapInit ) {
2021-12-19 17:25:18 +01:00
// key_typ_sym := g.table.sym(it.key_type)
// value_typ_sym := g.table.sym(it.value_type)
2020-07-01 00:53:53 +02:00
// key_typ_str := util.no_dots(key_typ_sym.name)
// value_typ_str := util.no_dots(value_typ_sym.name)
2021-07-15 16:36:53 +02:00
g . writeln ( ' n e w m a p ( ' )
g . inc_indent ( )
2020-05-24 22:49:01 +02:00
if it . vals . len > 0 {
2021-12-15 14:47:34 +01:00
g . writeln ( ' { ' )
2020-05-24 22:49:01 +02:00
g . inc_indent ( )
for i , key in it . keys {
val := it . vals [ i ]
g . write ( ' [ ' )
g . expr ( key )
2021-12-15 14:47:34 +01:00
g . write ( ' . \$ t o J S ( ) ] ' )
g . write ( ' : ' )
2020-05-24 22:49:01 +02:00
g . expr ( val )
if i < it . keys . len - 1 {
g . write ( ' , ' )
}
g . writeln ( ' ' )
}
g . dec_indent ( )
2021-12-15 14:47:34 +01:00
g . write ( ' } ' )
2020-05-24 22:49:01 +02:00
} else {
2021-12-15 14:47:34 +01:00
g . write ( ' { } ' )
2020-05-24 22:49:01 +02:00
}
2021-07-15 16:36:53 +02:00
g . dec_indent ( )
g . write ( ' ) ' )
2020-05-24 22:49:01 +02:00
}
2021-08-21 16:18:57 +02:00
fn ( mut g JsGen ) type_name ( raw_type ast . Type ) {
typ := raw_type
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( typ )
2021-08-21 16:18:57 +02:00
mut s := ' '
if sym . kind == . function {
// todo: properly print function signatures
if typ . is_ptr ( ) {
s = ' & f u n c t i o n '
} else {
s = ' f u n c t i o n '
}
} else {
s = g . table . type_to_str ( g . unwrap_generic ( typ ) )
}
2021-09-08 19:30:46 +02:00
g . write ( ' n e w s t r i n g ( " $ s " ) ' )
2021-08-21 16:18:57 +02:00
}
2020-05-24 22:49:01 +02:00
fn ( mut g JsGen ) gen_selector_expr ( it ast . SelectorExpr ) {
2021-08-21 16:18:57 +02:00
if it . name_type > 0 {
node := it
match node . gkind_field {
. name {
g . type_name ( it . name_type )
return
}
. typ {
2021-09-08 19:30:46 +02:00
g . write ( ' n e w i n t ( ' )
2021-08-21 16:18:57 +02:00
g . write ( ' $ { int ( g . unwrap_generic ( it . name_type ) ) } ' )
g . write ( ' ) ' )
g . write ( ' ) ' )
return
}
. unknown {
if node . field_name == ' n a m e ' {
g . type_name ( it . name_type )
return
} else if node . field_name == ' i d x ' {
2021-09-08 19:30:46 +02:00
g . write ( ' n e w i n t ( ' )
2021-08-21 16:18:57 +02:00
g . write ( ' $ { int ( g . unwrap_generic ( it . name_type ) ) } ' )
g . write ( ' ) ' )
return
}
panic ( ' u n k n o w n g e n e r i c f i e l d $ it . pos ' )
}
}
}
2020-05-24 22:49:01 +02:00
g . expr ( it . expr )
2021-08-18 10:33:37 +02:00
mut ltyp := it . expr_type
2021-12-19 17:25:18 +01:00
lsym := g . table . sym ( ltyp )
2021-11-18 09:09:53 +01:00
if lsym . kind != . interface_ && lsym . language != . js {
for ltyp . is_ptr ( ) {
g . write ( ' . v a l ' )
ltyp = ltyp . deref ( )
}
2021-07-24 14:35:17 +02:00
}
2020-05-24 22:49:01 +02:00
g . write ( ' . $ it . field_name ' )
}
fn ( mut g JsGen ) gen_string_inter_literal ( it ast . StringInterLiteral ) {
2021-04-02 00:57:09 +02:00
should_cast := ! ( g . cast_stack . len > 0 && g . cast_stack . last ( ) == ast . string_type_idx )
2021-03-04 14:02:16 +01:00
if should_cast {
2021-09-08 19:30:46 +02:00
g . write ( ' n e w ' )
2021-03-04 14:02:16 +01:00
g . write ( ' s t r i n g ( ' )
2020-12-29 16:14:08 +01:00
}
2021-03-04 14:02:16 +01:00
g . write ( ' ` ' )
2020-05-24 22:49:01 +02:00
for i , val in it . vals {
2020-06-10 20:53:43 +02:00
escaped_val := val . replace ( ' ` ' , ' \\ ` ' )
2020-05-24 22:49:01 +02:00
g . write ( escaped_val )
if i >= it . exprs . len {
continue
}
expr := it . exprs [ i ]
2021-09-26 06:33:53 +02:00
// fmt := it.fmts[i]
// fwidth := it.fwidths[i]
// precision := it.precisions[i]
2020-05-24 22:49:01 +02:00
g . write ( ' \$ { ' )
2021-09-26 06:33:53 +02:00
typ := g . unwrap_generic ( it . expr_types [ i ] )
/ *
g . expr ( expr )
2020-06-10 20:53:43 +02:00
if sym . kind == . struct_ && sym . has_method ( ' s t r ' ) {
g . write ( ' . s t r ( ) ' )
2021-09-26 06:33:53 +02:00
} * /
g . gen_expr_to_string ( expr , typ )
2020-05-24 22:49:01 +02:00
g . write ( ' } ' )
}
2021-03-04 14:02:16 +01:00
g . write ( ' ` ' )
if should_cast {
g . write ( ' ) ' )
}
}
fn ( mut g JsGen ) gen_string_literal ( it ast . StringLiteral ) {
2021-08-18 10:33:37 +02:00
mut text := it . val . replace ( " ' " , " ' " )
text = text . replace ( ' " ' , ' \\ " ' )
2021-04-02 00:57:09 +02:00
should_cast := ! ( g . cast_stack . len > 0 && g . cast_stack . last ( ) == ast . string_type_idx )
2021-07-18 08:00:20 +02:00
if true || should_cast {
2021-09-08 19:30:46 +02:00
g . write ( ' n e w ' )
2021-03-06 18:09:28 +01:00
g . write ( ' s t r i n g ( ' )
2021-03-04 14:02:16 +01:00
}
2021-08-25 13:40:53 +02:00
if it . is_raw {
g . writeln ( ' ( f u n c t i o n ( ) { l e t s = S t r i n g ( ) ; ' )
for x in text {
g . writeln ( ' s + = S t r i n g . f r o m C h a r C o d e ( $ x ) ; ' )
}
g . writeln ( ' r e t u r n s ; } ) ( ) ' )
} else {
2021-11-24 19:31:39 +01:00
g . write ( ' " ' )
for char in text {
if char == ` \n ` {
g . write ( ' \\ n ' )
} else {
g . write ( ' $ char . ascii_str ( ) ' )
}
}
g . write ( ' " ' )
2021-08-25 13:40:53 +02:00
}
2021-07-18 08:00:20 +02:00
if true || should_cast {
2021-03-04 14:02:16 +01:00
g . write ( ' ) ' )
}
2020-05-24 22:49:01 +02:00
}
fn ( mut g JsGen ) gen_struct_init ( it ast . StructInit ) {
2021-12-19 17:25:18 +01:00
type_sym := g . table . sym ( it . typ )
2021-11-29 14:32:29 +01:00
mut name := type_sym . name
if name . contains ( ' < ' ) {
name = name [ 0 .. name . index ( ' < ' ) or { name . len } ]
}
2021-11-17 10:41:33 +01:00
if it . fields . len == 0 && type_sym . kind != . interface_ {
2021-11-18 09:09:53 +01:00
if type_sym . kind == . struct_ && type_sym . language == . js {
g . write ( ' { } ' )
} else {
g . write ( ' n e w $ { g . js_name ( name ) } ( { } ) ' )
}
2021-11-17 10:41:33 +01:00
} else if it . fields . len == 0 && type_sym . kind == . interface_ {
g . write ( ' n e w $ { g . js_name ( name ) } ( ) ' ) // JS interfaces can be instantiated with default ctor
} else if type_sym . kind == . interface_ && it . fields . len != 0 {
g . writeln ( ' ( f u n c t i o n ( ) { ' )
g . inc_indent ( )
g . writeln ( ' l e t t m p = n e w $ { g . js_name ( name ) } ( ) ' )
for field in it . fields {
g . write ( ' t m p . $ field . name = ' )
g . expr ( field . expr )
g . writeln ( ' ; ' )
}
g . writeln ( ' r e t u r n t m p ' )
g . dec_indent ( )
g . writeln ( ' } ) ( ) ' )
2021-12-20 14:18:21 +01:00
} else if type_sym . kind == . struct_ && type_sym . language == . js {
g . writeln ( ' { ' )
2020-05-24 22:49:01 +02:00
g . inc_indent ( )
for i , field in it . fields {
2021-11-25 15:49:53 +01:00
if field . name . len != 0 {
g . write ( ' $ field . name : ' )
}
2020-05-24 22:49:01 +02:00
g . expr ( field . expr )
if i < it . fields . len - 1 {
g . write ( ' , ' )
}
g . writeln ( ' ' )
}
g . dec_indent ( )
2021-12-20 14:18:21 +01:00
g . writeln ( ' } ' )
} else {
g . writeln ( ' ( f u n c t i o n ( ) { ' )
g . inc_indent ( )
tmp := g . new_tmp_var ( )
g . writeln ( ' l e t $ tmp = n e w $ { g . js_name ( name ) } ( { } ) ; ' )
for field in it . fields {
if field . name . len != 0 {
g . write ( ' $ { tmp } . $ field . name = ' )
g . expr ( field . expr )
}
g . write ( ' ; ' )
g . writeln ( ' ' )
2021-11-18 09:09:53 +01:00
}
2021-12-20 14:18:21 +01:00
g . writeln ( ' r e t u r n $ tmp ; ' )
g . dec_indent ( )
g . writeln ( ' } ) ( ) ' )
2020-05-24 22:49:01 +02:00
}
}
fn ( mut g JsGen ) gen_typeof_expr ( it ast . TypeOf ) {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( it . expr_type )
2020-11-25 12:09:40 +01:00
if sym . kind == . sum_type {
2020-05-24 22:49:01 +02:00
// TODO: JS sumtypes not implemented yet
} else if sym . kind == . array_fixed {
2021-04-02 00:57:09 +02:00
fixed_info := sym . info as ast . ArrayFixed
2020-05-24 22:49:01 +02:00
typ_name := g . table . get_type_name ( fixed_info . elem_type )
2020-07-17 19:13:22 +02:00
g . write ( ' " [ $ fixed_info . size ] $ typ_name " ' )
2020-05-24 22:49:01 +02:00
} else if sym . kind == . function {
2021-04-02 00:57:09 +02:00
info := sym . info as ast . FnType
2020-05-24 22:49:01 +02:00
fn_info := info . func
mut repr := ' f n ( '
2020-09-27 03:32:56 +02:00
for i , arg in fn_info . params {
2020-05-24 22:49:01 +02:00
if i > 0 {
repr += ' , '
}
repr += g . table . get_type_name ( arg . typ )
}
repr += ' ) '
2021-04-02 00:57:09 +02:00
if fn_info . return_type != ast . void_type {
2020-05-24 22:49:01 +02:00
repr += ' $ { g . table . get_type_name ( fn_info . return_type ) } '
}
g . write ( ' " $ repr " ' )
} else {
2020-07-17 19:13:22 +02:00
g . write ( ' " $ sym . name " ' )
2020-04-15 23:16:49 +02:00
}
2020-04-17 02:38:39 +02:00
}
2020-12-08 17:49:20 +01:00
2021-10-18 09:56:21 +02:00
fn ( mut g JsGen ) gen_cast_tmp ( tmp string , typ_ ast . Type ) {
// Skip cast if type is the same as the parrent caster
2021-12-19 17:25:18 +01:00
tsym := g . table . final_sym ( typ_ )
2021-12-15 14:47:34 +01:00
if ! g . pref . output_es5 && ( tsym . kind == . i64 || tsym . kind == . u64 ) {
2021-10-18 09:56:21 +02:00
g . write ( ' n e w ' )
g . write ( ' $ tsym . kind . str ( ) ' )
g . write ( ' ( B i g I n t ( ' )
g . write ( tmp )
g . write ( ' n ) ) ' )
return
}
g . cast_stack << typ_
typ := g . typ ( typ_ )
if typ_ . is_ptr ( ) {
g . write ( ' n e w \$ r e f ( ' )
}
g . write ( ' n e w ' )
g . write ( ' $ { typ } ( ' )
g . write ( tmp )
if typ == ' s t r i n g ' {
g . write ( ' . t o S t r i n g ( ) ' )
}
g . write ( ' ) ' )
if typ_ . is_ptr ( ) {
g . write ( ' ) ' )
}
g . cast_stack . delete_last ( )
}
2020-12-08 17:49:20 +01:00
fn ( mut g JsGen ) gen_type_cast_expr ( it ast . CastExpr ) {
2021-04-02 00:57:09 +02:00
is_literal := ( ( it . expr is ast . IntegerLiteral && it . typ in ast . integer_type_idxs )
|| ( it . expr is ast . FloatLiteral && it . typ in ast . float_type_idxs ) )
2021-12-19 17:25:18 +01:00
from_type_sym := g . table . sym ( it . expr_type )
to_type_sym := g . table . sym ( it . typ ) // type to be used as cast
2021-11-10 10:37:16 +01:00
if it . typ . is_bool ( ) && from_type_sym . name == ' J S . B o o l e a n ' {
g . write ( ' n e w b o o l ( ' )
g . expr ( it . expr )
g . write ( ' ) ' )
return
}
if it . expr_type . is_bool ( ) && to_type_sym . name == ' J S . B o o l e a n ' {
g . expr ( it . expr )
g . write ( ' . \$ t o J S ( ) ' )
return
}
if ( to_type_sym . is_number ( ) && from_type_sym . name == ' J S . N u m b e r ' )
|| ( to_type_sym . is_number ( ) && from_type_sym . name == ' J S . B i g I n t ' )
|| ( to_type_sym . is_string ( ) && from_type_sym . name == ' J S . S t r i n g ' ) {
g . write ( ' n e w $ { to_type_sym . kind . str ( ) } ( ' )
g . expr ( it . expr )
g . write ( ' ) ' )
return
}
if ( from_type_sym . is_number ( ) && to_type_sym . name == ' J S . N u m b e r ' )
|| ( from_type_sym . is_number ( ) && to_type_sym . name == ' J S . B i g I n t ' )
|| ( from_type_sym . is_string ( ) && to_type_sym . name == ' J S . S t r i n g ' ) {
g . write ( ' $ { g . typ ( it . typ ) } ( ' )
g . expr ( it . expr )
g . write ( ' . \$ t o J S ( ) ) ' )
return
}
2021-12-20 14:18:21 +01:00
if ( from_type_sym . name == ' A n y ' && from_type_sym . language == . js )
2021-12-22 11:26:52 +01:00
|| from_type_sym . name == ' J S . A n y ' || from_type_sym . name == ' v o i d p t r ' {
2021-12-20 14:18:21 +01:00
if it . typ . is_ptr ( ) {
g . write ( ' n e w \$ r e f ( ' )
}
g . expr ( it . expr )
if it . typ . is_ptr ( ) {
g . write ( ' ) ' )
}
return
}
2021-03-04 14:02:16 +01:00
// Skip cast if type is the same as the parrent caster
2021-11-10 10:37:16 +01:00
tsym := to_type_sym
2021-10-28 18:31:56 +02:00
if tsym . kind == . sum_type {
g . expr ( it . expr )
return
}
2021-12-15 14:47:34 +01:00
if ! g . pref . output_es5 && it . expr is ast . IntegerLiteral
&& ( tsym . kind == . i64 || tsym . kind == . u64 ) {
2021-08-15 17:09:51 +02:00
g . write ( ' n e w ' )
2021-09-08 19:30:46 +02:00
g . write ( ' $ tsym . kind . str ( ) ' )
2021-08-13 20:24:10 +02:00
g . write ( ' ( B i g I n t ( ' )
g . write ( it . expr . val )
g . write ( ' n ) ) ' )
return
}
2021-03-04 14:02:16 +01:00
if g . cast_stack . len > 0 && is_literal {
if it . typ == g . cast_stack [ g . cast_stack . len - 1 ] {
2021-08-13 20:24:10 +02:00
g . expr ( it . expr )
2021-03-04 14:02:16 +01:00
return
}
}
g . cast_stack << it . typ
2020-12-08 17:49:20 +01:00
typ := g . typ ( it . typ )
if ! is_literal {
2021-08-18 10:33:37 +02:00
if it . typ . is_ptr ( ) {
g . write ( ' n e w \$ r e f ( ' )
}
2021-09-08 19:30:46 +02:00
g . write ( ' n e w ' )
2020-12-08 17:49:20 +01:00
g . write ( ' $ { typ } ( ' )
}
g . expr ( it . expr )
2021-02-09 23:00:43 +01:00
if typ == ' s t r i n g ' && it . expr ! is ast . StringLiteral {
2020-12-08 17:49:20 +01:00
g . write ( ' . t o S t r i n g ( ) ' )
}
if ! is_literal {
g . write ( ' ) ' )
2021-08-18 10:33:37 +02:00
if it . typ . is_ptr ( ) {
g . write ( ' ) ' )
}
2020-12-08 17:49:20 +01:00
}
2021-03-04 14:02:16 +01:00
g . cast_stack . delete_last ( )
}
fn ( mut g JsGen ) gen_integer_literal_expr ( it ast . IntegerLiteral ) {
2021-04-02 00:57:09 +02:00
typ := ast . Type ( ast . int_type )
2021-03-04 14:02:16 +01:00
// Don't wrap integers for use in JS.foo functions.
// TODO: call.language always seems to be "v", parser bug?
if g . call_stack . len > 0 {
call := g . call_stack [ g . call_stack . len - 1 ]
2021-07-15 16:36:53 +02:00
if call . language == . js {
for t in call . args {
if t . expr is ast . IntegerLiteral {
if t . expr == it {
g . write ( it . val )
return
}
2021-03-04 14:02:16 +01:00
}
}
2021-03-06 18:09:28 +01:00
}
2021-03-04 14:02:16 +01:00
}
// Skip cast if type is the same as the parrent caster
if g . cast_stack . len > 0 {
2021-04-02 00:57:09 +02:00
if g . cast_stack [ g . cast_stack . len - 1 ] in ast . integer_type_idxs {
2021-08-15 17:09:51 +02:00
g . write ( ' n e w ' )
2021-09-08 19:30:46 +02:00
2021-08-15 17:09:51 +02:00
g . write ( ' i n t ( $ it . val ) ' )
2021-03-04 14:02:16 +01:00
return
}
}
2021-08-15 17:09:51 +02:00
g . write ( ' n e w ' )
2021-03-04 14:02:16 +01:00
g . write ( ' $ { g . typ ( typ ) } ( $ it . val ) ' )
}
fn ( mut g JsGen ) gen_float_literal_expr ( it ast . FloatLiteral ) {
2021-04-02 00:57:09 +02:00
typ := ast . Type ( ast . f32_type )
2021-03-06 18:09:28 +01:00
2021-03-04 14:02:16 +01:00
// Don't wrap integers for use in JS.foo functions.
// TODO: call.language always seems to be "v", parser bug?
if g . call_stack . len > 0 {
call := g . call_stack [ g . call_stack . len - 1 ]
2021-08-23 13:25:02 +02:00
if call . language == . js {
for i , t in call . args {
if t . expr is ast . FloatLiteral {
if t . expr == it {
if call . expected_arg_types [ i ] in ast . integer_type_idxs {
g . write ( int ( it . val . f64 ( ) ) . str ( ) )
} else {
g . write ( it . val )
}
return
2021-03-04 14:02:16 +01:00
}
}
}
2021-03-06 18:09:28 +01:00
}
2021-03-04 14:02:16 +01:00
}
// Skip cast if type is the same as the parrent caster
if g . cast_stack . len > 0 {
2021-04-02 00:57:09 +02:00
if g . cast_stack [ g . cast_stack . len - 1 ] in ast . float_type_idxs {
2021-07-29 10:39:36 +02:00
g . write ( ' n e w f 3 2 ( $ it . val ) ' )
2021-03-04 14:02:16 +01:00
return
2021-04-02 00:57:09 +02:00
} else if g . cast_stack [ g . cast_stack . len - 1 ] in ast . integer_type_idxs {
2021-03-04 14:02:16 +01:00
g . write ( int ( it . val . f64 ( ) ) . str ( ) )
return
}
}
2021-08-15 17:09:51 +02:00
g . write ( ' n e w ' )
2021-03-04 14:02:16 +01:00
g . write ( ' $ { g . typ ( typ ) } ( $ it . val ) ' )
2020-12-12 10:42:07 +01:00
}
2021-07-19 14:55:03 +02:00
fn ( mut g JsGen ) unwrap_generic ( typ ast . Type ) ast . Type {
if typ . has_flag ( . generic ) {
2021-11-25 15:49:53 +01:00
/ *
resolve_generic_to_concrete should not mutate the table .
It mutates if the generic type is for example [ ] T and the
concrete type is an array type that has not been registered
yet . This should have already happened in the checker , since
it also calls resolve_generic_to_concrete . g . table is made
non - mut to make sure no one else can accidentally mutates the table .
* /
mut muttable := unsafe { & ast . Table ( g . table ) }
if t_typ := muttable . resolve_generic_to_concrete ( typ , if g . fn_decl != 0 {
g . fn_decl . generic_names
} else {
[ ] string { }
} , g . cur_concrete_types )
2021-07-19 14:55:03 +02:00
{
return t_typ
}
}
return typ
}
2021-09-08 19:30:46 +02:00
fn replace_op ( s string ) string {
return match s {
' + ' { ' _ p l u s ' }
' - ' { ' _ m i n u s ' }
' * ' { ' _ m u l t ' }
' / ' { ' _ d i v ' }
' % ' { ' _ m o d ' }
' < ' { ' _ l t ' }
' > ' { ' _ g t ' }
' = = ' { ' _ e q ' }
else { ' ' }
}
}
2021-10-01 20:23:49 +02:00
fn ( mut g JsGen ) gen_postfix_index_expr ( expr ast . IndexExpr , op token . Kind ) {
2021-12-19 17:25:18 +01:00
left_typ := g . table . sym ( expr . left_type )
2021-10-01 20:23:49 +02:00
// TODO: Handle splice setting if it's implemented
if expr . index is ast . RangeExpr {
if left_typ . kind == . array {
g . write ( ' a r r a y _ s l i c e ( ' )
} else {
g . write ( ' s t r i n g _ s l i c e ( ' )
}
g . expr ( expr . left )
if expr . left_type . is_ptr ( ) {
g . write ( ' . v a l u e O f ( ) ' )
}
g . write ( ' , ' )
if expr . index . has_low {
g . expr ( expr . index . low )
} else {
g . write ( ' n e w i n t ( 0 ) ' )
}
g . write ( ' , ' )
if expr . index . has_high {
g . expr ( expr . index . high )
} else {
g . expr ( expr . left )
if expr . left_type . is_ptr ( ) {
g . write ( ' . v a l u e O f ( ) ' )
}
g . write ( ' . l e n ' )
}
g . write ( ' ) ' )
} else if left_typ . kind == . map {
g . expr ( expr . left )
if expr . is_setter {
g . inside_map_set = true
g . write ( ' . m a p . s e t ( ' )
} else {
g . write ( ' . m a p . g e t ( ' )
}
g . expr ( expr . index )
g . write ( ' . \$ t o J S ( ) ' )
if ! expr . is_setter {
g . write ( ' ) ' )
} else {
g . write ( ' , ' )
2021-12-19 17:25:18 +01:00
lsym := g . table . sym ( expr . left_type )
2021-10-01 20:23:49 +02:00
key_typ := match lsym . info {
ast . Map {
lsym . info . value_type
}
else {
verror ( ' u n r e a c h a b l e ' )
}
}
g . write ( ' n e w $ { g . typ ( key_typ ) } ( ' )
g . expr ( expr . left )
g . write ( ' . m a p . g e t ( ' )
g . expr ( expr . index )
g . write ( ' . \$ t o J S ( ) ) ' )
match op {
. inc {
g . write ( ' . v a l + 1 ) ' )
}
. dec {
g . write ( ' . v a l - 1 ) ' )
}
else {
verror ( ' n o t y e t i m p l e m e n t e d ' )
}
}
g . write ( ' ) ' )
}
} else if left_typ . kind == . string {
if expr . is_setter {
// TODO: What's the best way to do this?
// 'string'[3] = `o`
} else {
// TODO: Maybe use u16 there? JS String returns values up to 2^16-1
2022-04-15 13:58:56 +02:00
g . write ( ' n e w u 8 ( ' )
2021-10-01 20:23:49 +02:00
g . expr ( expr . left )
if expr . left_type . is_ptr ( ) {
g . write ( ' . v a l u e O f ( ) ' )
}
g . write ( ' . s t r . c h a r C o d e A t ( ' )
g . expr ( expr . index )
g . write ( ' ) ) ' )
}
}
}