js: types (#7108)
parent
a2ec52b8c4
commit
90c1c639fe
|
@ -4,14 +4,18 @@
|
||||||
|
|
||||||
module builtin
|
module builtin
|
||||||
|
|
||||||
|
fn (a any) toString()
|
||||||
|
|
||||||
pub fn println(s any) {
|
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) {
|
pub fn print(s any) {
|
||||||
// TODO
|
// TODO
|
||||||
// $if js.node {
|
// $if js.node {
|
||||||
JS.process.stdout.write(s)
|
JS.process.stdout.write(s.toString())
|
||||||
// } $else {
|
// } $else {
|
||||||
// panic('Cannot `print` in a browser, use `println` instead')
|
// panic('Cannot `print` in a browser, use `println` instead')
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -7,6 +7,23 @@
|
||||||
|
|
||||||
module builtin
|
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
|
// Top level functions
|
||||||
fn JS.eval(string) any
|
fn JS.eval(string) any
|
||||||
fn JS.parseInt(string, f64) f64
|
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.sin(f64) f64
|
||||||
fn JS.Math.sqrt(f64) f64
|
fn JS.Math.sqrt(f64) f64
|
||||||
fn JS.Math.tan(f64) f64
|
fn JS.Math.tan(f64) f64
|
||||||
|
|
||||||
|
// JSON
|
||||||
|
fn JS.JSON.stringify(any) string
|
||||||
|
fn JS.JSON.parse(string) any
|
|
@ -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))
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ module js
|
||||||
import strings
|
import strings
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.table
|
import v.table
|
||||||
|
import v.token
|
||||||
import v.pref
|
import v.pref
|
||||||
import v.util
|
import v.util
|
||||||
import v.depgraph
|
import v.depgraph
|
||||||
|
@ -13,7 +14,9 @@ const (
|
||||||
'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'finally', 'for', 'function', 'if',
|
'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'finally', 'for', 'function', 'if',
|
||||||
'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'package', 'private', 'protected',
|
'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'package', 'private', 'protected',
|
||||||
'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void',
|
'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',
|
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']
|
'\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']
|
||||||
)
|
)
|
||||||
|
@ -43,6 +46,8 @@ mut:
|
||||||
inside_loop bool
|
inside_loop bool
|
||||||
inside_map_set bool // map.set(key, value)
|
inside_map_set bool // map.set(key, value)
|
||||||
inside_builtin bool
|
inside_builtin bool
|
||||||
|
generated_builtin bool
|
||||||
|
inside_def_typ_decl bool
|
||||||
is_test bool
|
is_test bool
|
||||||
stmt_start_pos int
|
stmt_start_pos int
|
||||||
defer_stmts []ast.DeferStmt
|
defer_stmts []ast.DeferStmt
|
||||||
|
@ -89,6 +94,11 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
|
||||||
imports << imp.mod
|
imports << imp.mod
|
||||||
}
|
}
|
||||||
graph.add(g.file.mod.name, imports)
|
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)
|
g.stmts(file.stmts)
|
||||||
// store the current namespace
|
// store the current namespace
|
||||||
g.escape_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\t/* module exports */'
|
||||||
}
|
}
|
||||||
out += '\n\treturn {'
|
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 {
|
for i, pub_var in g.namespaces[node.name].pub_vars {
|
||||||
out += '\n\t\t$pub_var'
|
out += '\n\t\t$pub_var'
|
||||||
if i < g.namespaces[node.name].pub_vars.len - 1 {
|
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 += 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 {
|
if pref.is_shared {
|
||||||
// Export, through CommonJS, the module of the entry file if `-shared` was passed
|
// Export, through CommonJS, the module of the entry file if `-shared` was passed
|
||||||
export := nodes[nodes.len - 1].name
|
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
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +207,7 @@ pub fn (mut g JsGen) find_class_methods(stmts []ast.Stmt) {
|
||||||
ast.FnDecl {
|
ast.FnDecl {
|
||||||
if stmt.is_method {
|
if stmt.is_method {
|
||||||
// Found struct method, store it to be generated along with the class.
|
// 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.
|
// Workaround until `map[key] << val` works.
|
||||||
mut arr := g.method_fn_decls[class_name]
|
mut arr := g.method_fn_decls[class_name]
|
||||||
arr << stmt
|
arr << stmt
|
||||||
|
@ -199,171 +231,6 @@ pub fn (g JsGen) hashes() string {
|
||||||
return res
|
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]
|
[inline]
|
||||||
fn verror(msg string) {
|
fn verror(msg string) {
|
||||||
eprintln('jsgen error: $msg')
|
eprintln('jsgen error: $msg')
|
||||||
|
@ -576,9 +443,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
ast.CastExpr {
|
ast.CastExpr {
|
||||||
// JS has no types, so no need to cast
|
g.gen_type_cast_expr(node)
|
||||||
// Just write the expression inside
|
|
||||||
g.expr(node.expr)
|
|
||||||
}
|
}
|
||||||
ast.CharLiteral {
|
ast.CharLiteral {
|
||||||
g.write("'$node.val'")
|
g.write("'$node.val'")
|
||||||
|
@ -593,7 +458,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
|
||||||
g.write('${styp}.$node.val')
|
g.write('${styp}.$node.val')
|
||||||
}
|
}
|
||||||
ast.FloatLiteral {
|
ast.FloatLiteral {
|
||||||
g.write(node.val)
|
g.write('${g.typ(table.Type(table.f32_type))}($node.val)')
|
||||||
}
|
}
|
||||||
ast.Ident {
|
ast.Ident {
|
||||||
g.gen_ident(node)
|
g.gen_ident(node)
|
||||||
|
@ -611,7 +476,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
|
||||||
g.gen_infix_expr(node)
|
g.gen_infix_expr(node)
|
||||||
}
|
}
|
||||||
ast.IntegerLiteral {
|
ast.IntegerLiteral {
|
||||||
g.write(node.val)
|
g.write('${g.typ(table.Type(table.int_type))}($node.val)')
|
||||||
}
|
}
|
||||||
ast.LockExpr {
|
ast.LockExpr {
|
||||||
g.gen_lock_expr(node)
|
g.gen_lock_expr(node)
|
||||||
|
@ -664,8 +529,9 @@ fn (mut g JsGen) expr(node ast.Expr) {
|
||||||
g.gen_string_inter_literal(node)
|
g.gen_string_inter_literal(node)
|
||||||
}
|
}
|
||||||
ast.StringLiteral {
|
ast.StringLiteral {
|
||||||
text := node.val.replace('`', '\\`')
|
text := node.val.replace('\'', "\\'")
|
||||||
g.write('`$text`')
|
if g.file.mod.name == 'builtin' { g.write('new ') }
|
||||||
|
g.write("string('$text')")
|
||||||
}
|
}
|
||||||
ast.StructInit {
|
ast.StructInit {
|
||||||
// `user := User{name: 'Bob'}`
|
// `user := User{name: 'Bob'}`
|
||||||
|
@ -904,6 +770,12 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
|
||||||
}
|
}
|
||||||
if !it.is_method {
|
if !it.is_method {
|
||||||
g.write('function ')
|
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}(')
|
g.write('${name}(')
|
||||||
if it.is_pub && !it.is_method {
|
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) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
if name in v_types && g.ns.name == 'builtin' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
js_name := g.js_name(name)
|
||||||
g.gen_attrs(node.attrs)
|
g.gen_attrs(node.attrs)
|
||||||
g.doc.gen_fac_fn(node.fields)
|
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 {
|
for i, field in node.fields {
|
||||||
g.write('$field.name = ')
|
g.write('$field.name = ')
|
||||||
if field.has_default_expr {
|
if field.has_default_expr {
|
||||||
|
@ -1117,31 +994,41 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
|
||||||
}
|
}
|
||||||
g.dec_indent()
|
g.dec_indent()
|
||||||
g.writeln('};')
|
g.writeln('};')
|
||||||
g.writeln('${g.js_name(node.name)}.prototype = {')
|
g.writeln('${js_name}.prototype = {')
|
||||||
g.inc_indent()
|
g.inc_indent()
|
||||||
fns := g.method_fn_decls[node.name]
|
fns := g.method_fn_decls[name]
|
||||||
for i, field in node.fields {
|
for field in node.fields {
|
||||||
typ := g.typ(field.typ)
|
typ := g.typ(field.typ)
|
||||||
g.doc.gen_typ(typ)
|
g.doc.gen_typ(typ)
|
||||||
g.write('$field.name: ${g.to_js_typ_val(field.typ)}')
|
g.write('$field.name: ${g.to_js_typ_val(field.typ)}')
|
||||||
if i < node.fields.len - 1 || fns.len > 0 {
|
|
||||||
g.writeln(',')
|
g.writeln(',')
|
||||||
} else {
|
|
||||||
g.writeln('')
|
|
||||||
}
|
}
|
||||||
}
|
for cfn in fns {
|
||||||
for i, cfn in fns {
|
|
||||||
g.gen_method_decl(cfn)
|
g.gen_method_decl(cfn)
|
||||||
if i < fns.len - 1 {
|
|
||||||
g.writeln(',')
|
g.writeln(',')
|
||||||
} else {
|
}
|
||||||
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.dec_indent()
|
||||||
g.writeln('};\n')
|
g.writeln('};\n')
|
||||||
if node.is_pub {
|
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 `is`
|
||||||
// TODO handle optionals
|
// TODO handle optionals
|
||||||
g.write(name)
|
g.write(name)
|
||||||
|
// TODO: Generate .val for basic types
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g JsGen) gen_lock_expr(node ast.LockExpr) {
|
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.write('.get(')
|
||||||
}
|
}
|
||||||
g.expr(expr.index)
|
g.expr(expr.index)
|
||||||
|
g.write('.toString()')
|
||||||
if !expr.is_setter {
|
if !expr.is_setter {
|
||||||
g.write(')')
|
g.write(')')
|
||||||
}
|
}
|
||||||
|
@ -1401,7 +1290,7 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
|
||||||
// 'string'[3] = `o`
|
// 'string'[3] = `o`
|
||||||
} else {
|
} else {
|
||||||
g.expr(expr.left)
|
g.expr(expr.left)
|
||||||
g.write('.charCodeAt(')
|
g.write('.str.charCodeAt(')
|
||||||
g.expr(expr.index)
|
g.expr(expr.index)
|
||||||
g.write(')')
|
g.write(')')
|
||||||
}
|
}
|
||||||
|
@ -1433,10 +1322,15 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
|
||||||
g.expr(it.right)
|
g.expr(it.right)
|
||||||
g.write(if r_sym.kind == .map {
|
g.write(if r_sym.kind == .map {
|
||||||
'.has('
|
'.has('
|
||||||
|
} else if r_sym.kind == .string {
|
||||||
|
'.str.includes('
|
||||||
} else {
|
} else {
|
||||||
'.includes('
|
'.includes('
|
||||||
})
|
})
|
||||||
g.expr(it.left)
|
g.expr(it.left)
|
||||||
|
if l_sym.kind == .string {
|
||||||
|
g.write('.str')
|
||||||
|
}
|
||||||
g.write(')')
|
g.write(')')
|
||||||
if it.op == .not_in {
|
if it.op == .not_in {
|
||||||
g.write(')')
|
g.write(')')
|
||||||
|
@ -1454,8 +1348,12 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
|
||||||
} else {
|
} else {
|
||||||
both_are_int := int(it.left_type) in table.integer_type_idxs &&
|
both_are_int := int(it.left_type) in table.integer_type_idxs &&
|
||||||
int(it.right_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 {
|
if it.op == .div && both_are_int {
|
||||||
g.write('parseInt(')
|
g.write('((')
|
||||||
}
|
}
|
||||||
g.expr(it.left)
|
g.expr(it.left)
|
||||||
// in js == is non-strict & === is strict, always do strict
|
// 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.write(' $it.op ')
|
||||||
}
|
}
|
||||||
g.expr(it.right)
|
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 {
|
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) {
|
fn (mut g JsGen) gen_map_init_expr(it ast.MapInit) {
|
||||||
// key_typ_sym := g.table.get_type_symbol(it.key_type)
|
// key_typ_sym := g.table.get_type_symbol(it.key_type)
|
||||||
// value_typ_sym := g.table.get_type_symbol(it.value_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) {
|
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 {
|
for i, val in it.vals {
|
||||||
escaped_val := val.replace('`', '\\`')
|
escaped_val := val.replace('`', '\\`')
|
||||||
g.write(escaped_val)
|
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('`')
|
g.write('`)')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g JsGen) gen_struct_init(it ast.StructInit) {
|
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"')
|
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(')')
|
||||||
|
}
|
||||||
|
}
|
|
@ -78,6 +78,8 @@ for i, _ in arr2 { println(i) }
|
||||||
println('\n\n4 to 5\t=> ')
|
println('\n\n4 to 5\t=> ')
|
||||||
for _, v in slice3 { println(v) }
|
for _, v in slice3 { println(v) }
|
||||||
|
|
||||||
|
println(int(1.5))
|
||||||
|
|
||||||
println('\n\n')
|
println('\n\n')
|
||||||
|
|
||||||
// map
|
// map
|
||||||
|
|
|
@ -34,13 +34,16 @@ fn main() {
|
||||||
mut a := 1
|
mut a := 1
|
||||||
a *= 2
|
a *= 2
|
||||||
a += 3
|
a += 3
|
||||||
println(a) // TODO: Handle string interpolation
|
println(a)
|
||||||
mut b := hl.Aaa{}
|
mut b := hl.Aaa{}
|
||||||
b.update('an update')
|
b.update('an update')
|
||||||
println(b)
|
println(b)
|
||||||
mut c := Foo{hl.Aaa{}}
|
mut c := Foo{hl.Aaa{}}
|
||||||
c.a.update('another update')
|
c.a.update('another update')
|
||||||
println(c)
|
println(c)
|
||||||
|
println('int(1.5) == "${int(1.5)}"')
|
||||||
|
d := int(10) + f32(127)
|
||||||
|
println('typeof (int + f32) == "${typeof(d)}"')
|
||||||
_ = 'done'
|
_ = 'done'
|
||||||
{
|
{
|
||||||
_ = 'block'
|
_ = 'block'
|
||||||
|
@ -52,7 +55,7 @@ fn main() {
|
||||||
await := '$super: $debugger'
|
await := '$super: $debugger'
|
||||||
mut finally := 'implemented'
|
mut finally := 'implemented'
|
||||||
println('$await $finally')
|
println('$await $finally')
|
||||||
dun := i_am_a_const * 20
|
dun := i_am_a_const * 20 + 2
|
||||||
dunn := hl.hello // External constant
|
dunn := hl.hello // External constant
|
||||||
_ = hl1.nested()
|
_ = hl1.nested()
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
|
|
|
@ -213,7 +213,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
if p.tok.kind == .name {
|
if p.tok.kind == .name {
|
||||||
// TODO high order fn
|
// TODO high order fn
|
||||||
name = if language == .js { p.check_js_name() } else { p.check_name() }
|
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')
|
p.error('function names cannot contain uppercase letters, use snake_case instead')
|
||||||
return ast.FnDecl{}
|
return ast.FnDecl{}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue