local M = require 'make'
local func = M.func

local tokens = require 'lex'
local srcslot, lexslot, rmapslot, frmapslot, constslot = 20, 16, 12, 8, 4

local rt = require 'rt'

local alloc = require 'alloc'
local types, obj, num, float, str, buf = alloc.types, alloc.obj, alloc.num, alloc.float, alloc.str, alloc.buf
local newi64, newf64, newtbl, newstr, newstrbuf, newvecbuf = alloc.newi64, alloc.newf64, alloc.newtbl, alloc.newstr, alloc.newstrbuf, alloc.newvecbuf
local allocsizef = alloc.allocsizef

local stack = require 'stack'
local tmppush, tmppop, nthtmp, setnthtmp = stack.tmppush, stack.tmppop, stack.nthtmp, stack.setnthtmp

local util = require 'util'

local _table = require '_table'
local tblget, tblset = _table.tblget, _table.tblset

local lxaddval = func(i32, i32, function(f, o)
	local boxlen, buflen, rmslot = f:locals(i32, 3)

	f:i32(frmapslot)
	f:i32(rmapslot)
	f:load(o)
	f:i32load8u(obj.type)
	f:i32(types.float)
	f:eq()
	f:select()
	f:tee(rmslot)
	f:call(nthtmp)
	f:load(o)
	f:call(tblget)
	f:tee(boxlen)
	f:eqz()
	f:iff(function()
		-- push const to constvec
		f:load(o)
		f:call(tmppush)
		f:i32(constslot+4)
		f:call(nthtmp)
		f:tee(buflen)
		f:i32load(buf.len)
		f:i32(2)
		f:shru()

		f:load(buflen)
		f:i32(4)
		f:call(nthtmp)
		f:tee(o)
		f:call(pushvec)
		f:i32(constslot+4)
		f:call(setnthtmp)

		f:tee(buflen)
		f:i64extendu()
		f:call(newi64)
		f:store(boxlen)

		-- add entry to revmap
		f:load(rmslot)
		f:i32(4)
		f:add()
		f:call(nthtmp)
		f:i32(4)
		f:call(nthtmp)
		f:call(tmppop)
		f:load(boxlen)
		f:call(tblset)

		f:load(buflen)
		f:ret()
	end)
	f:load(boxlen)
	f:i32load(num.val)
end)

local CHZERO = string.byte('0')

local lex = func(void, function(f)
	local src, i, ch, j, k, srclen, tlen = f:locals(i32, 7)
	local temp64, temp642 = f:locals(i64, 2)
	local double, flt10 = f:locals(f64, 2)

	f:i32(4)
	f:call(nthtmp)
	f:i32load(str.len)
	f:store(srclen)

	-- lexslot
	f:i32(1011)
	f:call(newstrbuf)
	f:call(tmppush)

	-- rmapslot
	f:call(newtbl)
	f:call(tmppush)

	-- frmapslot
	f:call(newtbl)
	f:call(tmppush)

	-- constslot
	f:i32(256)
	f:call(newvecbuf)
	f:call(tmppush)

	f:block(function(loopwrap)
		f:load(srclen)
		f:eqz()
		f:brif(loopwrap)

		f:loop(function(loopnoinc)
			f:block(i32, function(token1noinc)
				f:block(function(blloop)
					f:load(i)
					f:load(srclen)
					f:geu()
					f:brif(loopwrap)

					f:i32(srcslot)
					f:call(nthtmp)
					f:tee(src)
					f:load(i)
					f:add()
					f:i32load8u(str.base)
					f:tee(ch)
					f:i32(32)
					f:leu()
					f:brif(blloop)

					f:block(i32, function(token1)
						local function tcase(s)
							return string.byte(s) - 33
						end
						f:switch(function()
							f:load(ch)
							f:i32(33)
							f:sub()
						end,
						tcase("'"), tcase('"'), function()
							f:i32(11)
							f:call(newstrbuf)
							f:tee(tlen)
							f:call(tmppush)

							f:block(function(blockscanq)
							f:loop(function(loopscanq)
								f:load(i)
								f:i32(1)
								f:add()
								f:tee(i)
								f:load(srclen)
								f:eq()
								f:brif(blockscanq)

								f:load(i)
								f:i32(srcslot+4)
								f:call(nthtmp)
								f:tee(src)
								f:add()
								f:i32load8u(str.base)
								f:tee(j)
								f:load(ch)
								f:eq()
								f:brif(blockscanq)

								-- prep for pushstr call
								f:load(tlen)

								f:load(j)
								f:i32(string.byte('\\'))
								f:eq()
								f:iff(i32, function(stresc)
									f:i32(0)
									f:load(i)
									f:i32(1)
									f:add()
									f:tee(i)
									f:load(srclen)
									f:eq()
									f:brif(stresc)
									f:drop()

									f:load(i)
									f:load(src)
									f:add()
									f:i32load8u(str.base)
									f:tee(j)
									f:i32(string.byte('"'))
									f:eq()
									f:load(j)
									f:i32(string.byte("'"))
									f:eq()
									f:bor()
									f:load(j)
									f:i32(string.byte('\\'))
									f:eq()
									f:bor()
									f:load(j)
									f:i32(string.byte('\n'))
									f:eq()
									f:bor()
									f:iff(i32, function()
										f:load(j)
									end, function()
										f:load(j)
										f:i32(CHZERO)
										f:sub()
										f:tee(j)
										f:i32(10)
										f:ltu()
										f:iff(i32, function(tblbr)
											f:i32(0)
											f:load(i)
											f:i32(1)
											f:add()
											f:tee(i)
											f:load(srclen)
											f:eq()
											f:brif(stresc)
											f:drop()

											f:load(i)
											f:load(src)
											f:add()
											f:i32load8u(str.base)
											f:i32(CHZERO)
											f:sub()
											f:tee(k)
											f:i32(10)
											f:ltu()
											f:iff(i32, function()
												f:i32(0)
												f:load(i)
												f:i32(1)
												f:add()
												f:tee(i)
												f:load(srclen)
												f:eq()
												f:brif(stresc)
												f:drop()

												f:load(j)
												f:i32(10)
												f:mul()
												f:load(k)
												f:add()
												f:store(j)

												f:load(i)
												f:load(src)
												f:add()
												f:i32load8u(str.base)
												f:i32(CHZERO)
												f:sub()
												f:tee(k)
												f:i32(10)
												f:ltu()
												f:iff(i32, function()
													f:load(j)
													f:i32(10)
													f:mul()
													f:load(k)
													f:add()
												end, function()
													f:load(i)
													f:i32(1)
													f:sub()
													f:store(i)

													f:load(j)
												end)
											end, function()
												f:load(i)
												f:i32(1)
												f:sub()
												f:store(i)

												f:load(j)
											end)
										end, function(tblbr)
											f:switch(function()
												f:load(j)
												f:i32(string.byte('a') - string.byte('0'))
												f:sub()
											end, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 18, 22, 24, -1, 'noesc', function()
												f:unreachable()
											end, 25, function()
												f:loop(function(loop)
													f:i32(0)
													f:load(i)
													f:i32(1)
													f:add()
													f:tee(i)
													f:load(srclen)
													f:eq()
													f:brif(stresc)
													f:drop()

													f:load(i)
													f:load(src)
													f:add()
													f:i32load8u(str.base)
													f:tee(j)
													f:i32(32)
													f:eq()
													f:brif(loop)

													f:load(j)
													f:i32(9)
													f:geu()
													f:load(j)
													f:i32(13)
													f:leu()
													f:bor()
													f:brif(loop)
												end)

												f:load(i)
												f:i32(1)
												f:sub()
												f:store(i)
												f:br(loopscanq)
											end, 23, function()
												f:i32(0)
												f:load(i)
												f:i32(2)
												f:add()
												f:tee(i)
												f:load(srclen)
												f:geu()
												f:brif(stresc)
												f:drop()

												f:load(i)
												f:i32(1)
												f:sub()
												f:load(src)
												f:add()
												f:i32load8u(str.base)
												f:call(chex)
												f:i32(16)
												f:mul()

												f:load(i)
												f:load(src)
												f:add()
												f:i32load8u(str.base)
												f:call(chex)
												f:add()
												f:br(tblbr)
											end, 21, function()
												f:i32(11)
												f:br(tblbr)
											end, 20, function()
												f:i32(0)
												f:load(i)
												f:i32(1)
												f:add()
												f:tee(i)
												f:load(srclen)
												f:eq()
												f:brif(stresc)
												f:drop()

												f:i32(0)
												f:load(i)
												f:load(src)
												f:add()
												f:i32load8u(str.base)
												f:i32(123)
												f:ne()
												f:brif(stresc)
												f:drop()

												f:i32(0)
												f:store(j)

												-- j <- parse {hex} to int
												f:loop(function(uloop)
													f:i32(0)
													f:load(i)
													f:i32(1)
													f:add()
													f:tee(i)
													f:load(srclen)
													f:eq(0)
													f:brif(stresc)
													f:drop()

													f:load(i)
													f:load(src)
													f:add()
													f:i32load8u(str.base)
													f:tee(ch)
													f:i32(125)
													f:eq()
													f:iff(function()
														f:load(j)
														f:i32(4)
														f:shl()
														f:load(ch)
														f:call(chex)
														f:bor()
														f:store(j)
														f:br(uloop)
													end)
												end)

												-- encode utf8 codepoint j
												f:load(j)
												f:i32(0x80)
												f:ltu()
												f:iff(i32, function()
													f:load(tlen)
													f:load(j)
													f:call(pushstr)
												end, function()
													f:load(j)
													f:i32(0x800)
													f:ltu()
													f:iff(i32, function()
														f:load(tlen)
														f:load(j)
														f:i32(6)
														f:shru()
														f:i32(0x1f)
														f:band()
														f:i32(0xc0)
														f:bor()
														f:call(pushstr)

														f:load(j)
														f:i32(0x3f)
														f:band()
														f:i32(0x80)
														f:bor()
														f:call(pushstr)
													end, function()
														f:load(j)
														f:i32(0x10000)
														f:ltu()
														f:iff(i32, function()
															f:load(tlen)
															f:load(j)
															f:i32(12)
															f:shru()
															f:i32(0x0f)
															f:band()
															f:i32(0xe0)
															f:bor()
															f:call(pushstr)

															f:load(j)
															f:i32(6)
															f:shru()
															f:i32(0x3f)
															f:band()
															f:i32(0x80)
															f:bor()
															f:call(pushstr)

															f:load(j)
															f:i32(0x3f)
															f:band()
															f:i32(0x80)
															f:bor()
															f:call(pushstr)
														end, function()
															f:load(tlen)
															f:load(j)
															f:i32(18)
															f:shru()
															f:i32(0x07)
															f:band()
															f:i32(0xf0)
															f:bor()
															f:call(pushstr)

															f:load(j)
															f:i32(12)
															f:shru()
															f:i32(0x3f)
															f:band()
															f:i32(0x80)
															f:bor()
															f:call(pushstr)

															f:load(j)
															f:i32(6)
															f:shru()
															f:i32(0x3f)
															f:band()
															f:i32(0x80)
															f:bor()
															f:call(pushstr)

															f:load(j)
															f:i32(0x3f)
															f:band()
															f:i32(0x80)
															f:bor()
															f:call(pushstr)
														end)
													end)
												end)
												f:store(tlen)
												f:br(loopscanq)
											end, 19, function()
												f:i32(9)
												f:br(tblbr)
											end, 17, function()
												f:i32(13)
												f:br(tblbr)
											end, 13, function()
												f:i32(10)
												f:br(tblbr)
											end, 5, function()
												f:i32(12)
												f:br(tblbr)
											end, 1, function()
												f:i32(8)
												f:br(tblbr)
											end, 0)
											f:i32(7)
										end)
									end)
								end, function()
									f:load(j)
								end)

								f:call(pushstr)
								f:store(tlen)
								f:br(loopscanq)
							end)
							end)

							f:load(tlen)
							f:call(unbufstr)
							f:call(tmppop)
							f:call(lxaddval)
							f:store(ch)

							f:i32(lexslot)
							f:call(nthtmp)
							f:i32(tokens._string)
							f:call(pushstr)

							f:load(ch)
							f:call(pushstr)

							f:load(ch)
							f:i32(8)
							f:shru()
							f:call(pushstr)

							f:load(ch)
							f:i32(16)
							f:shru()
							f:call(pushstr)

							f:load(ch)
							f:i32(24)
							f:shru()
							f:call(pushstr)
							f:i32(lexslot)
							f:call(setnthtmp)

							f:br(blloop)
						end, tcase('#'), function()
							f:i32(tokens._hash)
							f:br(token1)
						end, tcase('%'), function()
							f:i32(tokens._mod)
							f:br(token1)
						end, tcase('&'), function()
							f:i32(tokens._band)
							f:br(token1)
						end, tcase('('), function()
							f:i32(tokens._pl)
							f:br(token1)
						end, tcase(')'), function()
							f:i32(tokens._pr)
							f:br(token1)
						end, tcase('*'), function()
							f:i32(tokens._mul)
							f:br(token1)
						end, tcase('+'), function()
							f:i32(tokens._plus)
							f:br(token1)
						end, tcase(','), function()
							f:i32(tokens._comma)
							f:br(token1)
						end, tcase('-'), function()
							f:load(i)
							f:i32(1)
							f:add()
							f:tee(i)
							f:load(srclen)
							f:ne()
							f:iff(function()
								f:load(i)
								f:load(src)
								f:add()
								f:i32load8u(str.base)
								f:i32(string.byte('-'))
								f:eq()
								f:iff(function()
									f:load(i)
									f:i32(1)
									f:add()
									f:load(srclen)
									f:eq()
									f:brif(blloop)

									f:load(i)
									f:i32(1)
									f:add()
									f:load(src)
									f:add()
									f:i32load8u(str.base)
									f:i32(string.byte('['))
									f:eq()
									f:iff(function(ifdeepcomm)
										f:i32(0)
										f:store(ch)
										f:loop(function(loop) -- count =s
											-- eof?
											f:load(ch)
											f:i32(1)
											f:add()
											f:tee(ch)
											f:load(i)
											f:add()
											f:load(srclen)
											f:eq()
											f:brif(loopnoinc)

											-- =?
											f:load(ch)
											f:load(i)
											f:add()
											f:load(src)
											f:add()
											f:i32load8u(str.base)
											f:i32(string.byte('='))
											f:eq()
											f:brif(loop)

											-- if [, scan, else enter newline scan
											f:load(ch)
											f:load(i)
											f:add()
											f:load(src)
											f:add()
											f:i32load8u(str.base)
											f:i32(string.byte('['))
											f:ne()
											f:brif(ifdeepcomm)
										end)

										f:loop(function(srscan) -- scan for ]
											-- eof?
											f:load(i)
											f:i32(1)
											f:add()
											f:tee(i)
											f:load(ch)
											f:add()
											f:load(srclen)
											f:eq()
											f:brif(loopnoinc)

											-- reloop if not ]
											f:load(i)
											f:load(ch)
											f:add()
											f:load(src)
											f:add()
											f:i32load8u(str.base)
											f:i32(string.byte(']'))
											f:ne()
											f:brif(srscan)

											-- count =s
											f:load(i)
											f:store(j)
											f:loop(void, function(loop)
												-- eof?
												f:load(i)
												f:i32(1)
												f:add()
												f:tee(i)
												f:load(ch)
												f:add()
												f:load(srclen)
												f:eq()
												f:brif(loopnoinc)

												-- =?
												f:load(i)
												f:load(ch)
												f:add()
												f:load(src)
												f:add()
												f:i32load8u(str.base)
												f:i32(string.byte('='))
												f:eq()
												f:brif(loop)
											end)
											f:load(i)
											f:load(j)
											f:sub()
											f:load(ch)
											f:ne()
											f:brif(srscan) -- reloop if wrong = count

											-- if not ], reloop
											f:load(i)
											f:load(ch)
											f:add()
											f:load(src)
											f:add()
											f:i32load8u(str.base)
											f:i32(string.byte(']'))
											f:ne()
											f:brif(srscan)

											-- end of comment. Increment i by ch & break
											f:load(i)
											f:load(ch)
											f:add()
											f:store(i)

											f:br(blloop)
										end)
									end)

									f:loop(function(loop)
										f:load(i)
										f:i32(1)
										f:add()
										f:tee(i)
										f:load(srclen)
										f:eq()
										f:brif(loopnoinc)

										f:load(i)
										f:load(src)
										f:add()
										f:i32load8u(str.base)
										f:i32(string.byte('\n'))
										f:eq()
										f:brtable(loop, blloop)
									end)
								end)
							end)
							f:i32(tokens._minus)
							f:br(token1noinc)
						end, tcase('/'), function()
							f:load(i)
							f:i32(1)
							f:add()
							f:tee(i)
							f:load(srclen)
							f:ne()
							f:iff(function()
								f:load(i)
								f:load(src)
								f:add()
								f:i32load8u(str.base)
								f:i32(string.byte('/'))
								f:eq()
								f:iff(function()
									f:i32(tokens._idiv)
									f:br(token1)
								end)
							end)
							f:i32(tokens._div)
							f:br(token1noinc)
						end, tcase('.'), function(scopes)
							f:load(i)
							f:i32(1)
							f:add()
							f:tee(i)
							f:load(srclen)
							f:eq()
							f:iff(i32, function()
								f:i32(tokens._dot)
							end, function()
								f:load(i)
								f:load(src)
								f:add()
								f:i32load8u(str.base)
								f:tee(ch)
								f:i32(string.byte('.'))
								f:eq()
								f:iff(i32, function()
									f:load(i)
									f:i32(1)
									f:add()
									f:tee(i)
									f:load(srclen)
									f:ne()
									f:iff(function()
										f:load(i)
										f:load(src)
										f:add()
										f:i32load8u(str.base)
										f:i32(string.byte('.'))
										f:eq()
										f:iff(function()
											f:i32(tokens._dotdotdot)
											f:br(token1)
										end)
									end)
									f:i32(tokens._dotdot)
								end, function()
									f:load(ch)
									f:i32(CHZERO)
									f:sub()
									f:i32(10)
									f:ltu()
									f:iff(function()
										-- move i before . to parse as of 0.x
										f:load(i)
										f:i32(2)
										f:sub()
										f:store(i)
										f:i32(CHZERO)
										f:store(ch)
										f:br(scopes.digit)
									end)
									f:i32(tokens._dot)
								end)
							end)
							f:br(token1noinc)
						end, tcase('0'), function(scopes)
							f:load(i)
							f:i32(1)
							f:add()
							f:tee(i)
							f:load(srclen)
							f:ne()
							f:iff(function()
								f:load(i)
								f:load(src)
								f:add()
								f:i32load8u(str.base)
								f:i32(-33)
								f:band()
								f:i32(string.byte('X'))
								f:eq()
								f:iff(function()
									f:i64(0)
									f:store(temp64)
									f:i32(0)
									f:store(tlen)
									f:loop(function(hexloop)
										f:load(i)
										f:i32(1)
										f:add()
										f:tee(i)
										f:load(srclen)
										f:ne()
										f:iff(function()
											f:load(i)
											f:load(src)
											f:add()
											f:i32load8u(str.base)
											f:tee(ch)
											f:i32(string.byte('.'))
											f:eq()
											f:iff(function() -- hex float
												f:load(temp64)
												f:f64convertu()
												f:store(double)

												f:f64(1)
												f:store(flt10)

												f:i32(1)
												f:store(tlen)

												f:loop(function(xfltloop)
													f:load(i)
													f:i32(1)
													f:add()
													f:tee(i)
													f:load(srclen)
													f:ne()
													f:iff(function()
														f:load(i)
														f:load(src)
														f:add()
														f:i32load8u(str.base)
														f:call(chex)
														f:tee(ch)
														f:i32(-1)
														f:ne()
														f:iff(function()
															f:load(double)
															f:load(ch)
															f:f64convertu()
															f:load(flt10)
															f:f64(16)
															f:div()
															f:tee(flt10)
															f:mul()
															f:add()
															f:store(double)
															f:br(xfltloop)
														end)
													end)
												end)
												f:br(scopes.digitpush)
											end)

											f:load(ch)
											f:call(chex)
											f:tee(ch)
											f:i32(-1)
											f:eq()
											f:brif(scopes.digitpush)

											f:load(temp64)
											f:i64(4)
											f:shl()
											f:load(ch)
											f:i64extendu()
											f:bor()
											f:store(temp64)

											f:br(hexloop)
										end)
										f:br(scopes.digitpush)
									end)
								end)
							end)
							f:load(i)
							f:i32(1)
							f:sub()
							f:store(i)

						end, 'digit', tcase('1'), tcase('2'), tcase('3'), tcase('4'), tcase('5'), tcase('6'), tcase('7'), tcase('8'), tcase('9'), function()
							f:load(ch)
							f:i32(CHZERO)
							f:sub()
							f:i64extendu()
							f:store(temp64)

							f:i32(0)
							f:store(tlen)

							f:loop(function(intloop)
								f:load(i)
								f:i32(1)
								f:add()
								f:tee(i)
								f:load(srclen)
								f:ne()
								f:iff(function()
									f:load(i)
									f:load(src)
									f:add()
									f:i64load8u(str.base)
									f:i64(CHZERO)
									f:sub()
									f:tee(temp642)
									f:i64(10)
									f:ltu()
									f:iff(function()
										f:load(temp64)
										f:i64(10)
										f:mul()
										f:load(temp642)
										f:add()
										f:store(temp64)
										f:br(intloop)
									end, function()
										f:load(temp642)
										f:i64(-2)
										f:eq()
										f:iff(function()
											f:load(temp64)
											f:f64convertu()
											f:store(double)

											f:f64(1)
											f:store(flt10)

											f:i32(1)
											f:store(tlen)

											f:loop(function(fltloop)
												f:load(i)
												f:i32(1)
												f:add()
												f:tee(i)
												f:load(srclen)
												f:ne()
												f:iff(function()
													f:load(i)
													f:load(src)
													f:add()
													f:i64load8u(str.base)
													f:i64(CHZERO)
													f:sub()
													f:tee(temp642)
													f:i64(10)
													f:ltu()
													f:iff(function()
														f:load(double)
														f:load(temp642)
														f:f64convertu()
														f:load(flt10)
														f:f64(10)
														f:div()
														f:tee(flt10)
														f:mul()
														f:add()
														f:store(double)
														f:br(fltloop)
													end)
												end)
											end)
										end)
									end)
								end)
							end)
						end, 'digitpush', function() -- shared endpoint of dec/hex parse

							f:load(i)
							f:load(srclen)
							f:ltu()
							f:iff(function(eorp)
								f:i32(0)
								f:store(j)

								f:load(i)
								f:load(src)
								f:add()
								f:i32load8u(str.base)
								f:i32(-33) -- x&~32 is upper(x)
								f:band()
								f:tee(ch)
								f:i32(string.byte('E'))
								f:eq()
								f:tee(k)
								f:load(ch)
								f:i32(string.byte('P'))
								f:eq()
								f:bor()
								f:iff(function()
									f:f64(10)
									f:f64(2)
									f:load(k)
									f:select()
									f:store(flt10)

									-- First iteration is unrolled to check for +-
									f:load(i)
									f:i32(1)
									f:add()
									f:tee(i)
									f:load(srclen)
									f:eq()
									f:brif(eorp)

									f:load(i)
									f:load(src)
									f:add()
									f:i32load8u(str.base)
									f:i32(CHZERO)
									f:sub()
									f:tee(j)
									f:i32(10)
									f:geu()
									f:iff(function()
										f:load(j)
										f:i32(-3)-- -
										f:eq()
										f:iff(function()
											f:f64(1)
											f:load(flt10)
											f:div()
											f:store(flt10)
										end, function()
											f:load(j)
											f:i32(-5) -- + is nop
											f:ne()
											f:brif(eorp) -- invalid number
										end)
										f:i32(0)
										f:store(j)
									end)

									f:loop(function(eorploop)
										f:load(i)
										f:i32(1)
										f:add()
										f:tee(i)
										f:load(srclen)
										f:eq()
										f:brif(eorp)

										f:load(i)
										f:load(src)
										f:add()
										f:i32load8u(str.base)
										f:i32(CHZERO)
										f:sub()
										f:tee(ch)
										f:i32(10)
										f:ltu()
										f:iff(function()
											f:load(j)
											f:i32(10)
											f:mul()
											f:load(ch)
											f:add()
											f:store(j)
											f:br(eorploop)
										end)
									end)
									f:load(tlen)
									f:eqz()
									f:iff(function()
										f:i32(1)
										f:store(tlen)
										f:load(temp64)
										f:f64convertu()
										f:store(double)
									end)
									f:load(j)
									f:iff(function()
										f:loop(function(loop)
											f:load(double)
											f:load(flt10)
											f:mul()
											f:store(double)

											f:load(j)
											f:i32(1)
											f:sub()
											f:tee(j)
											f:brif(loop)
										end)
									end)
								end)
							end)
							f:load(tlen)
							f:iff(i32, function()
								f:load(double)
								f:call(newf64)
							end, function()
								f:load(temp64)
								f:call(newi64)
							end)
							f:call(lxaddval)
							f:store(ch)

							f:i32(lexslot)
							f:call(nthtmp)
							f:i32(tokens._number)
							f:call(pushstr)

							f:load(ch)
							f:call(pushstr)

							f:load(ch)
							f:i32(8)
							f:shru()
							f:call(pushstr)

							f:load(ch)
							f:i32(16)
							f:shru()
							f:call(pushstr)

							f:load(ch)
							f:i32(24)
							f:shru()
							f:call(pushstr)
							f:i32(lexslot)
							f:call(setnthtmp)

							f:br(loopnoinc)
						end, tcase(':'), function()
							f:load(i)
							f:i32(1)
							f:add()
							f:tee(i)
							f:load(srclen)
							f:ne()
							f:iff(function()
								f:load(i)
								f:load(src)
								f:add()
								f:i32load8u(str.base)
								f:i32(string.byte(':'))
								f:eq()
								f:iff(function()
									f:i32(tokens._label)
									f:br(token1)
								end)
							end)
							f:i32(tokens._colon)
							f:br(token1noinc)
						end, tcase(';'), function()
							f:i32(tokens._semi)
							f:br(token1)
						end, tcase('<'), function()
							f:load(i)
							f:i32(1)
							f:add()
							f:tee(i)
							f:load(srclen)
							f:ne()
							f:iff(function()
								f:switch(function()
									f:load(i)
									f:load(src)
									f:add()
									f:i32load8u(str.base)
									f:i32(string.byte('<'))
									f:sub()
								end, 0, '<', function()
									f:i32(tokens._lsh)
									f:br(token1)
								end, 1, '=', function()
									f:i32(tokens._lte)
									f:br(token1)
								end, -1)
							end)
							f:i32(tokens._lt)
							f:br(token1noinc)
						end, tcase('='), function()
							f:i32(lexslot)
							f:call(nthtmp)
							f:load(i)
							f:i32(1)
							f:add()
							f:tee(i)
							f:load(srclen)
							f:eq()
							f:iff(i32, function()
								f:i32(tokens._set)
							end, function()
								f:load(i)
								f:load(src)
								f:add()
								f:i32load8u(str.base)
								f:i32(string.byte('='))
								f:eq()
								f:iff(i32, function()
									f:load(i)
									f:i32(1)
									f:add()
									f:store(i)

									f:i32(tokens._eq)
								end, function()
									f:i32(tokens._set)
								end)
							end)
							f:call(pushstr)
							f:i32(lexslot)
							f:call(setnthtmp)
							f:br(loopnoinc)
						end, tcase('>'), function()
							f:load(i)
							f:i32(1)
							f:add()
							f:tee(i)
							f:load(srclen)
							f:ne()
							f:iff(function()
								f:switch(function()
									f:load(i)
									f:load(src)
									f:add()
									f:i32load8u(str.base)
									f:i32(string.byte('='))
									f:sub()
								end, 0, '=', function()
									f:i32(tokens._gte)
									f:br(token1)
								end, 1, '>', function()
									f:i32(tokens._rsh)
									f:br(token1)
								end, -1)
							end)
							f:i32(tokens._gt)
							f:br(token1noinc)
						end, tcase('a'), tcase('b'), tcase('c'), tcase('d'), tcase('e'), tcase('f'), tcase('g'), tcase('h'), tcase('i'), tcase('j'), tcase('k'), tcase('l'), tcase('m'), tcase('n'), tcase('o'), tcase('p'), tcase('q'), tcase('r'), tcase('s'), tcase('t'), tcase('u'), tcase('v'), tcase('w'), tcase('x'), tcase('y'), tcase('z'),
						tcase('A'), tcase('B'), tcase('C'), tcase('D'), tcase('E'), tcase('F'), tcase('G'), tcase('H'), tcase('I'), tcase('J'), tcase('K'), tcase('L'), tcase('M'), tcase('N'), tcase('O'), tcase('P'), tcase('Q'), tcase('R'), tcase('S'), tcase('T'), tcase('U'), tcase('V'), tcase('W'), tcase('X'), tcase('Y'), tcase('Z'),
						tcase('_'), function()
							f:load(i)
							f:store(j)

							f:block(function(block)
								f:loop(function(loop) -- scan until not 0-9A-Z_a-z
									f:load(i)
									f:i32(1)
									f:add()
									f:tee(i)
									f:load(srclen)
									f:eq()
									f:brif(block)

									f:load(src)
									f:load(i)
									f:add()
									f:i32load8u(str.base)
									f:i32(CHZERO)
									f:sub()
									f:tee(ch)
									f:i32(10)
									f:ltu()
									f:brif(loop)

									f:load(ch)
									f:i32(string.byte('A') - CHZERO)
									f:sub()
									f:tee(ch)
									f:i32(26)
									f:ltu()
									f:brif(loop)

									f:load(ch)
									f:i32(string.byte('_') - string.byte('A'))
									f:eq()
									f:brif(loop)

									f:load(ch)
									f:i32(string.byte('a') - string.byte('A'))
									f:sub()
									f:i32(26)
									f:ltu()
									f:brif(loop)
								end)
							end)

							-- Check whether keyword
							f:switch(function()
								f:load(j)
								f:load(src)
								f:add()
								f:store(k)

								f:load(i)
								f:load(j)
								f:sub()
								f:tee(ch)
							end, 8, function(scopes)
								f:load(k)
								f:i64load(str.base)
								f:i64(string.unpack("<i8", "function"))
								f:ne()
								f:brif(scopes[0])

								f:i32(tokens._function)
								f:br(token1noinc)
							end, 6, function(scopes)
								assert(allocsizef(str.base + 6) == allocsizef(str.base + 8))
								f:load(k)
								f:i64load(str.base)
								f:i64(0xffffffffffff)
								f:band()

								f:tee(temp64)
								f:i64(string.unpack("<i6", "return"))
								f:eq()
								f:iff(i32, function()
									f:i32(tokens._return)
								end, function()
									f:load(temp64)
									f:i64(string.unpack("<i6", "elseif"))
									f:eq()
									f:iff(i32, function()
										f:i32(tokens._elseif)
									end, function()
										f:load(temp64)
										f:i64(string.unpack("<i6", "repeat"))
										f:ne()
										f:brif(scopes[0])

										f:i32(tokens._repeat)
									end)
								end)
								f:br(token1noinc)
							end, 5, function(scopes)
								assert(allocsizef(str.base + 5) == allocsizef(str.base + 8))
								f:load(k)
								f:i64load(str.base)
								f:i64(0xffffffffff)
								f:band()

								f:tee(temp64)
								f:i64(string.unpack("<i5", "local"))
								f:eq()
								f:iff(i32, function()
									f:i32(tokens._local)
								end, function()
									f:load(temp64)
									f:i64(string.unpack("<i5", "while"))
									f:eq()
									f:iff(i32, function()
										f:i32(tokens._while)
									end, function()
										f:load(temp64)
										f:i64(string.unpack("<i5", "false"))
										f:eq()
										f:iff(i32, function()
											f:i32(tokens._false)
										end, function()
											f:load(temp64)
											f:i64(string.unpack("<i5", "break"))
											f:eq()
											f:iff(i32, function()
												f:i32(tokens._break)
											end, function()
												f:load(temp64)
												f:i64(string.unpack("<i5", "until"))
												f:ne()
												f:brif(scopes[0])

												f:i32(tokens._until)
											end)
										end)
									end)
								end)
								f:br(token1noinc)
							end, 4, function(scopes)
								f:load(k)
								f:i32load(str.base)

								f:tee(k)
								f:i32(string.unpack("<i4", "then"))
								f:eq()
								f:iff(i32, function()
									f:i32(tokens._then)
								end, function()
									f:load(k)
									f:i32(string.unpack("<i4", "else"))
									f:eq()
									f:iff(i32, function()
										f:i32(tokens._else)
									end, function()
										f:load(k)
										f:i32(string.unpack("<i4", "true"))
										f:eq()
										f:iff(i32, function()
											f:i32(tokens._true)
										end, function()
											f:load(k)
											f:i32(string.unpack("<i4", "goto"))
											f:ne()
											f:brif(scopes[0])

											f:i32(tokens._goto)
										end)
									end)
								end)
								f:br(token1noinc)
							end, 3, function(scopes)
								assert(allocsizef(str.base + 3) ~= allocsizef(str.base + 8))
								f:load(k)
								f:i32load16u(str.base)
								f:load(k)
								f:i32load8u(str.base + 2)
								f:i32(16)
								f:shl()
								f:bor()

								f:tee(k)
								f:i32(string.unpack("<i3", "end"))
								f:eq()
								f:iff(i32, function()
									f:i32(tokens._end)
								end, function()
									f:load(k)
									f:i32(string.unpack("<i3", "and"))
									f:eq()
									f:iff(i32, function()
										f:i32(tokens._and)
									end, function()
										f:load(k)
										f:i32(string.unpack("<i3", "for"))
										f:eq()
										f:iff(i32, function()
											f:i32(tokens._for)
										end, function()
											f:load(k)
											f:i32(string.unpack("<i3", "nil"))
											f:eq()
											f:iff(i32, function()
												f:i32(tokens._nil)
											end, function()
												f:load(k)
												f:i32(string.unpack("<i3", "not"))
												f:ne()
												f:brif(scopes[0])

												f:i32(tokens._not)
											end)
										end)
									end)
								end)
								f:br(token1noinc)
							end, 2, function(scopes)
								f:load(k)
								f:i32load16u(str.base)

								f:tee(k)
								f:i32(string.unpack("<i2", "if"))
								f:eq()
								f:iff(i32, function()
									f:i32(tokens._if)
								end, function()
									f:load(k)
									f:i32(string.unpack("<i2", "or"))
									f:eq()
									f:iff(i32, function()
										f:i32(tokens._or)
									end, function()
										f:load(k)
										f:i32(string.unpack("<i2", "do"))
										f:eq()
										f:iff(i32, function()
											f:i32(tokens._do)
										end, function()
											f:load(k)
											f:i32(string.unpack("<i2", "in"))
											f:ne()
											f:brif(scopes[0])

											f:i32(tokens._in)
										end)
									end)
								end)
								f:br(token1noinc)
							end, 0, 1, 7, -1)

							f:load(ch)
							f:call(newstr)
							f:tee(src)
							f:i32(str.base)
							f:add()
							f:i32(srcslot)
							f:call(nthtmp)
							f:load(j)
							f:add()
							f:i32(str.base)
							f:add()
							f:load(ch)
							f:call(memcpy1rl)

							f:load(src)
							f:call(lxaddval)
							f:store(ch)

							f:i32(lexslot)
							f:call(nthtmp)
							f:i32(tokens._ident)
							f:call(pushstr)

							f:load(ch)
							f:call(pushstr)

							f:load(ch)
							f:i32(8)
							f:shru()
							f:call(pushstr)

							f:load(ch)
							f:i32(16)
							f:shru()
							f:call(pushstr)

							f:load(ch)
							f:i32(24)
							f:shru()
							f:call(pushstr)
							f:i32(lexslot)
							f:call(setnthtmp)

							f:br(loopnoinc)
						end, tcase('['), function()
							f:i32(lexslot)
							f:call(nthtmp)

							f:load(i)
							f:i32(1)
							f:add()
							f:tee(i)
							f:load(srclen)
							f:eq()
							f:iff(i32, function()
								f:i32(tokens._sl)
							end, function(lsvalif)
								-- TODO nested strings, omit opening character being newline
								f:i32(0)
								f:store(tlen)
								f:load(i)
								f:i32(1)
								f:sub()
								f:store(j)

								f:loop(i32, function(lscounteq)
									f:i32(tokens._sl)
									f:load(j)
									f:i32(1)
									f:add()
									f:tee(j)
									f:load(srclen)
									f:eq()
									f:brif(lsvalif)
									f:drop()

									f:load(j)
									f:load(src)
									f:add()
									f:i32load8u(str.base)
									f:tee(ch)
									f:i32(string.byte('='))
									f:eq()
									f:iff(i32, function()
										f:load(tlen)
										f:i32(1)
										f:add()
										f:store(tlen)
										f:br(lscounteq)
									end, function()
										f:i32(tokens._sl)
										f:load(ch)
										f:i32(string.byte('['))
										f:ne()
										f:brif(lsvalif)
										f:drop()

										f:loop(i32, function(lsseekeq)
											f:i32(tokens._sl)
											f:load(j)
											f:i32(1)
											f:add()
											f:tee(j)
											f:load(srclen)
											f:eq()
											f:brif(lsvalif)
											f:drop()

											f:load(j)
											f:load(src)
											f:add()
											f:i32load8u(str.base)
											f:i32(string.byte(']'))
											f:ne()
											f:brif(lsseekeq)

											f:load(j)
											f:store(ch)
											f:block(function(block)
											f:loop(function(loop)
												f:load(j)
												f:load(ch)
												f:sub()
												f:load(tlen)
												f:eq()
												f:brif(block)

												f:i32(tokens._sl)
												f:load(j)
												f:i32(1)
												f:add()
												f:tee(j)
												f:load(srclen)
												f:eq()
												f:brif(lsvalif)
												f:drop()

												f:load(j)
												f:load(src)
												f:add()
												f:i32load8u(str.base)
												f:i32(string.byte('='))
												f:ne()
												f:brif(lsseekeq)
												f:br(loop)
											end)
											end)

											f:i32(tokens._sl)
											f:load(j)
											f:i32(1)
											f:add()
											f:tee(j)
											f:load(srclen)
											f:eq()
											f:brif(lsvalif)
											f:drop()

											f:load(j)
											f:load(src)
											f:add()
											f:i32load8u(str.base)
											f:i32(string.byte(']'))
											f:ne()
											f:brif(lsseekeq)

											-- i, ch, tlen = j+1, i + 1 + tlen + 13, ch - i - tlen - 1
											f:load(ch)
											f:load(i)
											f:sub()
											f:load(tlen)
											f:sub()
											f:i32(1)
											f:sub()

											f:load(i)
											f:load(tlen)
											f:add()
											f:i32(14)
											f:add()

											f:load(j)
											f:i32(1)
											f:add()

											f:store(i)
											f:store(ch)
											f:tee(tlen)

											f:call(newstr)
											f:tee(src)
											f:i32(str.base)
											f:add()
											f:i32(srcslot)
											f:call(nthtmp)
											f:load(ch)
											f:add()
											f:load(tlen)
											f:call(memcpy1rl)

											f:load(src)
											f:call(lxaddval)
											f:store(ch)

											f:i32(lexslot)
											f:call(nthtmp)
											f:i32(tokens._string)
											f:call(pushstr)

											f:load(ch)
											f:call(pushstr)

											f:load(ch)
											f:i32(8)
											f:shru()
											f:call(pushstr)

											f:load(ch)
											f:i32(16)
											f:shru()
											f:call(pushstr)

											f:load(ch)
											f:i32(24)
											f:shru()
											f:call(pushstr)
											f:i32(lexslot)
											f:call(setnthtmp)

											f:load(j)
											f:i32(1)
											f:add()
											f:store(i)
											f:br(loopnoinc)
										end)
									end)
								end)
							end)
							f:call(pushstr)
							f:i32(lexslot)
							f:call(setnthtmp)

							f:br(loopnoinc)
						end, tcase(']'), function()
							f:i32(tokens._sr)
							f:br(token1)
						end, tcase('^'), function()
							f:i32(tokens._pow)
							f:br(token1)
						end, tcase('{'), function()
							f:i32(tokens._cl)
							f:br(token1)
						end, tcase('|'), function()
							f:i32(tokens._bor)
							f:br(token1)
						end, tcase('}'), function()
							f:i32(tokens._cr)
							f:br(token1)
						end, tcase('~'), function()
							f:load(i)
							f:i32(1)
							f:add()
							f:tee(i)
							f:load(srclen)
							f:ne()
							f:iff(function()
								f:load(i)
								f:load(src)
								f:add()
								f:i32load8u(str.base)
								f:i32(string.byte('='))
								f:eq()
								f:iff(function()
									f:i32(tokens._neq)
									f:br(token1)
								end)
							end)
							f:i32(tokens._bnot)
							f:br(token1noinc)
						end, tcase('!'), tcase('$'), tcase('?'), tcase('@'), tcase('`'), tcase('\\'), -1)
						f:unreachable()
					end) -- token1
					f:store(k)
					f:i32(lexslot)
					f:call(nthtmp)
					f:load(k)
					f:call(pushstr)
					f:i32(lexslot)
					f:call(setnthtmp)
				end) -- blloop
				f:load(i)
				f:i32(1)
				f:add()
				f:store(i)
				f:br(loopnoinc)
			end) -- token1noinc
			f:store(k)
			f:i32(lexslot)
			f:call(nthtmp)
			f:load(k)
			f:call(pushstr)
			f:i32(lexslot)
			f:call(setnthtmp)
			f:br(loopnoinc)
		end) -- loopnoinc
	end) -- loopwrap
	f:i32(lexslot)
	f:call(nthtmp)
	f:call(unbufstr)
	f:i32(constslot)
	f:call(nthtmp)
	f:call(unbufvec)
	f:call(tmppop)
	f:call(tmppop)
	f:call(tmppop)
	f:i32(4)
	f:call(setnthtmp)
	f:i32(8)
	f:call(setnthtmp)
end)

return {
	lex = lex,
}
