1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

work on new JS backend

This commit is contained in:
Adel Prokurov 2022-11-25 13:51:22 +05:00
parent 56239b4a23
commit c2d8349c48
5 changed files with 987 additions and 1 deletions

View File

@ -5,7 +5,7 @@ import v.pref
import v.util
import v.builder
import v.gen.js
import v.gen.newjs
pub fn start() {
mut args_and_flags := util.join_env_vflags_and_os_args()[1..]
prefs, _ := pref.parse_args([], args_and_flags)
@ -43,5 +43,7 @@ pub fn gen_js(mut b builder.Builder, v_files []string) string {
util.timing_start('JS GEN')
res := js.gen(b.parsed_files, b.table, b.pref)
util.timing_measure('JS GEN')
newjs.foo()
return res
}

49
vlib/v/gen/newjs/js.v Normal file
View File

@ -0,0 +1,49 @@
module newjs
import strings
import v.ast
import v.token
import v.pref
import v.util
import v.util.version
import v.depgraph
[heap]
struct JsGen {
pref &pref.Preferences
mut:
table &ast.Table = unsafe { nil }
definitions strings.Builder
}
[heap]
struct ModuleContext {
mut:
gen &JsGen = unsafe { nil }
mod ast.Module
files []string
indentation int = 1
minify bool
depdendencies map[&ast.Ident]bool
escaping_vars map[&ast.Var]bool
object_names map[&ast.Ident]string
mod_vars map[string]string
}
struct FuncContext {
mut:
mod_ctx &ModuleContext = unsafe { nil }
parent &FuncContext = unsafe { nil }
all_vars map[string]int = {}
local_vars []string
result_names []ast.Expr
output []byte
delayed_output []byte
pos_available bool
}
pub fn foo() {
}

View File

@ -0,0 +1,373 @@
var $stackDepthOffset = 0;
var $getStackDepth = function () {
var err = new Error();
if (err.stack === undefined) {
return undefined;
}
return $stackDepthOffset + err.stack.split("\n").length;
};
var $panicStackDepth = null, $panicValue;
var $callDeferred = function (deferred, jsErr, fromPanic) {
if (!fromPanic && deferred !== null && $curGoroutine.deferStack.indexOf(deferred) == -1) {
throw jsErr;
}
if (jsErr !== null) {
var newErr = null;
try {
$panic(new $jsErrorPtr(jsErr));
} catch (err) {
newErr = err;
}
$callDeferred(deferred, newErr);
return;
}
if ($curGoroutine.asleep) {
return;
}
$stackDepthOffset--;
var outerPanicStackDepth = $panicStackDepth;
var outerPanicValue = $panicValue;
var localPanicValue = $curGoroutine.panicStack.pop();
if (localPanicValue !== undefined) {
$panicStackDepth = $getStackDepth();
$panicValue = localPanicValue;
}
try {
while (true) {
if (deferred === null) {
deferred = $curGoroutine.deferStack[$curGoroutine.deferStack.length - 1];
if (deferred === undefined) {
/* The panic reached the top of the stack. Clear it and throw it as a JavaScript error. */
$panicStackDepth = null;
if (localPanicValue.Object instanceof Error) {
throw localPanicValue.Object;
}
var msg;
if (localPanicValue.constructor === $String) {
msg = localPanicValue.$val;
} else if (localPanicValue.Error !== undefined) {
msg = localPanicValue.Error();
} else if (localPanicValue.String !== undefined) {
msg = localPanicValue.String();
} else {
msg = localPanicValue;
}
throw new Error(msg);
}
}
var call = deferred.pop();
if (call === undefined) {
$curGoroutine.deferStack.pop();
if (localPanicValue !== undefined) {
deferred = null;
continue;
}
return;
}
var r = call[0].apply(call[2], call[1]);
if (r && r.$blk !== undefined) {
deferred.push([r.$blk, [], r]);
if (fromPanic) {
throw null;
}
return;
}
if (localPanicValue !== undefined && $panicStackDepth === null) {
/* error was recovered */
if (fromPanic) {
throw null;
}
return;
}
}
} catch (e) {
// Deferred function threw a JavaScript exception or tries to unwind stack
// to the point where a panic was handled.
if (fromPanic) {
// Re-throw the exception to reach deferral execution call at the end
// of the function.
throw e;
}
// We are at the end of the function, handle the error or re-throw to
// continue unwinding if necessary, or simply stop unwinding if we got far
// enough.
$callDeferred(deferred, e, fromPanic);
} finally {
if (localPanicValue !== undefined) {
if ($panicStackDepth !== null) {
$curGoroutine.panicStack.push(localPanicValue);
}
$panicStackDepth = outerPanicStackDepth;
$panicValue = outerPanicValue;
}
$stackDepthOffset++;
}
};
var $panic = function (value) {
$curGoroutine.panicStack.push(value);
$callDeferred(null, null, true);
};
var $recover = function () {
if ($panicStackDepth === null || ($panicStackDepth !== undefined && $panicStackDepth !== $getStackDepth() - 2)) {
return $ifaceNil;
}
$panicStackDepth = null;
return $panicValue;
};
var $throw = function (err) { throw err; };
var $noGoroutine = { asleep: false, exit: false, deferStack: [], panicStack: [] };
var $curGoroutine = $noGoroutine, $totalGoroutines = 0, $awakeGoroutines = 0, $checkForDeadlock = true, $exportedFunctions = 0;
var $mainFinished = false;
var $go = function (fun, args) {
$totalGoroutines++;
$awakeGoroutines++;
var $goroutine = function () {
try {
$curGoroutine = $goroutine;
var r = fun.apply(undefined, args);
if (r && r.$blk !== undefined) {
fun = function () { return r.$blk(); };
args = [];
return;
}
$goroutine.exit = true;
} catch (err) {
if (!$goroutine.exit) {
throw err;
}
} finally {
$curGoroutine = $noGoroutine;
if ($goroutine.exit) { /* also set by runtime.Goexit() */
$totalGoroutines--;
$goroutine.asleep = true;
}
if ($goroutine.asleep) {
$awakeGoroutines--;
if (!$mainFinished && $awakeGoroutines === 0 && $checkForDeadlock && $exportedFunctions === 0) {
console.error("fatal error: all goroutines are asleep - deadlock!");
if ($global.process !== undefined) {
$global.process.exit(2);
}
}
}
}
};
$goroutine.asleep = false;
$goroutine.exit = false;
$goroutine.deferStack = [];
$goroutine.panicStack = [];
$schedule($goroutine);
};
var $scheduled = [];
var $runScheduled = function () {
// For nested setTimeout calls browsers enforce 4ms minimum delay. We minimize
// the effect of this penalty by queueing the timer preemptively before we run
// the goroutines, and later cancelling it if it turns out unneeded. See:
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#nested_timeouts
var nextRun = setTimeout($runScheduled);
try {
var start = Date.now();
var r;
while ((r = $scheduled.shift()) !== undefined) {
r();
// We need to interrupt this loop in order to allow the event loop to
// process timers, IO, etc. However, invoking scheduling through
// setTimeout is ~1000 times more expensive, so we amortize this cost by
// looping until the 4ms minimal delay has elapsed (assuming there are
// scheduled goroutines to run), and then yield to the event loop.
var elapsed = Date.now() - start;
if (elapsed > 4 || elapsed < 0) { break; }
}
} finally {
if ($scheduled.length == 0) {
// Cancel scheduling pass if there's nothing to run.
clearTimeout(nextRun);
}
}
};
var $schedule = function (goroutine) {
if (goroutine.asleep) {
goroutine.asleep = false;
$awakeGoroutines++;
}
$scheduled.push(goroutine);
if ($curGoroutine === $noGoroutine) {
$runScheduled();
}
};
var $setTimeout = function (f, t) {
$awakeGoroutines++;
return setTimeout(function () {
$awakeGoroutines--;
f();
}, t);
};
var $block = function () {
if ($curGoroutine === $noGoroutine) {
$throwRuntimeError("cannot block in JavaScript callback, fix by wrapping code in goroutine");
}
$curGoroutine.asleep = true;
};
var $restore = function (context, params) {
if (context !== undefined && context.$blk !== undefined) {
return context;
}
return params;
}
var $send = function (chan, value) {
if (chan.$closed) {
$throwRuntimeError("send on closed channel");
}
var queuedRecv = chan.$recvQueue.shift();
if (queuedRecv !== undefined) {
queuedRecv([value, true]);
return;
}
if (chan.$buffer.length < chan.$capacity) {
chan.$buffer.push(value);
return;
}
var thisGoroutine = $curGoroutine;
var closedDuringSend;
chan.$sendQueue.push(function (closed) {
closedDuringSend = closed;
$schedule(thisGoroutine);
return value;
});
$block();
return {
$blk: function () {
if (closedDuringSend) {
$throwRuntimeError("send on closed channel");
}
}
};
};
var $recv = function (chan) {
var queuedSend = chan.$sendQueue.shift();
if (queuedSend !== undefined) {
chan.$buffer.push(queuedSend(false));
}
var bufferedValue = chan.$buffer.shift();
if (bufferedValue !== undefined) {
return [bufferedValue, true];
}
if (chan.$closed) {
return [chan.$elem.zero(), false];
}
var thisGoroutine = $curGoroutine;
var f = { $blk: function () { return this.value; } };
var queueEntry = function (v) {
f.value = v;
$schedule(thisGoroutine);
};
chan.$recvQueue.push(queueEntry);
$block();
return f;
};
var $close = function (chan) {
if (chan.$closed) {
$throwRuntimeError("close of closed channel");
}
chan.$closed = true;
while (true) {
var queuedSend = chan.$sendQueue.shift();
if (queuedSend === undefined) {
break;
}
queuedSend(true); /* will panic */
}
while (true) {
var queuedRecv = chan.$recvQueue.shift();
if (queuedRecv === undefined) {
break;
}
queuedRecv([chan.$elem.zero(), false]);
}
};
var $select = function (comms) {
var ready = [];
var selection = -1;
for (var i = 0; i < comms.length; i++) {
var comm = comms[i];
var chan = comm[0];
switch (comm.length) {
case 0: /* default */
selection = i;
break;
case 1: /* recv */
if (chan.$sendQueue.length !== 0 || chan.$buffer.length !== 0 || chan.$closed) {
ready.push(i);
}
break;
case 2: /* send */
if (chan.$closed) {
$throwRuntimeError("send on closed channel");
}
if (chan.$recvQueue.length !== 0 || chan.$buffer.length < chan.$capacity) {
ready.push(i);
}
break;
}
}
if (ready.length !== 0) {
selection = ready[Math.floor(Math.random() * ready.length)];
}
if (selection !== -1) {
var comm = comms[selection];
switch (comm.length) {
case 0: /* default */
return [selection];
case 1: /* recv */
return [selection, $recv(comm[0])];
case 2: /* send */
$send(comm[0], comm[1]);
return [selection];
}
}
var entries = [];
var thisGoroutine = $curGoroutine;
var f = { $blk: function () { return this.selection; } };
var removeFromQueues = function () {
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
var queue = entry[0];
var index = queue.indexOf(entry[1]);
if (index !== -1) {
queue.splice(index, 1);
}
}
};
for (var i = 0; i < comms.length; i++) {
(function (i) {
var comm = comms[i];
switch (comm.length) {
case 1: /* recv */
var queueEntry = function (value) {
f.selection = [i, value];
removeFromQueues();
$schedule(thisGoroutine);
};
entries.push([comm[0].$recvQueue, queueEntry]);
comm[0].$recvQueue.push(queueEntry);
break;
case 2: /* send */
var queueEntry = function () {
if (comm[0].$closed) {
$throwRuntimeError("send on closed channel");
}
f.selection = [i];
removeFromQueues();
$schedule(thisGoroutine);
return comm[1];
};
entries.push([comm[0].$sendQueue, queueEntry]);
comm[0].$sendQueue.push(queueEntry);
break;
}
})(i);
}
$block();
return f;
};

View File

@ -0,0 +1,390 @@
var $jsObjectPtr, $jsErrorPtr;
var $needsExternalization = function(t) {
switch (t.kind) {
case $kindBool:
case $kindInt:
case $kindInt8:
case $kindInt16:
case $kindInt32:
case $kindUint:
case $kindUint8:
case $kindUint16:
case $kindUint32:
case $kindUintptr:
case $kindFloat32:
case $kindFloat64:
return false;
default:
return t !== $jsObjectPtr;
}
};
var $externalize = function(v, t, makeWrapper) {
if (t === $jsObjectPtr) {
return v;
}
switch (t.kind) {
case $kindBool:
case $kindInt:
case $kindInt8:
case $kindInt16:
case $kindInt32:
case $kindUint:
case $kindUint8:
case $kindUint16:
case $kindUint32:
case $kindUintptr:
case $kindFloat32:
case $kindFloat64:
return v;
case $kindInt64:
case $kindUint64:
return $flatten64(v);
case $kindArray:
if ($needsExternalization(t.elem)) {
return $mapArray(v, function(e) { return $externalize(e, t.elem, makeWrapper); });
}
return v;
case $kindFunc:
return $externalizeFunction(v, t, false, makeWrapper);
case $kindInterface:
if (v === $ifaceNil) {
return null;
}
if (v.constructor === $jsObjectPtr) {
return v.$val.object;
}
return $externalize(v.$val, v.constructor, makeWrapper);
case $kindMap:
var m = {};
var keys = Array.from(v.keys());
for (var i = 0; i < keys.length; i++) {
var entry = v.get(keys[i]);
m[$externalize(entry.k, t.key, makeWrapper)] = $externalize(entry.v, t.elem, makeWrapper);
}
return m;
case $kindPtr:
if (v === t.nil) {
return null;
}
return $externalize(v.$get(), t.elem, makeWrapper);
case $kindSlice:
if ($needsExternalization(t.elem)) {
return $mapArray($sliceToNativeArray(v), function(e) { return $externalize(e, t.elem, makeWrapper); });
}
return $sliceToNativeArray(v);
case $kindString:
if ($isASCII(v)) {
return v;
}
var s = "", r;
for (var i = 0; i < v.length; i += r[1]) {
r = $decodeRune(v, i);
var c = r[0];
if (c > 0xFFFF) {
var h = Math.floor((c - 0x10000) / 0x400) + 0xD800;
var l = (c - 0x10000) % 0x400 + 0xDC00;
s += String.fromCharCode(h, l);
continue;
}
s += String.fromCharCode(c);
}
return s;
case $kindStruct:
var timePkg = $packages["time"];
if (timePkg !== undefined && v.constructor === timePkg.Time.ptr) {
var milli = $div64(v.UnixNano(), new $Int64(0, 1000000));
return new Date($flatten64(milli));
}
var noJsObject = {};
var searchJsObject = function(v, t) {
if (t === $jsObjectPtr) {
return v;
}
switch (t.kind) {
case $kindPtr:
if (v === t.nil) {
return noJsObject;
}
return searchJsObject(v.$get(), t.elem);
case $kindStruct:
var f = t.fields[0];
return searchJsObject(v[f.prop], f.typ);
case $kindInterface:
return searchJsObject(v.$val, v.constructor);
default:
return noJsObject;
}
};
var o = searchJsObject(v, t);
if (o !== noJsObject) {
return o;
}
if (makeWrapper !== undefined) {
return makeWrapper(v);
}
o = {};
for (var i = 0; i < t.fields.length; i++) {
var f = t.fields[i];
if (!f.exported) {
continue;
}
o[f.name] = $externalize(v[f.prop], f.typ, makeWrapper);
}
return o;
}
$throwRuntimeError("cannot externalize " + t.string);
};
var $externalizeFunction = function(v, t, passThis, makeWrapper) {
if (v === $throwNilPointerError) {
return null;
}
if (v.$externalizeWrapper === undefined) {
$checkForDeadlock = false;
v.$externalizeWrapper = function() {
var args = [];
for (var i = 0; i < t.params.length; i++) {
if (t.variadic && i === t.params.length - 1) {
var vt = t.params[i].elem, varargs = [];
for (var j = i; j < arguments.length; j++) {
varargs.push($internalize(arguments[j], vt, makeWrapper));
}
args.push(new (t.params[i])(varargs));
break;
}
args.push($internalize(arguments[i], t.params[i], makeWrapper));
}
var result = v.apply(passThis ? this : undefined, args);
switch (t.results.length) {
case 0:
return;
case 1:
return $externalize($copyIfRequired(result, t.results[0]), t.results[0], makeWrapper);
default:
for (var i = 0; i < t.results.length; i++) {
result[i] = $externalize($copyIfRequired(result[i], t.results[i]), t.results[i], makeWrapper);
}
return result;
}
};
}
return v.$externalizeWrapper;
};
var $internalize = function(v, t, recv, seen, makeWrapper) {
if (t === $jsObjectPtr) {
return v;
}
if (t === $jsObjectPtr.elem) {
$throwRuntimeError("cannot internalize js.Object, use *js.Object instead");
}
if (v && v.__internal_object__ !== undefined) {
return $assertType(v.__internal_object__, t, false);
}
var timePkg = $packages["time"];
if (timePkg !== undefined && t === timePkg.Time) {
if (!(v !== null && v !== undefined && v.constructor === Date)) {
$throwRuntimeError("cannot internalize time.Time from " + typeof v + ", must be Date");
}
return timePkg.Unix(new $Int64(0, 0), new $Int64(0, v.getTime() * 1000000));
}
// Cache for values we've already internalized in order to deal with circular
// references.
if (seen === undefined) { seen = new Map(); }
if (!seen.has(t)) { seen.set(t, new Map()); }
if (seen.get(t).has(v)) { return seen.get(t).get(v); }
switch (t.kind) {
case $kindBool:
return !!v;
case $kindInt:
return parseInt(v);
case $kindInt8:
return parseInt(v) << 24 >> 24;
case $kindInt16:
return parseInt(v) << 16 >> 16;
case $kindInt32:
return parseInt(v) >> 0;
case $kindUint:
return parseInt(v);
case $kindUint8:
return parseInt(v) << 24 >>> 24;
case $kindUint16:
return parseInt(v) << 16 >>> 16;
case $kindUint32:
case $kindUintptr:
return parseInt(v) >>> 0;
case $kindInt64:
case $kindUint64:
return new t(0, v);
case $kindFloat32:
case $kindFloat64:
return parseFloat(v);
case $kindArray:
if (v.length !== t.len) {
$throwRuntimeError("got array with wrong size from JavaScript native");
}
return $mapArray(v, function(e) { return $internalize(e, t.elem, makeWrapper); });
case $kindFunc:
return function() {
var args = [];
for (var i = 0; i < t.params.length; i++) {
if (t.variadic && i === t.params.length - 1) {
var vt = t.params[i].elem, varargs = arguments[i];
for (var j = 0; j < varargs.$length; j++) {
args.push($externalize(varargs.$array[varargs.$offset + j], vt, makeWrapper));
}
break;
}
args.push($externalize(arguments[i], t.params[i], makeWrapper));
}
var result = v.apply(recv, args);
switch (t.results.length) {
case 0:
return;
case 1:
return $internalize(result, t.results[0], makeWrapper);
default:
for (var i = 0; i < t.results.length; i++) {
result[i] = $internalize(result[i], t.results[i], makeWrapper);
}
return result;
}
};
case $kindInterface:
if (t.methods.length !== 0) {
$throwRuntimeError("cannot internalize " + t.string);
}
if (v === null) {
return $ifaceNil;
}
if (v === undefined) {
return new $jsObjectPtr(undefined);
}
switch (v.constructor) {
case Int8Array:
return new ($sliceType($Int8))(v);
case Int16Array:
return new ($sliceType($Int16))(v);
case Int32Array:
return new ($sliceType($Int))(v);
case Uint8Array:
return new ($sliceType($Uint8))(v);
case Uint16Array:
return new ($sliceType($Uint16))(v);
case Uint32Array:
return new ($sliceType($Uint))(v);
case Float32Array:
return new ($sliceType($Float32))(v);
case Float64Array:
return new ($sliceType($Float64))(v);
case Array:
return $internalize(v, $sliceType($emptyInterface), makeWrapper);
case Boolean:
return new $Bool(!!v);
case Date:
if (timePkg === undefined) {
/* time package is not present, internalize as &js.Object{Date} so it can be externalized into original Date. */
return new $jsObjectPtr(v);
}
return new timePkg.Time($internalize(v, timePkg.Time, makeWrapper));
case (function () { }).constructor: // is usually Function, but in Chrome extensions it is something else
var funcType = $funcType([$sliceType($emptyInterface)], [$jsObjectPtr], true);
return new funcType($internalize(v, funcType, makeWrapper));
case Number:
return new $Float64(parseFloat(v));
case String:
return new $String($internalize(v, $String, makeWrapper));
default:
if ($global.Node && v instanceof $global.Node) {
return new $jsObjectPtr(v);
}
var mapType = $mapType($String, $emptyInterface);
return new mapType($internalize(v, mapType, recv, seen, makeWrapper));
}
case $kindMap:
var m = new Map();
seen.get(t).set(v, m);
var keys = $keys(v);
for (var i = 0; i < keys.length; i++) {
var k = $internalize(keys[i], t.key, recv, seen, makeWrapper);
m.set(t.key.keyFor(k), { k: k, v: $internalize(v[keys[i]], t.elem, recv, seen, makeWrapper) });
}
return m;
case $kindPtr:
if (t.elem.kind === $kindStruct) {
return $internalize(v, t.elem, makeWrapper);
}
case $kindSlice:
return new t($mapArray(v, function(e) { return $internalize(e, t.elem, makeWrapper); }));
case $kindString:
v = String(v);
if ($isASCII(v)) {
return v;
}
var s = "";
var i = 0;
while (i < v.length) {
var h = v.charCodeAt(i);
if (0xD800 <= h && h <= 0xDBFF) {
var l = v.charCodeAt(i + 1);
var c = (h - 0xD800) * 0x400 + l - 0xDC00 + 0x10000;
s += $encodeRune(c);
i += 2;
continue;
}
s += $encodeRune(h);
i++;
}
return s;
case $kindStruct:
var noJsObject = {};
var searchJsObject = function(t) {
if (t === $jsObjectPtr) {
return v;
}
if (t === $jsObjectPtr.elem) {
$throwRuntimeError("cannot internalize js.Object, use *js.Object instead");
}
switch (t.kind) {
case $kindPtr:
return searchJsObject(t.elem);
case $kindStruct:
var f = t.fields[0];
var o = searchJsObject(f.typ);
if (o !== noJsObject) {
var n = new t.ptr();
n[f.prop] = o;
return n;
}
return noJsObject;
default:
return noJsObject;
}
};
var o = searchJsObject(t);
if (o !== noJsObject) {
return o;
}
}
$throwRuntimeError("cannot internalize " + t.string);
};
var $copyIfRequired = function(v, typ) {
// interface values
if (v && v.constructor && v.constructor.copy) {
return new v.constructor($clone(v.$val, v.constructor))
}
// array and struct values
if (typ.copy) {
var clone = typ.zero();
typ.copy(clone, v);
return clone;
}
return v;
}
/* $isASCII reports whether string s contains only ASCII characters. */
var $isASCII = function(s) {
for (var i = 0; i < s.length; i++) {
if (s.charCodeAt(i) >= 128) {
return false;
}
}
return true;
};

172
vlib/v/gen/newjs/util.v Normal file
View File

@ -0,0 +1,172 @@
module newjs
import net.urllib
import v.ast
fn (mut fc FuncContext) write(b []byte) int {
fc.output << b
return b.len
}
fn (mut fc FuncContext) print(str string) {
fc.write('\t'.repeat(fc.mod_ctx.indentation).bytes())
fc.write(str.bytes())
fc.write('\n'.bytes())
fc.write(fc.delayed_output)
fc.delayed_output = []
}
fn (mut fc FuncContext) indent(f fn()) {
fc.mod_ctx.indentation++
f()
fc.mod_ctx.indentation--
}
fn (mut fc FuncContext) catch_output(indent int, f fn()) []byte {
origoutput := fc.output
fc.output = []
fc.mod_ctx.indentation += indent
f()
caught := fc.output
fc.output = origoutput
fc.mod_ctx.indentation -= indent
return caught
}
fn (mut fc FuncContext) print_cond(cond bool, on_true string, on_false string) {
if !cond {
fc.print('/* ${on_true.replace('*/', '<star>/')} */ ${on_false}')
return
}
fc.print('${on_true}')
}
fn (mut fc FuncContext) delayed(f fn()) {
fc.delayed_output = fc.catch_output(0, f)
}
fn (mut fc FuncContext) new_variable_with_level(name_ string, mod_level bool) string {
if name_ == '' {
panic('new_variable: empty name')
}
mut name := encode_ident(name_)
if fc.mod_ctx.minify {
mut i := 0
for {
mut offset := int('a'[0])
if mod_level {
offset = int('A'[0])
}
mut j := 0
name = ''
for {
name = '${rune(offset+(j%26))}${name}'
j = j / 26 - 1
if j == -1 {
break
}
if fc.all_vars[name] == 0 {
break
}
i++
}
}
}
n := fc.all_vars[name]
fc.all_vars[name] = n + 1
mut var_name := name
if n > 0 {
var_name = '${name}\$${n}'
}
if mod_level {
mut c2 := fc.parent
for unsafe { c2 != nil } {
c2.all_vars[var_name] = n + 1
c2 = c2.parent
}
}
fc.local_vars << var_name
return var_name
}
fn (mut fc FuncContext) new_ident(name string, t ast.Type) {
}
fn is_mod_level(fc &FuncContext, sym &ast.Ident) bool {
return unsafe { sym.scope is nil || (sym.scope == fc.mod_ctx.gen.table.global_scope || sym.scope.parent == fc.mod_ctx.gen.table.global_scope) }
}
fn (mut fc FuncContext) ident_name(o &ast.Ident) string {
if is_mod_level(fc, o) {
fc.mod_ctx.depdendencies[o] = true
if o.mod != fc.mod_ctx.mod.name {
return '${fc.mod_var_str(o.mod)}.${o.name}'
}
}
name := fc.mod_ctx.object_names[o] or {
name := fc.new_variable_with_level(o.name, is_mod_level(o))
fc.mod_ctx.object_names[o] = name
name
}
if o.obj is ast.Var {
if fc.mod_ctx.escaping_vars[o.obj] {
return '${name}[0]'
}
}
return name
}
fn (mut fc FuncContext) mod_var(mod &ast.Module) string {
if mod.name == fc.mod_ctx.mod.name {
return '\$mod'
}
mod_var := fc.mod_ctx.mod_vars[mod.name] or {
'\$modules["${mod.name}"]'
}
return mod_var
}
fn (mut fc FuncContext) mod_var_str(mod string) string {
if mod == fc.mod_ctx.mod.name {
return '\$mod'
}
mod_var := fc.mod_ctx.mod_vars[mod] or {
'\$modules["${mod}"]'
}
return mod_var
}
fn encode_ident(name string) string {
return urllib.query_escape(name).replace('%', '$')
}
fn (fc &FuncContext) write_pos() {
// tbd
}