pull/7199/head
Leah Lundqvist 2020-12-08 17:49:20 +01:00 committed by GitHub
parent a2ec52b8c4
commit 90c1c639fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 476 additions and 223 deletions

View File

@ -4,14 +4,18 @@
module builtin
fn (a any) toString()
pub fn println(s any) {
JS.console.log(s)
// Quickfix to properly print basic types
// TODO: Add proper detection code for this
JS.console.log(s.toString())
}
pub fn print(s any) {
// TODO
// $if js.node {
JS.process.stdout.write(s)
JS.process.stdout.write(s.toString())
// } $else {
// panic('Cannot `print` in a browser, use `println` instead')
// }

View File

@ -7,6 +7,23 @@
module builtin
pub struct JS.Number {}
pub struct JS.String {
length JS.Number
}
pub struct JS.Boolean {}
pub struct JS.Array {}
pub struct JS.Map {}
// Type prototype functions
fn (v JS.String) toString() JS.String
fn (v JS.Number) toString() JS.String
fn (v JS.Boolean) toString() JS.String
fn (v JS.Array) toString() JS.String
fn (v JS.Map) toString() JS.String
fn (v JS.String) slice(a int, b int) JS.String
// Top level functions
fn JS.eval(string) any
fn JS.parseInt(string, f64) f64
@ -63,3 +80,7 @@ fn JS.Math.round(f64) f64
fn JS.Math.sin(f64) f64
fn JS.Math.sqrt(f64) f64
fn JS.Math.tan(f64) f64
// JSON
fn JS.JSON.stringify(any) string
fn JS.JSON.parse(string) any

View File

@ -0,0 +1,9 @@
module builtin
pub struct string {
str JS.String
}
pub fn (s string) slice(a int, b int) string {
return string(s.str.slice(a, b))
}

View File

@ -0,0 +1,260 @@
module js
import v.table
fn (mut g JsGen) to_js_typ_def_val(s string) string {
mut dval := ''
match s {
'JS.Number' { dval = '0' }
'JS.String' { dval = '""' }
'JS.Boolean' { dval = 'false' }
'JS.Array', 'JS.Map' { dval = '' }
else { dval = '{}' }
}
return dval
}
fn (mut g JsGen) to_js_typ_val(t table.Type) string {
sym := g.table.get_type_symbol(t)
mut styp := ''
mut prefix := if g.file.mod.name == 'builtin' { 'new ' } else { '' }
match sym.kind {
.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .any_int, .any_float, .size_t {
styp = '${prefix}${g.sym_to_js_typ(sym)}(0)'
}
.bool {
styp = '${prefix}${g.sym_to_js_typ(sym)}(false)'
}
.string {
styp = '${prefix}${g.sym_to_js_typ(sym)}("")'
}
.map {
styp = 'new Map()'
}
.array {
styp = '${prefix}${g.sym_to_js_typ(sym)}()'
}
.struct_ {
styp = 'new ${g.js_name(sym.name)}(${g.to_js_typ_def_val(sym.name)})'
}
else {
// TODO
styp = 'undefined'
}
}
return styp
}
fn (mut g JsGen) sym_to_js_typ(sym table.TypeSymbol) string {
mut styp := ''
match sym.kind {
.i8 { styp = 'i8' }
.i16 { styp = 'i16' }
.int { styp = 'int' }
.i64 { styp = 'i64' }
.byte { styp = 'byte' }
.u16 { styp = 'u16' }
.u32 { styp = 'u32' }
.u64 { styp = 'u64' }
.f32 { styp = 'f32' }
.f64 { styp = 'f64' }
.any_int { styp = 'any_int' }
.any_float { styp = 'any_float' }
.size_t { styp = 'size_t' }
.bool { styp = 'bool' }
.string { styp = 'string' }
.map { styp = 'map' }
.array { styp = 'array' }
else {
// TODO
styp = 'undefined'
}
}
return styp
}
// V type to JS type
pub fn (mut g JsGen) typ(t table.Type) string {
sym := g.table.get_type_symbol(t)
mut styp := ''
match sym.kind {
.placeholder {
// This should never happen: means checker bug
styp = 'any'
}
.void {
styp = 'void'
}
.voidptr {
styp = 'any'
}
.byteptr, .charptr {
styp = '${g.sym_to_js_typ(sym)}'
}
.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .any_int, .any_float, .size_t {
styp = '${g.sym_to_js_typ(sym)}'
}
.bool {
styp = '${g.sym_to_js_typ(sym)}'
}
.none_ {
styp = 'undefined'
}
.string, .ustring, .char {
styp = '${g.sym_to_js_typ(sym)}'
}
// 'array_array_int' => 'number[][]'
.array {
info := sym.info as table.Array
styp = '${g.sym_to_js_typ(sym)}(${g.typ(info.elem_type)})'
}
.array_fixed {
info := sym.info as table.ArrayFixed
styp = '${g.sym_to_js_typ(sym)}(${g.typ(info.elem_type)})'
}
.chan {
styp = 'chan'
}
// 'map[string]int' => 'Map<string, number>'
.map {
info := sym.info as table.Map
key := g.typ(info.key_type)
val := g.typ(info.value_type)
styp = 'Map<$key, $val>'
}
.any {
styp = 'any'
}
// ns.Foo => alias["Foo"]["prototype"]
.struct_ {
styp = g.struct_typ(sym.name)
}
.generic_struct_inst {}
// 'multi_return_int_int' => '[number, number]'
.multi_return {
info := sym.info as table.MultiReturn
types := info.types.map(g.typ(it))
joined := types.join(', ')
styp = '[$joined]'
}
.sum_type {
// TODO: Implement sumtypes
styp = 'union_sym_type'
}
.alias {
// TODO: Implement aliases
styp = 'alias'
}
.enum_ {
// NB: We could declare them as TypeScript enums but TS doesn't like
// our namespacing so these break if declared in a different module.
// Until this is fixed, We need to use the type of an enum's members
// rather than the enum itself, and this can only be 'number' for now
styp = 'number'
}
// 'anon_fn_7_7_1' => '(a number, b number) => void'
.function {
info := sym.info as table.FnType
styp = g.fn_typ(info.func.params, info.func.return_type)
}
.interface_ {
styp = g.js_name(sym.name)
}
.rune {
styp = 'any'
}
.aggregate {
panic('TODO: unhandled aggregate in JS')
}
}
/*
else {
println('jsgen.typ: Unhandled type $t')
styp = sym.name
}
*/
if styp.starts_with('JS.') {
return styp[3..]
}
return styp
}
fn (mut g JsGen) fn_typ(args []table.Param, return_type table.Type) string {
mut res := '('
for i, arg in args {
res += '$arg.name: ${g.typ(arg.typ)}'
if i < args.len - 1 {
res += ', '
}
}
return res + ') => ' + g.typ(return_type)
}
fn (mut g JsGen) struct_typ(s string) string {
ns := get_ns(s)
if ns == 'JS' { return s[3..] }
mut name := if ns == g.ns.name { s.split('.').last() } else { g.get_alias(s) }
mut styp := ''
for i, v in name.split('.') {
if i == 0 {
styp = v
} else {
styp += '["$v"]'
}
}
if ns in ['', g.ns.name] {
return styp
}
return styp + '["prototype"]'
}
// ugly arguments but not sure a config struct would be worth it
fn (mut g JsGen) gen_builtin_prototype(typ_name string, val_name string, default_value string, constructor string, value_of string, to_string string, extras string) {
g.writeln('function ${typ_name}($val_name = ${default_value}) { $constructor }')
g.writeln('${typ_name}.prototype = {')
g.inc_indent()
g.writeln('val: $default_value,')
if extras.len > 0 {
g.writeln('$extras,')
}
for method in g.method_fn_decls[typ_name] {
g.inside_def_typ_decl = true
g.gen_method_decl(method)
g.inside_def_typ_decl = false
g.writeln(',')
}
g.writeln('valueOf() { return $value_of },')
g.writeln('toString() { return $to_string }')
g.dec_indent()
g.writeln('};\n')
}
// generate builtin type definitions, used for casting and methods.
fn (mut g JsGen) gen_builtin_type_defs() {
g.inc_indent()
for typ_name in v_types {
// TODO: JsDoc
match typ_name {
'i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'any_int', 'size_t' {
// TODO: Bounds checking
g.gen_builtin_prototype(typ_name, 'val', 'new Number(0)', 'this.val = val | 0;', 'this.val | 0', '(this.val | 0).toString()', '')
}
'f32', 'f64', 'any_float' {
g.gen_builtin_prototype(typ_name, 'val', 'new Number(0)', 'this.val = val;', 'this.val', 'this.val.toString()', '')
}
'bool' {
g.gen_builtin_prototype(typ_name, 'val', 'new Boolean(false)', 'this.val = val;', 'this.val', 'this.val.toString()', '')
}
'string' {
g.gen_builtin_prototype(typ_name, 'str', 'new String("")', 'this.str = str;', 'this.str', 'this.str.toString()', 'get length() { return this.str.length }')
}
'map' {
g.gen_builtin_prototype(typ_name, 'val', 'new Map()', 'this.val = val;', 'this.val', 'this.val.toString()', '')
}
'array' {
g.gen_builtin_prototype(typ_name, 'val', 'new Array()', 'this.val = val;', 'this.val', 'this.val.toString()', '')
}
else {}
}
}
g.dec_indent()
}

View File

@ -3,6 +3,7 @@ module js
import strings
import v.ast
import v.table
import v.token
import v.pref
import v.util
import v.depgraph
@ -13,7 +14,9 @@ const (
'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']
'while', 'with', 'yield', 'Number', 'String', 'Boolean', 'Array', 'Map']
// used to generate type structs
v_types = ['i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64', 'any_int', 'any_float', 'size_t', 'bool', 'string', 'map', 'array']
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', '\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']
)
@ -32,25 +35,27 @@ struct JsGen {
table &table.Table
pref &pref.Preferences
mut:
definitions strings.Builder
ns &Namespace
namespaces map[string]&Namespace
doc &JsDoc
enable_doc bool
file ast.File
tmp_count int
inside_ternary bool
inside_loop bool
inside_map_set bool // map.set(key, value)
inside_builtin 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
str_types []string // types that need automatic str() generation
method_fn_decls map[string][]ast.FnDecl
builtin_fns []string // Functions defined in `builtin`
empty_line bool
definitions strings.Builder
ns &Namespace
namespaces map[string]&Namespace
doc &JsDoc
enable_doc bool
file ast.File
tmp_count int
inside_ternary bool
inside_loop bool
inside_map_set bool // map.set(key, value)
inside_builtin 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
str_types []string // types that need automatic str() generation
method_fn_decls map[string][]ast.FnDecl
builtin_fns []string // Functions defined in `builtin`
empty_line bool
}
pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string {
@ -89,6 +94,11 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
imports << imp.mod
}
graph.add(g.file.mod.name, imports)
// builtin types
if g.file.mod.name == 'builtin' && !g.generated_builtin {
g.gen_builtin_type_defs()
g.generated_builtin = true
}
g.stmts(file.stmts)
// store the current namespace
g.escape_namespace()
@ -119,6 +129,12 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
out += '\n\t/* module exports */'
}
out += '\n\treturn {'
// export builtin types
if name == 'builtin' {
for typ in v_types {
out += '\n\t\t$typ,'
}
}
for i, pub_var in g.namespaces[node.name].pub_vars {
out += '\n\t\t$pub_var'
if i < g.namespaces[node.name].pub_vars.len - 1 {
@ -136,13 +152,29 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
}
out += key.replace('.', '_')
}
out += ');\n\n'
out += ');\n'
// generate builtin basic type casts
if name == 'builtin' {
out += '// builtin type casts\n'
out += 'const ['
for i, typ in v_types {
if i > 0 { out += ', ' }
out += '$typ'
}
out += '] = ['
for i, typ in v_types {
if i > 0 { out += ',' }
out += '\n\tfunction(val) { return new builtin.${typ}(val) }'
}
out += '\n]\n'
}
}
if pref.is_shared {
// Export, through CommonJS, the module of the entry file if `-shared` was passed
export := nodes[nodes.len - 1].name
out += 'if (typeof module === "object" && module.exports) module.exports = $export;'
out += 'if (typeof module === "object" && module.exports) module.exports = $export;\n'
}
out += '\n'
return out
}
@ -175,7 +207,7 @@ pub fn (mut g JsGen) find_class_methods(stmts []ast.Stmt) {
ast.FnDecl {
if stmt.is_method {
// Found struct method, store it to be generated along with the class.
class_name := g.table.get_type_name(stmt.receiver.typ)
mut class_name := g.table.get_type_name(stmt.receiver.typ)
// Workaround until `map[key] << val` works.
mut arr := g.method_fn_decls[class_name]
arr << stmt
@ -199,171 +231,6 @@ pub fn (g JsGen) hashes() string {
return res
}
// V type to JS type
pub fn (mut g JsGen) typ(t table.Type) string {
sym := g.table.get_type_symbol(t)
mut styp := ''
match sym.kind {
.placeholder {
// This should never happen: means checker bug
styp = 'any'
}
.void {
styp = 'void'
}
.voidptr {
styp = 'any'
}
.byteptr, .charptr {
styp = 'string'
}
.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .any_int, .any_float, .size_t {
// TODO: Should u64 and i64 use BigInt rather than number?
styp = 'number'
}
.bool {
styp = 'boolean'
}
.none_ {
styp = 'undefined'
}
.string, .ustring, .char {
styp = 'string'
}
// 'array_array_int' => 'number[][]'
.array {
info := sym.info as table.Array
styp = g.typ(info.elem_type) + '[]'
}
.array_fixed {
info := sym.info as table.ArrayFixed
styp = g.typ(info.elem_type) + '[]'
}
.chan {
styp = 'chan'
}
// 'map[string]int' => 'Map<string, number>'
.map {
info := sym.info as table.Map
key := g.typ(info.key_type)
val := g.typ(info.value_type)
styp = 'Map<$key, $val>'
}
.any {
styp = 'any'
}
// ns.Foo => alias["Foo"]["prototype"]
.struct_ {
styp = g.struct_typ(sym.name)
}
.generic_struct_inst {}
// 'multi_return_int_int' => '[number, number]'
.multi_return {
info := sym.info as table.MultiReturn
types := info.types.map(g.typ(it))
joined := types.join(', ')
styp = '[$joined]'
}
.sum_type {
// TODO: Implement sumtypes
styp = 'union_sym_type'
}
.alias {
// TODO: Implement aliases
styp = 'alias'
}
.enum_ {
// NB: We could declare them as TypeScript enums but TS doesn't like
// our namespacing so these break if declared in a different module.
// Until this is fixed, We need to use the type of an enum's members
// rather than the enum itself, and this can only be 'number' for now
styp = 'number'
}
// 'anon_fn_7_7_1' => '(a number, b number) => void'
.function {
info := sym.info as table.FnType
styp = g.fn_typ(info.func.params, info.func.return_type)
}
.interface_ {
styp = g.js_name(sym.name)
}
.rune {
styp = 'any'
}
.aggregate {
panic('TODO: unhandled aggregate in JS')
}
}
/*
else {
println('jsgen.typ: Unhandled type $t')
styp = sym.name
}
*/
if styp.starts_with('JS.') {
return styp[3..]
}
return styp
}
fn (mut g JsGen) fn_typ(args []table.Param, return_type table.Type) string {
mut res := '('
for i, arg in args {
res += '$arg.name: ${g.typ(arg.typ)}'
if i < args.len - 1 {
res += ', '
}
}
return res + ') => ' + g.typ(return_type)
}
fn (mut g JsGen) struct_typ(s string) string {
ns := get_ns(s)
mut name := if ns == g.ns.name { s.split('.').last() } else { g.get_alias(s) }
mut styp := ''
for i, v in name.split('.') {
if i == 0 {
styp = v
} else {
styp += '["$v"]'
}
}
if ns in ['', g.ns.name] {
return styp
}
return styp + '["prototype"]'
}
fn (mut g JsGen) to_js_typ_val(t table.Type) string {
sym := g.table.get_type_symbol(t)
mut styp := ''
match sym.kind {
.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .any_int, .any_float, .size_t {
styp = '0'
}
.bool {
styp = 'false'
}
.string {
styp = '""'
}
.map {
styp = 'new Map()'
}
.array {
styp = '[]'
}
.struct_ {
styp = 'new ${g.js_name(sym.name)}({})'
}
else {
// TODO
styp = 'undefined'
}
}
return styp
}
[inline]
fn verror(msg string) {
eprintln('jsgen error: $msg')
@ -576,9 +443,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
// TODO
}
ast.CastExpr {
// JS has no types, so no need to cast
// Just write the expression inside
g.expr(node.expr)
g.gen_type_cast_expr(node)
}
ast.CharLiteral {
g.write("'$node.val'")
@ -593,7 +458,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
g.write('${styp}.$node.val')
}
ast.FloatLiteral {
g.write(node.val)
g.write('${g.typ(table.Type(table.f32_type))}($node.val)')
}
ast.Ident {
g.gen_ident(node)
@ -611,7 +476,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
g.gen_infix_expr(node)
}
ast.IntegerLiteral {
g.write(node.val)
g.write('${g.typ(table.Type(table.int_type))}($node.val)')
}
ast.LockExpr {
g.gen_lock_expr(node)
@ -664,8 +529,9 @@ fn (mut g JsGen) expr(node ast.Expr) {
g.gen_string_inter_literal(node)
}
ast.StringLiteral {
text := node.val.replace('`', '\\`')
g.write('`$text`')
text := node.val.replace('\'', "\\'")
if g.file.mod.name == 'builtin' { g.write('new ') }
g.write("string('$text')")
}
ast.StructInit {
// `user := User{name: 'Bob'}`
@ -904,6 +770,12 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
}
if !it.is_method {
g.write('function ')
} else {
if it.attrs.contains('js_getter') {
g.write('get ')
} else if it.attrs.contains('js_setter') {
g.write('set ')
}
}
g.write('${name}(')
if it.is_pub && !it.is_method {
@ -1093,12 +965,17 @@ fn (mut g JsGen) gen_hash_stmt(it ast.HashStmt) {
}
fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
if node.name.starts_with('JS.') {
mut name := node.name
if name.starts_with('JS.') {
return
}
if name in v_types && g.ns.name == 'builtin' {
return
}
js_name := g.js_name(name)
g.gen_attrs(node.attrs)
g.doc.gen_fac_fn(node.fields)
g.write('function ${g.js_name(node.name)}({ ')
g.write('function ${js_name}({ ')
for i, field in node.fields {
g.write('$field.name = ')
if field.has_default_expr {
@ -1117,31 +994,41 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
}
g.dec_indent()
g.writeln('};')
g.writeln('${g.js_name(node.name)}.prototype = {')
g.writeln('${js_name}.prototype = {')
g.inc_indent()
fns := g.method_fn_decls[node.name]
for i, field in node.fields {
fns := g.method_fn_decls[name]
for field in node.fields {
typ := g.typ(field.typ)
g.doc.gen_typ(typ)
g.write('$field.name: ${g.to_js_typ_val(field.typ)}')
if i < node.fields.len - 1 || fns.len > 0 {
g.writeln(',')
} else {
g.writeln('')
}
g.writeln(',')
}
for i, cfn in fns {
for cfn in fns {
g.gen_method_decl(cfn)
if i < fns.len - 1 {
g.writeln(',')
} else {
g.writeln('')
}
g.writeln(',')
}
// gen toString method
fn_names := fns.map(it.name)
if !('toString' in fn_names) {
g.writeln('toString() {')
g.inc_indent()
g.write('return `${js_name} {')
for i, field in node.fields {
g.write(if i == 0 { ' ' } else { ', ' })
match g.typ(field.typ).split('.').last() {
"string" { g.write('$field.name: "\${this["${field.name}"].toString()}"') }
else { g.write('$field.name: \${this["${field.name}"].toString()} ') }
}
}
g.writeln('}`')
g.dec_indent()
g.writeln('}')
}
g.dec_indent()
g.writeln('};\n')
if node.is_pub {
g.push_pub_var(node.name)
g.push_pub_var(name)
}
}
@ -1296,6 +1183,7 @@ fn (mut g JsGen) gen_ident(node ast.Ident) {
// TODO `is`
// TODO handle optionals
g.write(name)
// TODO: Generate .val for basic types
}
fn (mut g JsGen) gen_lock_expr(node ast.LockExpr) {
@ -1392,6 +1280,7 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
g.write('.get(')
}
g.expr(expr.index)
g.write('.toString()')
if !expr.is_setter {
g.write(')')
}
@ -1401,7 +1290,7 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
// 'string'[3] = `o`
} else {
g.expr(expr.left)
g.write('.charCodeAt(')
g.write('.str.charCodeAt(')
g.expr(expr.index)
g.write(')')
}
@ -1433,10 +1322,15 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
g.expr(it.right)
g.write(if r_sym.kind == .map {
'.has('
} else if r_sym.kind == .string {
'.str.includes('
} else {
'.includes('
})
g.expr(it.left)
if l_sym.kind == .string {
g.write('.str')
}
g.write(')')
if it.op == .not_in {
g.write(')')
@ -1454,8 +1348,12 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
} else {
both_are_int := int(it.left_type) in table.integer_type_idxs &&
int(it.right_type) in table.integer_type_idxs
is_arithmetic := it.op in [token.Kind.plus, .minus, .mul, .div, .mod]
if is_arithmetic {
g.write('${g.typ(g.greater_typ(it.left_type, it.right_type))}(')
}
if it.op == .div && both_are_int {
g.write('parseInt(')
g.write('((')
}
g.expr(it.left)
// in js == is non-strict & === is strict, always do strict
@ -1467,13 +1365,47 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
g.write(' $it.op ')
}
g.expr(it.right)
// Int division: 2.5 -> 2 by prepending |0
// Int division: 2.5 -> 2 by appending |0
if it.op == .div && both_are_int {
g.write(',10)')
g.write(')|0)')
}
if is_arithmetic {
g.write(')')
}
}
}
fn (mut g JsGen) greater_typ(left table.Type, right table.Type) table.Type {
l := int(left)
r := int(right)
lr := [l,r]
if table.string_type_idx in lr { return table.Type(table.string_type_idx) }
should_float := (l in table.integer_type_idxs && r in table.float_type_idxs) || (r in table.integer_type_idxs && l in table.float_type_idxs)
if should_float {
if table.f64_type_idx in lr { return table.Type(table.f64_type_idx) }
if table.f32_type_idx in lr { return table.Type(table.f32_type_idx) }
return table.Type(table.any_flt_type)
}
should_int := (l in table.integer_type_idxs && r in table.integer_type_idxs)
if should_int {
// cant add to u64 - if (table.u64_type_idx in lr) { return table.Type(table.u64_type_idx) }
// just guessing this order
if table.i64_type_idx in lr { return table.Type(table.i64_type_idx) }
if table.u32_type_idx in lr { return table.Type(table.u32_type_idx) }
if table.int_type_idx in lr { return table.Type(table.int_type_idx) }
if table.u16_type_idx in lr { return table.Type(table.u16_type_idx) }
if table.i16_type_idx in lr { return table.Type(table.i16_type_idx) }
if table.byte_type_idx in lr { return table.Type(table.byte_type_idx) }
if table.i8_type_idx in lr { return table.Type(table.i8_type_idx) }
return table.Type(table.any_int_type_idx)
}
return table.Type(l)
}
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)
@ -1507,7 +1439,8 @@ fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) {
}
fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
g.write('`')
if g.file.mod.name == 'builtin' { g.write('new ') }
g.write('string(`')
for i, val in it.vals {
escaped_val := val.replace('`', '\\`')
g.write(escaped_val)
@ -1531,7 +1464,7 @@ fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
}
g.write('}')
}
g.write('`')
g.write('`)')
}
fn (mut g JsGen) gen_struct_init(it ast.StructInit) {
@ -1582,3 +1515,24 @@ fn (mut g JsGen) gen_typeof_expr(it ast.TypeOf) {
g.write('"$sym.name"')
}
}
fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) {
is_literal := (
(it.expr is ast.IntegerLiteral && it.typ in table.integer_type_idxs) ||
(it.expr is ast.FloatLiteral && it.typ in table.float_type_idxs)
)
typ := g.typ(it.typ)
if !is_literal {
if !(typ in v_types) || g.ns.name == 'builtin' {
g.write('new ')
}
g.write('${typ}(')
}
g.expr(it.expr)
if typ == 'string' && !(it.expr is ast.StringLiteral) {
g.write('.toString()')
}
if !is_literal {
g.write(')')
}
}

View File

@ -78,6 +78,8 @@ for i, _ in arr2 { println(i) }
println('\n\n4 to 5\t=> ')
for _, v in slice3 { println(v) }
println(int(1.5))
println('\n\n')
// map

View File

@ -34,13 +34,16 @@ fn main() {
mut a := 1
a *= 2
a += 3
println(a) // TODO: Handle string interpolation
println(a)
mut b := hl.Aaa{}
b.update('an update')
println(b)
mut c := Foo{hl.Aaa{}}
c.a.update('another update')
println(c)
println('int(1.5) == "${int(1.5)}"')
d := int(10) + f32(127)
println('typeof (int + f32) == "${typeof(d)}"')
_ = 'done'
{
_ = 'block'
@ -52,7 +55,7 @@ fn main() {
await := '$super: $debugger'
mut finally := 'implemented'
println('$await $finally')
dun := i_am_a_const * 20
dun := i_am_a_const * 20 + 2
dunn := hl.hello // External constant
_ = hl1.nested()
for i := 0; i < 10; i++ {

View File

@ -213,7 +213,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
if p.tok.kind == .name {
// TODO high order fn
name = if language == .js { p.check_js_name() } else { p.check_name() }
if language == .v && !p.pref.translated && util.contains_capital(name) {
if language == .v && !p.pref.translated && util.contains_capital(name) && p.mod != 'builtin' {
p.error('function names cannot contain uppercase letters, use snake_case instead')
return ast.FnDecl{}
}