2020-04-15 23:16:49 +02:00
|
|
|
module js
|
|
|
|
|
2020-04-26 07:35:59 +02:00
|
|
|
import strings
|
|
|
|
import v.ast
|
|
|
|
import v.table
|
|
|
|
import v.pref
|
|
|
|
import v.util
|
2020-05-17 21:39:01 +02:00
|
|
|
import v.depgraph
|
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
|
|
|
|
js_reserved = ['await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger',
|
|
|
|
'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'finally', 'for', 'function',
|
|
|
|
'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'package', 'private',
|
|
|
|
'protected', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'try', 'typeof',
|
|
|
|
'var', 'void', 'while', 'with', 'yield']
|
2020-04-15 23:16:49 +02:00
|
|
|
tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t']
|
2020-05-21 15:17:16 +02:00
|
|
|
builtin_globals = ['println', 'print']
|
2020-05-21 22:36:06 +02:00
|
|
|
type_values = {
|
|
|
|
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
struct JsGen {
|
2020-05-20 16:57:42 +02:00
|
|
|
table &table.Table
|
|
|
|
definitions strings.Builder
|
|
|
|
pref &pref.Preferences
|
2020-04-27 22:53:26 +02:00
|
|
|
mut:
|
2020-05-20 16:57:42 +02:00
|
|
|
out strings.Builder
|
|
|
|
namespaces map[string]strings.Builder
|
|
|
|
namespaces_pub map[string][]string
|
|
|
|
namespace_imports map[string]map[string]string
|
|
|
|
namespace string
|
|
|
|
doc &JsDoc
|
|
|
|
enable_doc bool
|
|
|
|
constants strings.Builder // all global V constants
|
|
|
|
file ast.File
|
|
|
|
tmp_count int
|
|
|
|
inside_ternary bool
|
|
|
|
inside_loop bool
|
|
|
|
is_test bool
|
|
|
|
indents map[string]int // indentations mapped to namespaces
|
|
|
|
stmt_start_pos int
|
|
|
|
defer_stmts []ast.DeferStmt
|
|
|
|
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
|
|
|
|
str_types []string // types that need automatic str() generation
|
|
|
|
method_fn_decls map[string][]ast.Stmt
|
|
|
|
empty_line bool
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string {
|
|
|
|
mut g := &JsGen{
|
|
|
|
out: strings.new_builder(100)
|
|
|
|
definitions: strings.new_builder(100)
|
|
|
|
constants: strings.new_builder(100)
|
|
|
|
table: table
|
|
|
|
pref: pref
|
|
|
|
fn_decl: 0
|
|
|
|
empty_line: true
|
|
|
|
doc: 0
|
2020-05-20 16:57:42 +02:00
|
|
|
enable_doc: true
|
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
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
g.init()
|
|
|
|
|
2020-05-17 21:39:01 +02:00
|
|
|
mut graph := depgraph.new_dep_graph()
|
|
|
|
|
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)
|
|
|
|
g.is_test = g.file.path.ends_with('_test.v')
|
|
|
|
g.find_class_methods(file.stmts)
|
|
|
|
g.escape_namespace()
|
|
|
|
}
|
|
|
|
|
|
|
|
for file in files {
|
|
|
|
g.file = file
|
|
|
|
g.enter_namespace(g.file.mod.name)
|
|
|
|
g.is_test = g.file.path.ends_with('_test.v')
|
2020-05-17 21:39:01 +02:00
|
|
|
|
|
|
|
// store imports
|
|
|
|
mut imports := []string{}
|
|
|
|
for imp in g.file.imports {
|
|
|
|
imports << imp.mod
|
|
|
|
}
|
|
|
|
graph.add(g.file.mod.name, imports)
|
|
|
|
|
2020-04-15 23:16:49 +02:00
|
|
|
g.stmts(file.stmts)
|
|
|
|
// store the current namespace
|
|
|
|
g.escape_namespace()
|
|
|
|
}
|
2020-05-17 21:39:01 +02:00
|
|
|
|
|
|
|
// resolve imports
|
|
|
|
deps_resolved := graph.resolve()
|
|
|
|
|
2020-04-15 23:16:49 +02:00
|
|
|
g.finish()
|
|
|
|
mut out := g.hashes() + g.definitions.str() + g.constants.str()
|
2020-05-17 21:39:01 +02:00
|
|
|
for node in deps_resolved.nodes {
|
2020-05-21 16:31:07 +02:00
|
|
|
out += g.doc.gen_namespace(node.name)
|
2020-05-17 21:39:01 +02:00
|
|
|
out += 'const $node.name = (function ('
|
|
|
|
imports := g.namespace_imports[node.name]
|
|
|
|
for i, key in imports.keys() {
|
|
|
|
if i > 0 { out += ', ' }
|
|
|
|
out += imports[key]
|
|
|
|
}
|
2020-05-20 16:57:42 +02:00
|
|
|
out += ') {\n\t'
|
2020-04-15 23:16:49 +02:00
|
|
|
// private scope
|
2020-05-20 16:57:42 +02:00
|
|
|
out += g.namespaces[node.name].str().trim_space()
|
2020-04-15 23:16:49 +02:00
|
|
|
// public scope
|
2020-05-20 16:57:42 +02:00
|
|
|
out += '\n\n\t/* module exports */'
|
2020-04-15 23:16:49 +02:00
|
|
|
out += '\n\treturn {'
|
2020-05-17 21:39:01 +02:00
|
|
|
for pub_var in g.namespaces_pub[node.name] {
|
2020-04-15 23:16:49 +02:00
|
|
|
out += '\n\t\t$pub_var,'
|
|
|
|
}
|
2020-05-21 22:36:06 +02:00
|
|
|
if g.namespaces_pub[node.name].len > 0 { out += '\n\t' }
|
|
|
|
out += '};'
|
2020-05-17 21:39:01 +02:00
|
|
|
out += '\n})('
|
|
|
|
for i, key in imports.keys() {
|
|
|
|
if i > 0 { out += ', ' }
|
|
|
|
out += key
|
|
|
|
}
|
2020-05-20 16:57:42 +02:00
|
|
|
out += ');\n\n'
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut g JsGen) enter_namespace(n string) {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.namespace = n
|
|
|
|
if g.namespaces[g.namespace].len == 0 {
|
|
|
|
// create a new namespace
|
|
|
|
g.out = strings.new_builder(100)
|
|
|
|
g.indents[g.namespace] = 0
|
2020-05-20 16:57:42 +02:00
|
|
|
} else {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.out = g.namespaces[g.namespace]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut g JsGen) escape_namespace() {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.namespaces[g.namespace] = g.out
|
2020-05-20 16:57:42 +02:00
|
|
|
g.namespace = ''
|
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-05-17 21:39:01 +02:00
|
|
|
mut arr := g.namespaces_pub[g.namespace]
|
2020-05-20 16:57:42 +02:00
|
|
|
arr << g.js_name(s)
|
2020-05-17 21:39:01 +02:00
|
|
|
g.namespaces_pub[g.namespace] = arr
|
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 {
|
|
|
|
match stmt {
|
|
|
|
ast.FnDecl {
|
|
|
|
if it.is_method {
|
|
|
|
// Found struct method, store it to be generated along with the class.
|
2020-05-20 16:57:42 +02:00
|
|
|
class_name := g.table.get_type_name(it.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('// Generated by the V compiler\n')
|
2020-04-15 23:16:49 +02:00
|
|
|
g.definitions.writeln('"use strict";')
|
|
|
|
g.definitions.writeln('')
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut g JsGen) finish() {
|
2020-04-15 23:16:49 +02:00
|
|
|
if g.constants.len > 0 {
|
|
|
|
constants := g.constants.str()
|
|
|
|
g.constants = strings.new_builder(100)
|
2020-05-20 16:57:42 +02:00
|
|
|
g.constants.writeln('const _CONSTS = Object.freeze({')
|
2020-04-15 23:16:49 +02:00
|
|
|
g.constants.write(constants)
|
|
|
|
g.constants.writeln('});')
|
|
|
|
g.constants.writeln('')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (g JsGen) hashes() string {
|
|
|
|
mut res := '// V_COMMIT_HASH ${util.vhash()}\n'
|
2020-05-20 16:57:42 +02:00
|
|
|
res += '// V_CURRENT_COMMIT_HASH ${util.githash(g.pref.building_v)}\n'
|
2020-04-15 23:16:49 +02:00
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
// V type to JS type
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut g JsGen) typ(t table.Type) string {
|
2020-04-15 23:16:49 +02:00
|
|
|
sym := g.table.get_type_symbol(t)
|
2020-05-20 16:57:42 +02:00
|
|
|
mut styp := sym.name
|
|
|
|
if styp.starts_with('JS.') {
|
|
|
|
styp = styp[3..]
|
|
|
|
}
|
|
|
|
// 'multi_return_int_int' => '[number, number]'
|
|
|
|
if styp.starts_with('multi_return_') {
|
|
|
|
tokens := styp.replace('multi_return_', '').split('_')
|
|
|
|
return '[' + tokens.map(g.to_js_typ(it)).join(', ') + ']'
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
// 'anon_fn_7_7_1' => '(a number, b number) => void'
|
2020-05-20 16:57:42 +02:00
|
|
|
if styp.starts_with('anon_') {
|
|
|
|
info := sym.info as table.FnType
|
|
|
|
mut res := '('
|
|
|
|
for i, arg in info.func.args {
|
|
|
|
res += '$arg.name: ${g.typ(arg.typ)}'
|
|
|
|
if i < info.func.args.len - 1 { res += ', ' }
|
|
|
|
}
|
|
|
|
return res + ') => ' + g.typ(info.func.return_type)
|
|
|
|
}
|
|
|
|
// Struct instance => ns["class"]["prototype"]
|
2020-05-20 20:34:37 +02:00
|
|
|
if sym.kind == .struct_ && get_ns(styp).len > 0 {
|
|
|
|
return g.to_js_typ(g.get_alias(styp)) + '["prototype"]'
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
return g.to_js_typ(styp)
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) to_js_typ(typ string) string {
|
2020-04-15 23:16:49 +02:00
|
|
|
mut styp := ''
|
|
|
|
match typ {
|
|
|
|
'int' {
|
|
|
|
styp = 'number'
|
|
|
|
}
|
|
|
|
'bool' {
|
|
|
|
styp = 'boolean'
|
|
|
|
}
|
|
|
|
'voidptr' {
|
|
|
|
styp = 'Object'
|
2020-04-17 02:38:39 +02:00
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
'byteptr' {
|
|
|
|
styp = 'string'
|
|
|
|
}
|
|
|
|
'charptr' {
|
|
|
|
styp = 'string'
|
2020-04-22 20:12:30 +02:00
|
|
|
}
|
|
|
|
else {
|
2020-04-15 23:16:49 +02:00
|
|
|
if typ.starts_with('array_') {
|
|
|
|
styp = g.to_js_typ(typ.replace('array_', '')) + '[]'
|
|
|
|
} else if typ.starts_with('map_') {
|
|
|
|
tokens := typ.split('_')
|
2020-05-21 22:36:06 +02:00
|
|
|
styp = 'Map<${g.to_js_typ(tokens[1])}, ${g.to_js_typ(tokens[2])}>'
|
2020-04-15 23:16:49 +02:00
|
|
|
} else {
|
|
|
|
styp = typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-20 16:57:42 +02:00
|
|
|
// ns.export => ns["export"]
|
|
|
|
for i, v in styp.split('.') {
|
|
|
|
if i == 0 {
|
|
|
|
styp = v
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
styp += '["$v"]'
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
return styp
|
2020-04-17 02:38:39 +02:00
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
|
2020-05-21 22:36:06 +02:00
|
|
|
fn (mut g JsGen) to_js_typ_val(typ string) string {
|
|
|
|
mut styp := ''
|
|
|
|
match typ {
|
|
|
|
'number' {
|
|
|
|
styp = '0'
|
|
|
|
}
|
|
|
|
'boolean' {
|
|
|
|
styp = 'false'
|
|
|
|
}
|
|
|
|
'Object' {
|
|
|
|
styp = '{}'
|
|
|
|
}
|
|
|
|
'string' {
|
|
|
|
styp = '""'
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if typ.starts_with('Map') {
|
|
|
|
styp = 'new Map()'
|
|
|
|
} else if typ.ends_with('[]') {
|
|
|
|
styp = '[]'
|
|
|
|
} else {
|
|
|
|
styp = '{}'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// ns.export => ns["export"]
|
|
|
|
for i, v in styp.split('.') {
|
|
|
|
if i == 0 {
|
|
|
|
styp = v
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
styp += '["$v"]'
|
|
|
|
}
|
|
|
|
return styp
|
|
|
|
}
|
|
|
|
|
2020-04-15 23:16:49 +02:00
|
|
|
pub fn (g &JsGen) save() {}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut g JsGen) gen_indent() {
|
2020-04-15 23:16:49 +02:00
|
|
|
if g.indents[g.namespace] > 0 && g.empty_line {
|
|
|
|
g.out.write(tabs[g.indents[g.namespace]])
|
|
|
|
}
|
|
|
|
g.empty_line = false
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut g JsGen) inc_indent() {
|
2020-05-15 15:55:49 +02:00
|
|
|
g.indents[g.namespace]++
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut g JsGen) dec_indent() {
|
2020-05-15 15:55:49 +02:00
|
|
|
g.indents[g.namespace]--
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut g JsGen) write(s string) {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.gen_indent()
|
|
|
|
g.out.write(s)
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut g JsGen) writeln(s string) {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.gen_indent()
|
|
|
|
g.out.writeln(s)
|
|
|
|
g.empty_line = true
|
|
|
|
}
|
|
|
|
|
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 '_tmp$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 {
|
|
|
|
parts := s.split('.')
|
|
|
|
mut res := ''
|
|
|
|
for i, p in parts {
|
|
|
|
if i == parts.len - 1 { break } // Last part (fn/struct/var name): skip
|
|
|
|
res += p
|
|
|
|
if i < parts.len - 2 { res += '.' } // Avoid trailing dot
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut g JsGen) get_alias(name string) string {
|
|
|
|
// TODO: This is a hack; find a better way to do this
|
|
|
|
split := name.split('.')
|
|
|
|
if split.len > 1 {
|
|
|
|
imports := g.namespace_imports[g.namespace]
|
|
|
|
alias := imports[split[0]]
|
2020-05-24 22:49:01 +02:00
|
|
|
|
2020-05-20 16:57:42 +02:00
|
|
|
if alias != '' {
|
|
|
|
return alias + '.' + split[1..].join('.')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return name // No dot == no alias
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut g JsGen) js_name(name_ string) string {
|
|
|
|
ns := get_ns(name_)
|
|
|
|
mut name := if ns == g.namespace { name_.split('.').last() } else { g.get_alias(name_) }
|
|
|
|
mut parts := name.split('.')
|
|
|
|
for i, p in parts {
|
|
|
|
if p in js_reserved { parts[i] = 'v_$p' }
|
2020-05-15 15:55:49 +02:00
|
|
|
}
|
2020-05-20 16:57:42 +02:00
|
|
|
return parts.join('.')
|
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
|
|
|
g.inc_indent()
|
|
|
|
for stmt in stmts {
|
|
|
|
g.stmt(stmt)
|
|
|
|
}
|
|
|
|
g.dec_indent()
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) stmt(node ast.Stmt) {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.stmt_start_pos = g.out.len
|
|
|
|
|
|
|
|
match node {
|
|
|
|
ast.AssertStmt {
|
|
|
|
g.gen_assert_stmt(it)
|
|
|
|
}
|
|
|
|
ast.AssignStmt {
|
|
|
|
g.gen_assign_stmt(it)
|
|
|
|
}
|
|
|
|
ast.Attr {
|
|
|
|
g.gen_attr(it)
|
|
|
|
}
|
|
|
|
ast.Block {
|
|
|
|
g.gen_block(it)
|
|
|
|
g.writeln('')
|
|
|
|
}
|
|
|
|
ast.BranchStmt {
|
|
|
|
g.gen_branch_stmt(it)
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.Comment {
|
|
|
|
// Skip: don't generate comments
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
ast.CompIf {
|
|
|
|
// skip: JS has no compile time if
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.ConstDecl {
|
|
|
|
g.gen_const_decl(it)
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
ast.DeferStmt {
|
|
|
|
g.defer_stmts << *it
|
|
|
|
}
|
|
|
|
ast.EnumDecl {
|
|
|
|
g.gen_enum_decl(it)
|
|
|
|
g.writeln('')
|
|
|
|
}
|
|
|
|
ast.ExprStmt {
|
|
|
|
g.gen_expr_stmt(it)
|
|
|
|
}
|
|
|
|
ast.FnDecl {
|
|
|
|
g.fn_decl = it
|
|
|
|
g.gen_fn_decl(it)
|
|
|
|
g.writeln('')
|
|
|
|
}
|
|
|
|
ast.ForCStmt {
|
|
|
|
g.gen_for_c_stmt(it)
|
|
|
|
g.writeln('')
|
|
|
|
}
|
|
|
|
ast.ForInStmt {
|
|
|
|
g.gen_for_in_stmt(it)
|
|
|
|
g.writeln('')
|
|
|
|
}
|
|
|
|
ast.ForStmt {
|
|
|
|
g.gen_for_stmt(it)
|
|
|
|
g.writeln('')
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.GlobalDecl {
|
|
|
|
// TODO
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
ast.GoStmt {
|
|
|
|
g.gen_go_stmt(it)
|
|
|
|
g.writeln('')
|
|
|
|
}
|
|
|
|
ast.GotoLabel {
|
2020-05-20 16:57:42 +02:00
|
|
|
g.writeln('${g.js_name(it.name)}:')
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
ast.GotoStmt {
|
|
|
|
// skip: JS has no goto
|
|
|
|
}
|
|
|
|
ast.HashStmt {
|
|
|
|
// skip: nothing with # in JS
|
|
|
|
}
|
2020-05-17 21:39:01 +02:00
|
|
|
ast.Import {
|
|
|
|
g.gen_import_stmt(it)
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
ast.InterfaceDecl {
|
|
|
|
// TODO skip: interfaces not implemented yet
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.Module {
|
|
|
|
// skip: namespacing implemented externally
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
ast.Return {
|
|
|
|
if g.defer_stmts.len > 0 {
|
|
|
|
g.gen_defer_stmts()
|
|
|
|
}
|
|
|
|
g.gen_return_stmt(it)
|
|
|
|
}
|
|
|
|
ast.StructDecl {
|
|
|
|
g.gen_struct_decl(it)
|
|
|
|
}
|
|
|
|
ast.TypeDecl {
|
|
|
|
// skip JS has no typedecl
|
|
|
|
}
|
|
|
|
ast.UnsafeStmt {
|
|
|
|
g.stmts(it.stmts)
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
/*
|
2020-04-15 23:16:49 +02:00
|
|
|
else {
|
|
|
|
verror('jsgen.stmt(): bad node ${typeof(node)}')
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
*/
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) expr(node ast.Expr) {
|
2020-04-15 23:16:49 +02:00
|
|
|
match node {
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.AnonFn {
|
|
|
|
g.gen_fn_decl(it.decl)
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
ast.ArrayInit {
|
|
|
|
g.gen_array_init_expr(it)
|
|
|
|
}
|
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?
|
|
|
|
}
|
2020-05-20 16:57:42 +02:00
|
|
|
ast.AssignExpr {
|
|
|
|
g.gen_assign_expr(it)
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.Assoc {
|
|
|
|
// TODO
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
ast.BoolLiteral {
|
|
|
|
if it.val == true {
|
|
|
|
g.write('true')
|
2020-05-20 16:57:42 +02:00
|
|
|
} else {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.write('false')
|
|
|
|
}
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.CallExpr {
|
|
|
|
g.gen_call_expr(it)
|
|
|
|
}
|
|
|
|
ast.CastExpr {
|
|
|
|
// skip: JS has no types, so no need to cast
|
|
|
|
// TODO: Check if jsdoc is needed for TS support
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
ast.CharLiteral {
|
|
|
|
g.write("'$it.val'")
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.ConcatExpr {
|
|
|
|
// TODO
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
ast.EnumVal {
|
|
|
|
styp := g.typ(it.typ)
|
|
|
|
g.write('${styp}.${it.val}')
|
|
|
|
}
|
|
|
|
ast.FloatLiteral {
|
|
|
|
g.write(it.val)
|
|
|
|
}
|
|
|
|
ast.Ident {
|
|
|
|
g.gen_ident(it)
|
|
|
|
}
|
|
|
|
ast.IfExpr {
|
|
|
|
g.gen_if_expr(it)
|
|
|
|
}
|
|
|
|
ast.IfGuardExpr {
|
|
|
|
// TODO no optionals yet
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.IndexExpr {
|
|
|
|
g.gen_index_expr(it)
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
ast.InfixExpr {
|
2020-05-24 22:49:01 +02:00
|
|
|
g.gen_infix_expr(it)
|
|
|
|
}
|
|
|
|
ast.IntegerLiteral {
|
|
|
|
g.write(it.val)
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
ast.MapInit {
|
|
|
|
g.gen_map_init_expr(it)
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.MatchExpr {
|
|
|
|
// TODO
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.None {
|
|
|
|
// TODO
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.OrExpr {
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
ast.ParExpr {
|
|
|
|
// TODO
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
ast.PostfixExpr {
|
|
|
|
g.expr(it.expr)
|
|
|
|
g.write(it.op.str())
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.PrefixExpr {
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
ast.RangeExpr {
|
|
|
|
// Only used in IndexExpr, requires index type info
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
ast.SelectorExpr {
|
|
|
|
g.gen_selector_expr(it)
|
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.SizeOf {
|
|
|
|
// TODO
|
2020-05-20 16:57:42 +02:00
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.StringInterLiteral {
|
|
|
|
g.gen_string_inter_literal(it)
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.StringLiteral {
|
|
|
|
g.write('"$it.val"')
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.StructInit {
|
|
|
|
// `user := User{name: 'Bob'}`
|
|
|
|
g.gen_struct_init(it)
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
ast.Type {
|
|
|
|
// skip: JS has no types
|
|
|
|
// TODO maybe?
|
|
|
|
}
|
|
|
|
ast.TypeOf {
|
|
|
|
g.gen_typeof_expr(it)
|
|
|
|
// TODO: Should this print the V type or the JS type?
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
else {
|
|
|
|
println(term.red('jsgen.expr(): unhandled node "${typeof(node)}"'))
|
|
|
|
}
|
|
|
|
*/
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
2020-05-20 16:57:42 +02:00
|
|
|
}
|
2020-05-17 21:39:01 +02:00
|
|
|
|
2020-04-15 23:16:49 +02:00
|
|
|
|
2020-05-24 22:49:01 +02:00
|
|
|
// TODO
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.writeln('// assert')
|
|
|
|
g.write('if( ')
|
|
|
|
g.expr(a.expr)
|
|
|
|
g.write(' ) {')
|
|
|
|
s_assertion := a.expr.str().replace('"', "\'")
|
|
|
|
mut mod_path := g.file.path
|
|
|
|
if g.is_test {
|
|
|
|
g.writeln(' g_test_oks++;')
|
|
|
|
g.writeln(' cb_assertion_ok("${mod_path}", ${a.pos.line_nr+1}, "assert ${s_assertion}", "${g.fn_decl.name}()" );')
|
|
|
|
g.writeln('} else {')
|
|
|
|
g.writeln(' g_test_fails++;')
|
|
|
|
g.writeln(' cb_assertion_failed("${mod_path}", ${a.pos.line_nr+1}, "assert ${s_assertion}", "${g.fn_decl.name}()" );')
|
|
|
|
g.writeln(' exit(1);')
|
|
|
|
g.writeln('}')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
g.writeln('} else {')
|
|
|
|
g.writeln(' eprintln("${mod_path}:${a.pos.line_nr+1}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion");')
|
|
|
|
g.writeln(' exit(1);')
|
|
|
|
g.writeln('}')
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) {
|
2020-04-15 23:16:49 +02:00
|
|
|
if it.left.len > it.right.len {
|
|
|
|
// multi return
|
|
|
|
jsdoc := strings.new_builder(50)
|
|
|
|
jsdoc.write('[')
|
|
|
|
stmt := strings.new_builder(50)
|
|
|
|
stmt.write('const [')
|
|
|
|
for i, ident in it.left {
|
|
|
|
ident_var_info := ident.var_info()
|
|
|
|
styp := g.typ(ident_var_info.typ)
|
|
|
|
jsdoc.write(styp)
|
|
|
|
|
2020-05-20 16:57:42 +02:00
|
|
|
stmt.write(g.js_name(ident.name))
|
2020-04-15 23:16:49 +02:00
|
|
|
|
|
|
|
if i < it.left.len - 1 {
|
|
|
|
jsdoc.write(', ')
|
|
|
|
stmt.write(', ')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
jsdoc.write(']')
|
|
|
|
stmt.write('] = ')
|
|
|
|
g.writeln(g.doc.gen_typ(jsdoc.str(), ''))
|
|
|
|
g.write(stmt.str())
|
|
|
|
g.expr(it.right[0])
|
|
|
|
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`
|
|
|
|
for i, ident in it.left {
|
|
|
|
val := it.right[i]
|
|
|
|
ident_var_info := ident.var_info()
|
|
|
|
mut styp := g.typ(ident_var_info.typ)
|
2020-04-17 02:38:39 +02:00
|
|
|
|
2020-05-20 16:57:42 +02:00
|
|
|
if val is ast.EnumVal {
|
|
|
|
// we want the type of the enum value not the enum
|
|
|
|
styp = 'number'
|
|
|
|
} else if val is ast.StructInit {
|
|
|
|
// no need to print jsdoc for structs
|
|
|
|
styp = ''
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
2020-04-17 02:38:39 +02:00
|
|
|
|
2020-04-15 23:16:49 +02:00
|
|
|
if !g.inside_loop && styp.len > 0 {
|
|
|
|
g.writeln(g.doc.gen_typ(styp, ident.name))
|
|
|
|
}
|
2020-04-17 02:38:39 +02:00
|
|
|
|
2020-04-15 23:16:49 +02:00
|
|
|
if g.inside_loop || ident.is_mut {
|
|
|
|
g.write('let ')
|
|
|
|
} else {
|
|
|
|
g.write('const ')
|
|
|
|
}
|
|
|
|
|
2020-05-20 16:57:42 +02:00
|
|
|
g.write('${g.js_name(ident.name)} = ')
|
2020-04-15 23:16:49 +02:00
|
|
|
g.expr(val)
|
|
|
|
|
|
|
|
if g.inside_loop {
|
2020-05-20 16:57:42 +02:00
|
|
|
g.write('; ')
|
2020-04-15 23:16:49 +02:00
|
|
|
} else {
|
|
|
|
g.writeln(';')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) gen_attr(it ast.Attr) {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.writeln('/* [$it.name] */')
|
|
|
|
}
|
|
|
|
|
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('{')
|
|
|
|
g.stmts(it.stmts)
|
|
|
|
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
|
|
|
|
g.write(it.tok.kind.str())
|
|
|
|
g.writeln(';')
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) gen_const_decl(it ast.ConstDecl) {
|
2020-04-22 01:52:56 +02:00
|
|
|
// old_indent := g.indents[g.namespace]
|
2020-04-15 23:16:49 +02:00
|
|
|
for i, field in it.fields {
|
|
|
|
// TODO hack. Cut the generated value and paste it into definitions.
|
|
|
|
pos := g.out.len
|
|
|
|
g.expr(field.expr)
|
|
|
|
val := g.out.after(pos)
|
|
|
|
g.out.go_back(val.len)
|
2020-05-20 16:57:42 +02:00
|
|
|
if g.enable_doc {
|
|
|
|
typ := g.typ(field.typ)
|
|
|
|
g.constants.write('\t')
|
|
|
|
g.constants.writeln(g.doc.gen_typ(typ, field.name))
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
g.constants.write('\t')
|
2020-05-20 16:57:42 +02:00
|
|
|
g.constants.write('${g.js_name(field.name)}: $val')
|
2020-04-15 23:16:49 +02:00
|
|
|
if i < it.fields.len - 1 {
|
|
|
|
g.constants.writeln(',')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.constants.writeln('')
|
|
|
|
}
|
|
|
|
|
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('(function defer() {')
|
|
|
|
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-20 16:57:42 +02:00
|
|
|
g.writeln('const ${g.js_name(it.name)} = Object.freeze({')
|
2020-04-15 23:16:49 +02:00
|
|
|
g.inc_indent()
|
|
|
|
for i, field in it.fields {
|
|
|
|
g.write('$field.name: ')
|
|
|
|
if field.has_expr {
|
|
|
|
pos := g.out.len
|
|
|
|
g.expr(field.expr)
|
|
|
|
expr_str := g.out.after(pos)
|
|
|
|
g.out.go_back(expr_str.len)
|
|
|
|
g.write('$expr_str')
|
|
|
|
} else {
|
|
|
|
g.write('$i')
|
|
|
|
}
|
|
|
|
g.writeln(',')
|
|
|
|
}
|
|
|
|
g.dec_indent()
|
|
|
|
g.writeln('});')
|
|
|
|
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)
|
|
|
|
expr := it.expr
|
2020-05-20 16:57:42 +02:00
|
|
|
if expr is ast.IfExpr { } // no ; after an if expression
|
|
|
|
else if !g.inside_ternary { g.writeln(';') }
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) {
|
2020-04-15 23:16:49 +02:00
|
|
|
if it.is_method {
|
|
|
|
// Struct methods are handled by class generation code.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if it.no_body {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
g.gen_method_decl(it)
|
|
|
|
}
|
|
|
|
|
2020-05-24 22:49:01 +02:00
|
|
|
fn fn_has_go(it ast.FnDecl) bool {
|
|
|
|
mut has_go := false
|
|
|
|
for stmt in it.stmts {
|
|
|
|
if stmt is ast.GoStmt { has_go = true }
|
|
|
|
}
|
|
|
|
return has_go
|
2020-05-20 16:57:42 +02:00
|
|
|
}
|
2020-05-17 21:39:01 +02:00
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.fn_decl = &it
|
|
|
|
has_go := fn_has_go(it)
|
|
|
|
is_main := it.name == 'main'
|
|
|
|
if is_main {
|
|
|
|
// there is no concept of main in JS but we do have iife
|
|
|
|
g.writeln('/* program entry point */')
|
|
|
|
g.write('(')
|
|
|
|
if has_go {
|
|
|
|
g.write('async ')
|
|
|
|
}
|
|
|
|
g.write('function(')
|
2020-05-17 21:39:01 +02:00
|
|
|
} else if it.is_anon {
|
|
|
|
g.write('function (')
|
2020-04-15 23:16:49 +02:00
|
|
|
} else {
|
2020-05-20 16:57:42 +02:00
|
|
|
mut name := g.js_name(it.name)
|
2020-04-15 23:16:49 +02:00
|
|
|
c := name[0]
|
|
|
|
if c in [`+`, `-`, `*`, `/`] {
|
|
|
|
name = util.replace_op(name)
|
|
|
|
}
|
|
|
|
|
2020-04-22 01:52:56 +02:00
|
|
|
// type_name := g.typ(it.return_type)
|
2020-04-15 23:16:49 +02:00
|
|
|
|
|
|
|
// generate jsdoc for the function
|
|
|
|
g.writeln(g.doc.gen_fn(it))
|
|
|
|
|
|
|
|
if has_go {
|
|
|
|
g.write('async ')
|
|
|
|
}
|
|
|
|
if !it.is_method {
|
|
|
|
g.write('function ')
|
|
|
|
}
|
|
|
|
g.write('${name}(')
|
|
|
|
|
2020-05-20 16:57:42 +02:00
|
|
|
if it.is_pub && !it.is_method {
|
2020-04-15 23:16:49 +02:00
|
|
|
g.push_pub_var(name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mut args := it.args
|
|
|
|
if it.is_method {
|
|
|
|
args = args[1..]
|
|
|
|
}
|
|
|
|
g.fn_args(args, it.is_variadic)
|
|
|
|
g.writeln(') {')
|
|
|
|
|
|
|
|
if it.is_method {
|
|
|
|
g.inc_indent()
|
|
|
|
g.writeln('const ${it.args[0].name} = this;')
|
|
|
|
g.dec_indent()
|
|
|
|
}
|
|
|
|
|
|
|
|
g.stmts(it.stmts)
|
|
|
|
g.write('}')
|
|
|
|
if is_main {
|
|
|
|
g.write(')();')
|
|
|
|
}
|
2020-05-21 22:36:06 +02:00
|
|
|
if !it.is_anon && !it.is_method {
|
2020-05-17 21:39:01 +02:00
|
|
|
g.writeln('')
|
|
|
|
}
|
2020-04-17 02:38:39 +02:00
|
|
|
|
2020-04-15 23:16:49 +02:00
|
|
|
g.fn_decl = 0
|
|
|
|
}
|
|
|
|
|
2020-05-24 22:49:01 +02:00
|
|
|
fn (mut g JsGen) fn_args(args []table.Arg, is_variadic bool) {
|
|
|
|
// no_names := args.len > 0 && args[0].name == 'arg_1'
|
|
|
|
for i, arg in args {
|
|
|
|
name := g.js_name(arg.name)
|
|
|
|
is_varg := i == args.len - 1 && is_variadic
|
|
|
|
if is_varg {
|
|
|
|
g.write('...$name')
|
|
|
|
} else {
|
|
|
|
g.write(name)
|
|
|
|
}
|
|
|
|
// if its not the last argument
|
|
|
|
if i < args.len - 1 {
|
|
|
|
g.write(', ')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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('for (')
|
|
|
|
if it.has_init {
|
|
|
|
g.stmt(it.init)
|
|
|
|
} else {
|
|
|
|
g.write('; ')
|
|
|
|
}
|
|
|
|
if it.has_cond {
|
|
|
|
g.expr(it.cond)
|
|
|
|
}
|
|
|
|
g.write('; ')
|
|
|
|
if it.has_inc {
|
|
|
|
g.expr(it.inc)
|
|
|
|
}
|
|
|
|
g.writeln(') {')
|
|
|
|
g.stmts(it.stmts)
|
|
|
|
g.writeln('}')
|
|
|
|
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 {`
|
|
|
|
i := it.val_var
|
|
|
|
g.inside_loop = true
|
|
|
|
g.write('for (let $i = ')
|
|
|
|
g.expr(it.cond)
|
|
|
|
g.write('; $i < ')
|
|
|
|
g.expr(it.high)
|
|
|
|
g.writeln('; ++$i) {')
|
|
|
|
g.inside_loop = false
|
|
|
|
g.stmts(it.stmts)
|
|
|
|
g.writeln('}')
|
2020-04-25 09:08:53 +02:00
|
|
|
} else if it.kind == .array || it.cond_type.flag_is(.variadic) {
|
2020-04-15 23:16:49 +02:00
|
|
|
// `for num in nums {`
|
|
|
|
i := if it.key_var == '' { g.new_tmp_var() } else { it.key_var }
|
2020-04-22 01:52:56 +02:00
|
|
|
// styp := g.typ(it.val_type)
|
2020-04-15 23:16:49 +02:00
|
|
|
g.inside_loop = true
|
|
|
|
g.write('for (let $i = 0; $i < ')
|
|
|
|
g.expr(it.cond)
|
|
|
|
g.writeln('.length; ++$i) {')
|
|
|
|
g.inside_loop = false
|
|
|
|
g.write('\tlet $it.val_var = ')
|
|
|
|
g.expr(it.cond)
|
|
|
|
g.writeln('[$i];')
|
|
|
|
g.stmts(it.stmts)
|
|
|
|
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-04-15 23:16:49 +02:00
|
|
|
key := if it.key_var == '' { g.new_tmp_var() } else { it.key_var }
|
|
|
|
g.write('for (let [$key, $it.val_var] of ')
|
|
|
|
g.expr(it.cond)
|
|
|
|
g.writeln(') {')
|
|
|
|
g.stmts(it.stmts)
|
|
|
|
g.writeln('}')
|
|
|
|
} else if it.kind == .string {
|
|
|
|
// `for x in 'hello' {`
|
|
|
|
i := if it.key_var == '' { g.new_tmp_var() } else { it.key_var }
|
|
|
|
g.inside_loop = true
|
|
|
|
g.write('for (let $i = 0; $i < ')
|
|
|
|
g.expr(it.cond)
|
|
|
|
g.writeln('.length; ++$i) {')
|
|
|
|
g.inside_loop = false
|
|
|
|
g.write('\tlet $it.val_var = ')
|
|
|
|
g.expr(it.cond)
|
|
|
|
g.writeln('[$i];')
|
|
|
|
g.stmts(it.stmts)
|
|
|
|
g.writeln('}')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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('while (')
|
|
|
|
if it.is_inf {
|
|
|
|
g.write('true')
|
|
|
|
} else {
|
|
|
|
g.expr(it.cond)
|
|
|
|
}
|
|
|
|
g.writeln(') {')
|
|
|
|
g.stmts(it.stmts)
|
|
|
|
g.writeln('}')
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) gen_go_stmt(node ast.GoStmt) {
|
2020-04-15 23:16:49 +02:00
|
|
|
// x := node.call_expr as ast.CallEpxr // TODO
|
|
|
|
match node.call_expr {
|
2020-04-17 02:38:39 +02:00
|
|
|
ast.CallExpr {
|
2020-04-15 23:16:49 +02:00
|
|
|
mut name := it.name
|
|
|
|
if it.is_method {
|
|
|
|
receiver_sym := g.table.get_type_symbol(it.receiver_type)
|
|
|
|
name = receiver_sym.name + '.' + name
|
|
|
|
}
|
|
|
|
g.writeln('await new Promise(function(resolve){')
|
|
|
|
g.inc_indent()
|
|
|
|
g.write('${name}(')
|
|
|
|
for i, arg in it.args {
|
|
|
|
g.expr(arg.expr)
|
|
|
|
if i < it.args.len - 1 {
|
|
|
|
g.write(', ')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.writeln(');')
|
|
|
|
g.writeln('resolve();')
|
|
|
|
g.dec_indent()
|
|
|
|
g.writeln('});')
|
|
|
|
}
|
2020-05-20 16:57:42 +02:00
|
|
|
else {}
|
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) {
|
|
|
|
mut imports := g.namespace_imports[g.namespace]
|
|
|
|
imports[it.mod] = it.alias
|
|
|
|
g.namespace_imports[g.namespace] = imports
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) gen_return_stmt(it ast.Return) {
|
2020-05-20 16:57:42 +02:00
|
|
|
if it.exprs.len == 0 {
|
|
|
|
// Returns nothing
|
2020-05-24 22:49:01 +02:00
|
|
|
g.write('return;')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
g.write('return ')
|
|
|
|
if it.exprs.len == 1 {
|
2020-05-20 16:57:42 +02:00
|
|
|
g.expr(it.exprs[0])
|
|
|
|
} else {
|
|
|
|
// Multi return
|
2020-04-15 23:16:49 +02:00
|
|
|
g.write('[')
|
|
|
|
for i, expr in it.exprs {
|
|
|
|
g.expr(expr)
|
|
|
|
if i < it.exprs.len - 1 {
|
|
|
|
g.write(', ')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.write(']')
|
|
|
|
}
|
|
|
|
g.writeln(';')
|
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
|
2020-05-21 22:36:06 +02:00
|
|
|
g.writeln(g.doc.gen_fac_fn(node.fields))
|
|
|
|
g.write('function ${g.js_name(node.name)}({ ')
|
|
|
|
for i, field in node.fields {
|
|
|
|
g.write('$field.name = ')
|
|
|
|
if field.has_default_expr {
|
|
|
|
g.expr(field.default_expr)
|
|
|
|
} else {
|
|
|
|
g.write('${g.to_js_typ_val(g.typ(field.typ))}')
|
|
|
|
}
|
|
|
|
if i < node.fields.len - 1 { g.write(', ') }
|
|
|
|
}
|
|
|
|
g.writeln(' }) {')
|
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('this.$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('};')
|
|
|
|
|
|
|
|
g.writeln('${g.js_name(node.name)}.prototype = {')
|
|
|
|
g.inc_indent()
|
2020-04-15 23:16:49 +02:00
|
|
|
|
|
|
|
fns := g.method_fn_decls[node.name]
|
2020-05-21 22:36:06 +02:00
|
|
|
|
|
|
|
for i, field in node.fields {
|
|
|
|
g.writeln(g.doc.gen_typ(g.typ(field.typ), field.name))
|
|
|
|
g.write('$field.name: ${g.to_js_typ_val(g.typ(field.typ))}')
|
|
|
|
if i < node.fields.len - 1 || fns.len > 0 { g.writeln(',') } else { g.writeln('') }
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, cfn in fns {
|
2020-05-20 16:57:42 +02:00
|
|
|
// TODO: Move cast to the entire array whenever it's possible
|
|
|
|
it := cfn as ast.FnDecl
|
|
|
|
g.gen_method_decl(it)
|
2020-05-21 22:36:06 +02:00
|
|
|
if i < fns.len - 1 { g.writeln(',') } else { g.writeln('') }
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
g.dec_indent()
|
2020-05-21 22:36:06 +02:00
|
|
|
g.writeln('};\n')
|
2020-04-15 23:16:49 +02:00
|
|
|
if node.is_pub {
|
|
|
|
g.push_pub_var(node.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-24 22:49:01 +02:00
|
|
|
fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) {
|
2020-04-15 23:16:49 +02:00
|
|
|
type_sym := g.table.get_type_symbol(it.typ)
|
2020-05-24 22:49:01 +02:00
|
|
|
if type_sym.kind != .array_fixed {
|
|
|
|
g.write('[')
|
|
|
|
for i, expr in it.exprs {
|
|
|
|
g.expr(expr)
|
|
|
|
if i < it.exprs.len - 1 {
|
2020-05-21 22:36:06 +02:00
|
|
|
g.write(', ')
|
|
|
|
}
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
2020-05-24 22:49:01 +02:00
|
|
|
g.write(']')
|
|
|
|
} else {}
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
|
2020-05-24 22:49:01 +02:00
|
|
|
fn (mut g JsGen) gen_assign_expr(it ast.AssignExpr) {
|
|
|
|
g.expr(it.left)
|
|
|
|
g.write(' $it.op ')
|
|
|
|
g.expr(it.val)
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
|
2020-05-21 15:17:16 +02:00
|
|
|
fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
|
|
|
|
mut name := ''
|
|
|
|
if it.name.starts_with('JS.') {
|
|
|
|
name = it.name[3..]
|
|
|
|
} else {
|
|
|
|
name = g.js_name(it.name)
|
|
|
|
}
|
|
|
|
g.expr(it.left)
|
|
|
|
if it.is_method {
|
|
|
|
// example: foo.bar.baz()
|
|
|
|
g.write('.')
|
|
|
|
} else {
|
|
|
|
if name in builtin_globals {
|
|
|
|
g.write('builtin.')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.write('${g.js_name(name)}(')
|
|
|
|
for i, arg in it.args {
|
|
|
|
g.expr(arg.expr)
|
|
|
|
if i != it.args.len - 1 {
|
|
|
|
g.write(', ')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.write(')')
|
|
|
|
}
|
|
|
|
|
2020-05-24 22:49:01 +02:00
|
|
|
fn (mut g JsGen) gen_ident(node ast.Ident) {
|
|
|
|
if node.kind == .constant {
|
|
|
|
// TODO: Handle const namespacing: only consts in the main module are handled rn
|
|
|
|
g.write('_CONSTS.')
|
|
|
|
}
|
|
|
|
|
|
|
|
name := g.js_name(node.name)
|
|
|
|
// TODO `is`
|
|
|
|
// TODO handle optionals
|
|
|
|
g.write(name)
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut g JsGen) gen_if_expr(node ast.IfExpr) {
|
2020-04-15 23:16:49 +02:00
|
|
|
type_sym := g.table.get_type_symbol(node.typ)
|
|
|
|
|
|
|
|
// one line ?:
|
|
|
|
if node.is_expr && node.branches.len >= 2 && node.has_else && type_sym.kind != .void {
|
|
|
|
// `x := if a > b { } else if { } else { }`
|
|
|
|
g.write('(')
|
|
|
|
g.inside_ternary = true
|
|
|
|
for i, branch in node.branches {
|
|
|
|
if i > 0 {
|
|
|
|
g.write(' : ')
|
|
|
|
}
|
|
|
|
if i < node.branches.len - 1 || !node.has_else {
|
|
|
|
g.expr(branch.cond)
|
|
|
|
g.write(' ? ')
|
|
|
|
}
|
|
|
|
g.stmts(branch.stmts)
|
|
|
|
}
|
|
|
|
g.inside_ternary = false
|
|
|
|
g.write(')')
|
|
|
|
} else {
|
2020-05-20 16:57:42 +02:00
|
|
|
// mut is_guard = false
|
2020-04-15 23:16:49 +02:00
|
|
|
for i, branch in node.branches {
|
|
|
|
if i == 0 {
|
|
|
|
match branch.cond {
|
|
|
|
ast.IfGuardExpr {
|
|
|
|
// TODO optionals
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
g.write('if (')
|
|
|
|
g.expr(branch.cond)
|
|
|
|
g.writeln(') {')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if i < node.branches.len - 1 || !node.has_else {
|
|
|
|
g.write('} else if (')
|
|
|
|
g.expr(branch.cond)
|
|
|
|
g.writeln(') {')
|
|
|
|
} else if i == node.branches.len - 1 && node.has_else {
|
|
|
|
/* if is_guard {
|
|
|
|
//g.writeln('} if (!$guard_ok) { /* else */')
|
|
|
|
} else { */
|
|
|
|
g.writeln('} else {')
|
2020-05-20 16:57:42 +02:00
|
|
|
// }
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
g.stmts(branch.stmts)
|
|
|
|
}
|
|
|
|
/* if is_guard {
|
|
|
|
g.write('}')
|
|
|
|
} */
|
|
|
|
g.writeln('}')
|
|
|
|
g.writeln('')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-24 22:49:01 +02:00
|
|
|
fn (mut g JsGen) gen_index_expr(it ast.IndexExpr) {
|
|
|
|
// TODO: Handle splice setting if it's implemented
|
|
|
|
if it.index is ast.RangeExpr {
|
|
|
|
range := it.index as ast.RangeExpr
|
|
|
|
g.expr(it.left)
|
|
|
|
g.write('.slice(')
|
|
|
|
if range.has_low {
|
|
|
|
g.expr(range.low)
|
|
|
|
} else {
|
|
|
|
g.write('0')
|
|
|
|
}
|
|
|
|
g.write(', ')
|
|
|
|
if range.has_high {
|
|
|
|
g.expr(range.high)
|
|
|
|
} else {
|
|
|
|
g.expr(it.left)
|
|
|
|
g.write('.length')
|
|
|
|
}
|
|
|
|
g.write(')')
|
|
|
|
} else {
|
|
|
|
// TODO Does this work in all cases?
|
|
|
|
g.expr(it.left)
|
|
|
|
g.write('[')
|
|
|
|
g.expr(it.index)
|
|
|
|
g.write(']')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
|
|
|
|
g.expr(it.left)
|
|
|
|
|
|
|
|
mut op := it.op.str()
|
|
|
|
// in js == is non-strict & === is strict, always do strict
|
|
|
|
if op == '==' { op = '===' }
|
|
|
|
else if op == '!=' { op = '!==' }
|
|
|
|
|
|
|
|
g.write(' $op ')
|
|
|
|
g.expr(it.right)
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
|
|
|
|
2020-05-24 22:49:01 +02:00
|
|
|
|
|
|
|
fn (mut g JsGen) gen_map_init_expr(it ast.MapInit) {
|
|
|
|
// key_typ_sym := g.table.get_type_symbol(it.key_type)
|
|
|
|
// value_typ_sym := g.table.get_type_symbol(it.value_type)
|
|
|
|
// key_typ_str := key_typ_sym.name.replace('.', '__')
|
|
|
|
// value_typ_str := value_typ_sym.name.replace('.', '__')
|
|
|
|
if it.vals.len > 0 {
|
|
|
|
g.writeln('new Map([')
|
|
|
|
g.inc_indent()
|
|
|
|
for i, key in it.keys {
|
|
|
|
val := it.vals[i]
|
|
|
|
g.write('[')
|
|
|
|
g.expr(key)
|
|
|
|
g.write(', ')
|
|
|
|
g.expr(val)
|
|
|
|
g.write(']')
|
|
|
|
if i < it.keys.len - 1 {
|
|
|
|
g.write(',')
|
|
|
|
}
|
|
|
|
g.writeln('')
|
|
|
|
}
|
|
|
|
g.dec_indent()
|
|
|
|
g.write('])')
|
|
|
|
} else {
|
|
|
|
g.write('new Map()')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) {
|
|
|
|
g.expr(it.expr)
|
|
|
|
g.write('.$it.field_name')
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
|
|
|
|
// TODO Implement `tos3`
|
|
|
|
g.write('tos3(`')
|
|
|
|
for i, val in it.vals {
|
|
|
|
escaped_val := val.replace_each(['`', '\`', '\r\n', '\n'])
|
|
|
|
g.write(escaped_val)
|
|
|
|
if i >= it.exprs.len {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
expr := it.exprs[i]
|
|
|
|
sfmt := it.expr_fmts[i]
|
|
|
|
g.write('\${')
|
|
|
|
if sfmt.len > 0 {
|
|
|
|
fspec := sfmt[sfmt.len - 1]
|
|
|
|
if fspec == `s` && it.expr_types[i] == table.string_type {
|
|
|
|
g.expr(expr)
|
|
|
|
g.write('.str')
|
|
|
|
} else {
|
|
|
|
g.expr(expr)
|
|
|
|
}
|
|
|
|
} else if it.expr_types[i] == table.string_type {
|
|
|
|
// `name.str`
|
|
|
|
g.expr(expr)
|
|
|
|
g.write('.str')
|
|
|
|
} else if it.expr_types[i] == table.bool_type {
|
|
|
|
// `expr ? "true" : "false"`
|
|
|
|
g.expr(expr)
|
|
|
|
g.write(' ? "true" : "false"')
|
|
|
|
} else {
|
|
|
|
sym := g.table.get_type_symbol(it.expr_types[i])
|
|
|
|
|
|
|
|
match sym.kind {
|
|
|
|
.struct_ {
|
|
|
|
g.expr(expr)
|
|
|
|
if sym.has_method('str') {
|
|
|
|
g.write('.str()')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
g.expr(expr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.write('}')
|
|
|
|
}
|
|
|
|
g.write('`)')
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut g JsGen) gen_struct_init(it ast.StructInit) {
|
|
|
|
type_sym := g.table.get_type_symbol(it.typ)
|
|
|
|
name := type_sym.name
|
|
|
|
if it.fields.len == 0 {
|
|
|
|
g.write('new ${g.js_name(name)}({})')
|
|
|
|
} else {
|
|
|
|
g.writeln('new ${g.js_name(name)}({')
|
|
|
|
g.inc_indent()
|
|
|
|
for i, field in it.fields {
|
|
|
|
g.write('$field.name: ')
|
|
|
|
g.expr(field.expr)
|
|
|
|
if i < it.fields.len - 1 {
|
|
|
|
g.write(',')
|
|
|
|
}
|
|
|
|
g.writeln('')
|
|
|
|
}
|
|
|
|
g.dec_indent()
|
|
|
|
g.write('})')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut g JsGen) gen_typeof_expr(it ast.TypeOf) {
|
|
|
|
sym := g.table.get_type_symbol(it.expr_type)
|
|
|
|
if sym.kind == .sum_type {
|
|
|
|
// TODO: JS sumtypes not implemented yet
|
|
|
|
} else if sym.kind == .array_fixed {
|
|
|
|
fixed_info := sym.info as table.ArrayFixed
|
|
|
|
typ_name := g.table.get_type_name(fixed_info.elem_type)
|
|
|
|
g.write('"[$fixed_info.size]${typ_name}"')
|
|
|
|
} else if sym.kind == .function {
|
|
|
|
info := sym.info as table.FnType
|
|
|
|
fn_info := info.func
|
|
|
|
mut repr := 'fn ('
|
|
|
|
for i, arg in fn_info.args {
|
|
|
|
if i > 0 {
|
|
|
|
repr += ', '
|
|
|
|
}
|
|
|
|
repr += g.table.get_type_name(arg.typ)
|
|
|
|
}
|
|
|
|
repr += ')'
|
|
|
|
if fn_info.return_type != table.void_type {
|
|
|
|
repr += ' ${g.table.get_type_name(fn_info.return_type)}'
|
|
|
|
}
|
|
|
|
g.write('"$repr"')
|
|
|
|
} else {
|
|
|
|
g.write('"${sym.name}"')
|
2020-04-15 23:16:49 +02:00
|
|
|
}
|
2020-04-17 02:38:39 +02:00
|
|
|
}
|