Name | Type | is_array | initial_value |
library libH uses libassert
// version 2018-03-29
globals
// shorthand for 's.len' in a reslice call
// local H_int s2 = s1.H(0, s.len)d
// local H_int s3 = s1.H(0, End)
//
integer End = 2147483647
endglobals
//
// H_int
//
globals
private hashtable ht_int = InitHashtable()
endglobals
/*private*/ struct H_int_Array
integer refs = 1
integer cap
static method create takes integer cap returns thistype
local thistype a = thistype.allocate()
set a.cap = cap
return a
endmethod
method destroy takes nothing returns nothing
static if DEBUG_MODE then
local string s = "[H_int_Array.destroy]: refs > 0; "
set s = s + " refs = " + I2S(refs)
call assert(s, refs > 0)
endif
set refs = refs - 1
// call writeln("refs = " + I2S(refs))
if refs == 0 then
// call writeln("destroying backing array: " + I2S(this))
call FlushChildHashtable(ht_int, integer(this))
call thistype.deallocate(this)
endif
endmethod
static method copy takes thistype dst, thistype src returns nothing
local integer i
local integer ii
static if DEBUG_MODE then
local string s = "[H_int_Array.copy]: dst.cap >= src.cap; "
set s = s + "dst.cap = " + I2S(dst.cap) + ", src.cap = " + I2S(src.cap)
call assert(s, dst.cap >= src.cap)
endif
set i = 0
set ii = src.cap
loop
exitwhen i >= ii
call SaveInteger(ht_int, integer(dst), i, LoadInteger(ht_int, integer(src), i))
set i = i + 1
endloop
endmethod
endstruct // H_int_Array
struct H_int
H_int_Array arr_ptr // ptr to backing array
integer offset
integer m_len
integer m_cap
method destroy takes nothing returns nothing
local H_int_Array a
if this != H_int(0) then
set a = arr_ptr
call H_int.deallocate(this)
call a.destroy()
endif
endmethod
static method new_len_cap takes integer len, integer cap returns H_int
local H_int s
static if DEBUG_MODE then
local string m = "[H_int.new_len_cap]: 1 <= cap; "
set m = m + "cap = " + I2S(cap)
call assert(m, 1 <= cap)
set m = "[H_int.new_len_cap]: 0 <= len and len <= cap; "
set m = m + "len = " + I2S(len) + ", cap = " + I2S(cap)
call assert(m, 0 <= len and len <= cap)
endif
set s = H_int.allocate()
set s.offset = 0
set s.m_len = len
set s.m_cap = cap
set s.arr_ptr = H_int_Array.create(cap)
return s
endmethod
static method new_cap takes integer cap returns H_int
return new_len_cap(0, cap)
endmethod
static method new_len takes integer len returns H_int
return new_len_cap(len, len)
endmethod
method operator len takes nothing returns integer
// we don't need to check if the instance is H_int(0)
// because arrays in Jass are 0 initialized
// and vjass rewrites m_len to H_int_m_len[this]
// if this == H_int(0) then
// return 0
// endif
return m_len
endmethod
method operator cap takes nothing returns integer
// if this == H_int(0) then
// return 0
// endif
return m_cap
endmethod
method H takes integer b, integer e returns H_int
local H_int s
local string m
if e == End then
set e = this.len
endif
static if DEBUG_MODE then
set m = "[H_int.H]: 0 <= b and b < len; "
set m = m + "b = " + I2S(b) + ", len = " + I2S(this.len)
call assert(m, 0 <= b and b < this.len)
set m = "[H_int.H]: b < e and e <= cap; "
set m = m + "b = " + I2S(b) + ", e = " + I2S(e) + ", cap = " + I2S(this.cap)
call assert(m, b < e and e <= this.cap)
endif
set s = H_int.allocate()
set s.arr_ptr = arr_ptr
set arr_ptr.refs = arr_ptr.refs + 1
set s.offset = offset + b
set s.m_len = e - b
set s.m_cap = s.arr_ptr.cap - s.offset
return s
endmethod
method operator[] takes integer index returns integer
static if DEBUG_MODE then
local string s = "[H_int.operator[]]: 0 <= index and index < this.len; "
set s = s + "index = " + I2S(index) + ", len = " + I2S(this.len)
call assert(s, 0 <= index and index < this.len)
endif
return LoadInteger(ht_int, integer(arr_ptr), offset + index)
endmethod
method operator[]= takes integer index, integer val returns nothing
static if DEBUG_MODE then
local string s = "[H_int.operator[]=]: 0 <= index and index < this.len; "
set s = s + "index = " + I2S(index) + ", len = " + I2S(this.len)
call assert(s, 0 <= index and index < this.len)
endif
call SaveInteger(ht_int, integer(arr_ptr), offset + index, val)
endmethod
method eq takes H_int rhs returns boolean
local integer i
local integer ii
if m_len != rhs.m_len then
return false
endif
set i = 0
set ii = m_len
loop
exitwhen i >= ii
if this[i] != rhs[i] then
return false
endif
set i = i + 1
endloop
return true
endmethod
method str takes nothing returns string
local string s = "["
local integer i
local integer ii
set ii = this.len
if ii > 0 then
set s = s + I2S(this[0])
set i = 1
loop
exitwhen i >= ii
set s = s + " " + I2S(this[i])
set i = i + 1
endloop
endif
set s = s + "]"
return s
endmethod
static method copy takes H_int dst, H_int src returns integer
local integer min_len
local integer i
set min_len = src.len
if min_len > dst.len then
set min_len = dst.len
endif
if (src.arr_ptr != dst.arr_ptr) or (dst.offset <= src.offset) then
set i = 0
loop
exitwhen i >= min_len
set dst[i] = src[i]
set i = i + 1
endloop
else
set i = min_len - 1
loop
exitwhen i < 0
set dst[i] = src[i]
set i = i - 1
endloop
endif
return min_len
endmethod
method ensure_can_push takes integer n returns nothing
local integer new_cap
local H_int_Array new_arr_ptr
if m_len + n > m_cap then
set new_cap = 2*m_cap
loop
exitwhen new_cap >= m_len + n
set new_cap = 2*new_cap
endloop
if new_cap > arr_ptr.cap then
set new_arr_ptr = H_int_Array.create(new_cap)
call H_int_Array.copy(new_arr_ptr, arr_ptr)
call arr_ptr.destroy()
set arr_ptr = new_arr_ptr
endif
set m_cap = new_cap
endif
endmethod
method push takes integer x returns H_int
if this == 0 then
set this = new_cap(1)
else
call ensure_can_push(1)
endif
call SaveInteger(ht_int, integer(arr_ptr), offset + m_len, x)
set m_len = m_len + 1
return this
endmethod
method push2 takes integer x0, integer x1 returns H_int
local integer i
if this == 0 then
set this = new_cap(2)
else
call ensure_can_push(2)
endif
set i = offset + m_len
call SaveInteger(ht_int, integer(arr_ptr), i, x0)
call SaveInteger(ht_int, integer(arr_ptr), i+1, x1)
set m_len = m_len + 2
return this
endmethod
method push3 takes integer x0, integer x1, integer x2 returns H_int
local integer i
if this == 0 then
set this = new_cap(3)
else
call ensure_can_push(3)
endif
set i = offset + m_len
call SaveInteger(ht_int, integer(arr_ptr), i, x0)
call SaveInteger(ht_int, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_int, integer(arr_ptr), i+2, x2)
set m_len = m_len + 3
return this
endmethod
method push4 takes integer x0, integer x1, integer x2, integer x3 returns H_int
local integer i
if this == 0 then
set this = new_cap(4)
else
call ensure_can_push(4)
endif
set i = offset + m_len
call SaveInteger(ht_int, integer(arr_ptr), i, x0)
call SaveInteger(ht_int, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_int, integer(arr_ptr), i+2, x2)
call SaveInteger(ht_int, integer(arr_ptr), i+3, x3)
set m_len = m_len + 4
return this
endmethod
method push5 takes integer x0, integer x1, integer x2, integer x3, integer x4 returns H_int
local integer i
if this == 0 then
set this = new_cap(5)
else
call ensure_can_push(5)
endif
set i = offset + m_len
call SaveInteger(ht_int, integer(arr_ptr), i, x0)
call SaveInteger(ht_int, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_int, integer(arr_ptr), i+2, x2)
call SaveInteger(ht_int, integer(arr_ptr), i+3, x3)
call SaveInteger(ht_int, integer(arr_ptr), i+4, x4)
set m_len = m_len + 5
return this
endmethod
method push6 takes integer x0, integer x1, integer x2, integer x3, integer x4, integer x5 returns H_int
local integer i
if this == 0 then
set this = new_cap(6)
else
call ensure_can_push(6)
endif
set i = offset + m_len
call SaveInteger(ht_int, integer(arr_ptr), i, x0)
call SaveInteger(ht_int, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_int, integer(arr_ptr), i+2, x2)
call SaveInteger(ht_int, integer(arr_ptr), i+3, x3)
call SaveInteger(ht_int, integer(arr_ptr), i+4, x4)
call SaveInteger(ht_int, integer(arr_ptr), i+5, x5)
set m_len = m_len + 6
return this
endmethod
method push7 takes integer x0, integer x1, integer x2, integer x3, integer x4, integer x5, integer x6 returns H_int
local integer i
if this == 0 then
set this = new_cap(7)
else
call ensure_can_push(7)
endif
set i = offset + m_len
call SaveInteger(ht_int, integer(arr_ptr), i, x0)
call SaveInteger(ht_int, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_int, integer(arr_ptr), i+2, x2)
call SaveInteger(ht_int, integer(arr_ptr), i+3, x3)
call SaveInteger(ht_int, integer(arr_ptr), i+4, x4)
call SaveInteger(ht_int, integer(arr_ptr), i+5, x5)
call SaveInteger(ht_int, integer(arr_ptr), i+6, x6)
set m_len = m_len + 7
return this
endmethod
method push8 takes integer x0, integer x1, integer x2, integer x3, integer x4, integer x5, integer x6, integer x7 returns H_int
local integer i
if this == 0 then
set this = new_cap(8)
else
call ensure_can_push(8)
endif
set i = offset + m_len
call SaveInteger(ht_int, integer(arr_ptr), i, x0)
call SaveInteger(ht_int, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_int, integer(arr_ptr), i+2, x2)
call SaveInteger(ht_int, integer(arr_ptr), i+3, x3)
call SaveInteger(ht_int, integer(arr_ptr), i+4, x4)
call SaveInteger(ht_int, integer(arr_ptr), i+5, x5)
call SaveInteger(ht_int, integer(arr_ptr), i+6, x6)
call SaveInteger(ht_int, integer(arr_ptr), i+7, x7)
set m_len = m_len + 8
return this
endmethod
method push9 takes integer x0, integer x1, integer x2, integer x3, integer x4, integer x5, integer x6, integer x7, integer x8 returns H_int
local integer i
if this == 0 then
set this = new_cap(9)
else
call ensure_can_push(9)
endif
set i = offset + m_len
call SaveInteger(ht_int, integer(arr_ptr), i, x0)
call SaveInteger(ht_int, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_int, integer(arr_ptr), i+2, x2)
call SaveInteger(ht_int, integer(arr_ptr), i+3, x3)
call SaveInteger(ht_int, integer(arr_ptr), i+4, x4)
call SaveInteger(ht_int, integer(arr_ptr), i+5, x5)
call SaveInteger(ht_int, integer(arr_ptr), i+6, x6)
call SaveInteger(ht_int, integer(arr_ptr), i+7, x7)
call SaveInteger(ht_int, integer(arr_ptr), i+8, x8)
set m_len = m_len + 9
return this
endmethod
method push10 takes integer x0, integer x1, integer x2, integer x3, integer x4, integer x5, integer x6, integer x7, integer x8, integer x9 returns H_int
local integer i
if this == 0 then
set this = new_cap(10)
else
call ensure_can_push(10)
endif
set i = offset + m_len
call SaveInteger(ht_int, integer(arr_ptr), i, x0)
call SaveInteger(ht_int, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_int, integer(arr_ptr), i+2, x2)
call SaveInteger(ht_int, integer(arr_ptr), i+3, x3)
call SaveInteger(ht_int, integer(arr_ptr), i+4, x4)
call SaveInteger(ht_int, integer(arr_ptr), i+5, x5)
call SaveInteger(ht_int, integer(arr_ptr), i+6, x6)
call SaveInteger(ht_int, integer(arr_ptr), i+7, x7)
call SaveInteger(ht_int, integer(arr_ptr), i+8, x8)
call SaveInteger(ht_int, integer(arr_ptr), i+9, x9)
set m_len = m_len + 10
return this
endmethod
method clear takes nothing returns nothing
set m_len = 0
endmethod
endstruct // H_int
public function slice_int_test takes nothing returns nothing
local H_int s
local H_int a
local H_int b
local H_int c
local H_int src
local H_int dst
if true then
set s = H_int(0)
call assert("[slice_int_test] 1", 0 == s.len)
call assert("[slice_int_test] 2", 0 == s.cap)
set s = s.push(10)
call assert("[slice_int_test] 3", 1 == s.arr_ptr)
call assert("[slice_int_test] 4", 1 == s.len)
call assert("[slice_int_test] 5", 1 == s.cap)
call assert("[slice_int_test] 6", 10 == s[0])
set s = s.push8(20, 30, 40, 50, 60, 70, 80, 90)
// set s = s.push(20)
// set s = s.push(30)
// set s = s.push(40)
// set s = s.push(50)
// set s = s.push(60)
// set s = s.push(70)
// set s = s.push(80)
// set s = s.push(90)
call assert("[slice_int_test] 7", 9 == s.len)
call assert("[slice_int_test] 8", 16 == s.cap)
call assert("[slice_int_test] 9", 90 == s[8])
call s.destroy()
endif
if false then
set s = H_int(0).push5(0, 0, 0, 0, 0)
call assert("[slice_int_test] 10", 5 == s.len)
call assert("[slice_int_test] 11", 5 == s.cap)
set a = s.H(1, 4)
set b = s.H(2, 4)
call writeln("s.len: " + I2S(s.len) + ", s.cap: " + I2S(s.cap) + ", s: " + s.str())
call writeln("a.len: " + I2S(a.len) + ", a.cap: " + I2S(a.cap) + ", a: " + a.str())
call writeln("b.len: " + I2S(b.len) + ", b.cap: " + I2S(b.cap) + ", b: " + b.str())
set a = a.push(1)
set b = b.push(10) // b stomps over a's last pushed value
call writeln("s.len: " + I2S(s.len) + ", s.cap: " + I2S(s.cap) + ", s: " + s.str())
call writeln("a.len: " + I2S(a.len) + ", a.cap: " + I2S(a.cap) + ", a: " + a.str())
call writeln("b.len: " + I2S(b.len) + ", b.cap: " + I2S(b.cap) + ", b: " + b.str())
call s.destroy()
call a.destroy()
call b.destroy()
endif
if false then
set s = H_int(0).push10(0, 0 ,0, 0, 0, 0, 0, 0, 0, 0)
set a = s.H(0, 3)
set b = s.H(3, 6)
set c = s.H(6, 10)
set a[0] = 0
set a[1] = 1
set a[2] = 2
set b[0] = 3
set b[1] = 4
set b[2] = 5
set c[0] = 6
set c[1] = 7
set c[2] = 8
set c[3] = 9
call assert("[slice_int_test] 12", s.eq(H_int(0).push10(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)))
call writeln("s.len: " + I2S(s.len) + ", s.cap: " + I2S(s.cap) + ", s: " + s.str())
call writeln("a.len: " + I2S(a.len) + ", a.cap: " + I2S(a.cap) + ", a: " + a.str())
call writeln("b.len: " + I2S(b.len) + ", b.cap: " + I2S(b.cap) + ", b: " + b.str())
call writeln("c.len: " + I2S(c.len) + ", c.cap: " + I2S(c.cap) + ", c: " + c.str())
call s.destroy()
call a.destroy()
call b.destroy()
call c.destroy()
endif
if true then
set s = H_int(0).push10(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
set a = H_int(0).push5(1, 1, 1, 1, 1)
set b = H_int(0).push5(2, 2, 2, 2, 2)
call H_int.copy(s.H(0, 5), a)
call H_int.copy(s.H(5, 10), b)
call assert("[slice_int_test] 13", s.eq(H_int(0).push10(1, 1, 1, 1, 1, 2, 2, 2, 2, 2)))
set s = H_int(0).push4(0, 1, 2, 3)
set src = s.H(1, 4)
set dst = s.H(0, 3)
call H_int.copy(dst, src)
call assert("[slice_int_test] 14", s.eq(H_int(0).push4(1, 2, 3, 3)))
set s = H_int(0).push4(0, 1, 2, 3)
set src = s.H(0, 3)
set dst = s.H(1, 4)
call H_int.copy(dst, src)
call assert("[slice_int_test] 15", s.eq(H_int(0).push4(0, 0, 1, 2)))
endif
endfunction
//
// H_string
//
globals
private hashtable ht_string = InitHashtable()
endglobals
/*private*/ struct H_string_Array
integer refs = 1
integer cap
static method create takes integer cap returns thistype
local thistype a = thistype.allocate()
set a.cap = cap
return a
endmethod
method destroy takes nothing returns nothing
static if DEBUG_MODE then
local string s = "[H_string_Array.destroy]: refs > 0; "
set s = s + " refs = " + I2S(refs)
call assert(s, refs > 0)
endif
set refs = refs - 1
if refs == 0 then
call FlushChildHashtable(ht_string, integer(this))
call thistype.deallocate(this)
endif
endmethod
static method copy takes thistype dst, thistype src returns nothing
local integer i
local integer ii
static if DEBUG_MODE then
local string s = "[H_string_Array.copy]: dst.cap >= src.cap; "
set s = s + "dst.cap = " + I2S(dst.cap) + ", src.cap = " + I2S(src.cap)
call assert(s, dst.cap >= src.cap)
endif
set i = 0
set ii = src.cap
loop
exitwhen i >= ii
call SaveStr(ht_string, integer(dst), i, LoadStr(ht_string, integer(src), i))
set i = i + 1
endloop
endmethod
endstruct // H_string_Array
struct H_string
H_string_Array arr_ptr
integer offset
integer m_len
integer m_cap
method destroy takes nothing returns nothing
local H_string_Array a
if this != H_string(0) then
set a = arr_ptr
call H_string.deallocate(this)
call a.destroy()
endif
endmethod
static method new_len_cap takes integer len, integer cap returns H_string
local H_string s
static if DEBUG_MODE then
local string m = "[H_string.new_len_cap]: 1 <= cap; "
set m = m + "cap = " + I2S(cap)
call assert(m, 1 <= cap)
set m = "[H_string.new_len_cap]: 0 <= len and len <= cap; "
set m = m + "len = " + I2S(len) + ", cap = " + I2S(cap)
call assert(m, 0 <= len and len <= cap)
endif
set s = H_string.allocate()
set s.offset = 0
set s.m_len = len
set s.m_cap = cap
set s.arr_ptr = H_string_Array.create(cap)
return s
endmethod
static method new_cap takes integer cap returns H_string
return new_len_cap(0, cap)
endmethod
static method new_len takes integer len returns H_string
return new_len_cap(len, len)
endmethod
method operator len takes nothing returns integer
return m_len
endmethod
method operator cap takes nothing returns integer
return m_cap
endmethod
method H takes integer b, integer e returns H_string
local H_string s
local string m
if e == End then
set e = this.len
endif
static if DEBUG_MODE then
set m = "[H_string.H]: 0 <= b and b < len; "
set m = m + "b = " + I2S(b) + ", len = " + I2S(this.len)
call assert(m, 0 <= b and b < this.len)
set m = "[H_string.H]: b < e and e <= cap; "
set m = m + "b = " + I2S(b) + ", e = " + I2S(e) + ", cap = " + I2S(this.cap)
call assert(m, b < e and e <= this.cap)
endif
set s = H_string.allocate()
set s.arr_ptr = arr_ptr
set arr_ptr.refs = arr_ptr.refs + 1
set s.offset = offset + b
set s.m_len = e - b
set s.m_cap = s.arr_ptr.cap - s.offset
return s
endmethod
method operator[] takes integer index returns string
static if DEBUG_MODE then
local string s = "[H_string.operator[]]: 0 <= index and index < this.len; "
set s = s + "index = " + I2S(index) + ", len = " + I2S(this.len)
call assert(s, 0 <= index and index < this.len)
endif
return LoadStr(ht_string, integer(arr_ptr), offset + index)
endmethod
method operator[]= takes integer index, string val returns nothing
static if DEBUG_MODE then
local string s = "[H_string.operator[]=]: 0 <= index and index < this.len; "
set s = s + "index = " + I2S(index) + ", len = " + I2S(this.len)
call assert(s, 0 <= index and index < this.len)
endif
call SaveStr(ht_string, integer(arr_ptr), offset + index, val)
endmethod
method str takes nothing returns string
local string s = "["
local integer i
local integer ii
set ii = this.len
if ii > 0 then
set s = s + this[0]
set i = 1
loop
exitwhen i >= ii
set s = s + " " + this[i]
set i = i + 1
endloop
endif
set s = s + "]"
return s
endmethod
static method copy takes H_string dst, H_string src returns integer
local integer min_len
local integer i
set min_len = src.len
if min_len > dst.len then
set min_len = dst.len
endif
if (src.arr_ptr != dst.arr_ptr) or (dst.offset <= src.offset) then
set i = 0
loop
exitwhen i >= min_len
set dst[i] = src[i]
set i = i + 1
endloop
else
set i = min_len - 1
loop
exitwhen i < 0
set dst[i] = src[i]
set i = i - 1
endloop
endif
return min_len
endmethod
method ensure_can_push takes integer n returns nothing
local integer new_cap
local H_string_Array new_arr_ptr
if m_len + n > m_cap then
set new_cap = 2*m_cap
loop
exitwhen new_cap >= m_len + n
set new_cap = 2*new_cap
endloop
if new_cap > arr_ptr.cap then
set new_arr_ptr = H_string_Array.create(new_cap)
call H_string_Array.copy(new_arr_ptr, arr_ptr)
call arr_ptr.destroy()
set arr_ptr = new_arr_ptr
endif
set m_cap = new_cap
endif
endmethod
method push takes string x returns H_string
if this == 0 then
set this = new_cap(1)
else
call ensure_can_push(1)
endif
call SaveStr(ht_string, integer(arr_ptr), offset + m_len, x)
set m_len = m_len + 1
return this
endmethod
method push2 takes string x0, string x1 returns H_string
local integer i
if this == 0 then
set this = new_cap(2)
else
call ensure_can_push(2)
endif
set i = offset + m_len
call SaveStr(ht_string, integer(arr_ptr), i, x0)
call SaveStr(ht_string, integer(arr_ptr), i+1, x1)
set m_len = m_len + 2
return this
endmethod
method push3 takes string x0, string x1, string x2 returns H_string
local integer i
if this == 0 then
set this = new_cap(3)
else
call ensure_can_push(3)
endif
set i = offset + m_len
call SaveStr(ht_string, integer(arr_ptr), i, x0)
call SaveStr(ht_string, integer(arr_ptr), i+1, x1)
call SaveStr(ht_string, integer(arr_ptr), i+2, x2)
set m_len = m_len + 3
return this
endmethod
method push4 takes string x0, string x1, string x2, string x3 returns H_string
local integer i
if this == 0 then
set this = new_cap(4)
else
call ensure_can_push(4)
endif
set i = offset + m_len
call SaveStr(ht_string, integer(arr_ptr), i, x0)
call SaveStr(ht_string, integer(arr_ptr), i+1, x1)
call SaveStr(ht_string, integer(arr_ptr), i+2, x2)
call SaveStr(ht_string, integer(arr_ptr), i+3, x3)
set m_len = m_len + 4
return this
endmethod
method push5 takes string x0, string x1, string x2, string x3, string x4 returns H_string
local integer i
if this == 0 then
set this = new_cap(5)
else
call ensure_can_push(5)
endif
set i = offset + m_len
call SaveStr(ht_string, integer(arr_ptr), i, x0)
call SaveStr(ht_string, integer(arr_ptr), i+1, x1)
call SaveStr(ht_string, integer(arr_ptr), i+2, x2)
call SaveStr(ht_string, integer(arr_ptr), i+3, x3)
call SaveStr(ht_string, integer(arr_ptr), i+4, x4)
set m_len = m_len + 5
return this
endmethod
method push6 takes string x0, string x1, string x2, string x3, string x4, string x5 returns H_string
local integer i
if this == 0 then
set this = new_cap(6)
else
call ensure_can_push(6)
endif
set i = offset + m_len
call SaveStr(ht_string, integer(arr_ptr), i, x0)
call SaveStr(ht_string, integer(arr_ptr), i+1, x1)
call SaveStr(ht_string, integer(arr_ptr), i+2, x2)
call SaveStr(ht_string, integer(arr_ptr), i+3, x3)
call SaveStr(ht_string, integer(arr_ptr), i+4, x4)
call SaveStr(ht_string, integer(arr_ptr), i+5, x5)
set m_len = m_len + 6
return this
endmethod
method push7 takes string x0, string x1, string x2, string x3, string x4, string x5, string x6 returns H_string
local integer i
if this == 0 then
set this = new_cap(7)
else
call ensure_can_push(7)
endif
set i = offset + m_len
call SaveStr(ht_string, integer(arr_ptr), i, x0)
call SaveStr(ht_string, integer(arr_ptr), i+1, x1)
call SaveStr(ht_string, integer(arr_ptr), i+2, x2)
call SaveStr(ht_string, integer(arr_ptr), i+3, x3)
call SaveStr(ht_string, integer(arr_ptr), i+4, x4)
call SaveStr(ht_string, integer(arr_ptr), i+5, x5)
call SaveStr(ht_string, integer(arr_ptr), i+6, x6)
set m_len = m_len + 7
return this
endmethod
method push8 takes string x0, string x1, string x2, string x3, string x4, string x5, string x6, string x7 returns H_string
local integer i
if this == 0 then
set this = new_cap(8)
else
call ensure_can_push(8)
endif
set i = offset + m_len
call SaveStr(ht_string, integer(arr_ptr), i, x0)
call SaveStr(ht_string, integer(arr_ptr), i+1, x1)
call SaveStr(ht_string, integer(arr_ptr), i+2, x2)
call SaveStr(ht_string, integer(arr_ptr), i+3, x3)
call SaveStr(ht_string, integer(arr_ptr), i+4, x4)
call SaveStr(ht_string, integer(arr_ptr), i+5, x5)
call SaveStr(ht_string, integer(arr_ptr), i+6, x6)
call SaveStr(ht_string, integer(arr_ptr), i+7, x7)
set m_len = m_len + 8
return this
endmethod
method push9 takes string x0, string x1, string x2, string x3, string x4, string x5, string x6, string x7, string x8 returns H_string
local integer i
if this == 0 then
set this = new_cap(9)
else
call ensure_can_push(9)
endif
set i = offset + m_len
call SaveStr(ht_string, integer(arr_ptr), i, x0)
call SaveStr(ht_string, integer(arr_ptr), i+1, x1)
call SaveStr(ht_string, integer(arr_ptr), i+2, x2)
call SaveStr(ht_string, integer(arr_ptr), i+3, x3)
call SaveStr(ht_string, integer(arr_ptr), i+4, x4)
call SaveStr(ht_string, integer(arr_ptr), i+5, x5)
call SaveStr(ht_string, integer(arr_ptr), i+6, x6)
call SaveStr(ht_string, integer(arr_ptr), i+7, x7)
call SaveStr(ht_string, integer(arr_ptr), i+8, x8)
set m_len = m_len + 9
return this
endmethod
method push10 takes string x0, string x1, string x2, string x3, string x4, string x5, string x6, string x7, string x8, string x9 returns H_string
local integer i
if this == 0 then
set this = new_cap(10)
else
call ensure_can_push(10)
endif
set i = offset + m_len
call SaveStr(ht_string, integer(arr_ptr), i, x0)
call SaveStr(ht_string, integer(arr_ptr), i+1, x1)
call SaveStr(ht_string, integer(arr_ptr), i+2, x2)
call SaveStr(ht_string, integer(arr_ptr), i+3, x3)
call SaveStr(ht_string, integer(arr_ptr), i+4, x4)
call SaveStr(ht_string, integer(arr_ptr), i+5, x5)
call SaveStr(ht_string, integer(arr_ptr), i+6, x6)
call SaveStr(ht_string, integer(arr_ptr), i+7, x7)
call SaveStr(ht_string, integer(arr_ptr), i+8, x8)
call SaveStr(ht_string, integer(arr_ptr), i+9, x9)
set m_len = m_len + 10
return this
endmethod
method clear takes nothing returns nothing
set m_len = 0
endmethod
endstruct // H_string
function H_string_join takes string delim, H_string s returns string
local string r = ""
local integer i
local integer ii = s.len
if ii > 0 then
set r = s[0]
set i = 1
loop
exitwhen i >= ii
set r = r + delim + s[i]
set i = i + 1
endloop
endif
return r
endfunction
public function index_of_char takes string c, string s, integer offset returns integer
local integer i = offset
local integer ii = StringLength(s)
loop
exitwhen i >= ii
if c == SubString(s, i, i + 1) then
return i
endif
set i = i + 1
endloop
return -1
endfunction
public function index_of_string takes string sub, string s, integer offset returns integer
local integer sub_len = StringLength(sub)
local integer s_len = StringLength(s)
local integer ii = s_len - sub_len
local string sub_first_c
local integer i
local integer j
local integer sub_i
local integer s_i
local boolean match
call assert("[index_of_string]: sub != \"\"", sub != "")
set sub_first_c = SubString(sub, 0, 1)
set i = offset
loop
exitwhen i > ii
set j = index_of_char(sub_first_c, s, i)
if j == -1 then
return -1
else
set sub_i = 0
set s_i = j
if sub == SubString(s, j, j + sub_len) then
return j
else
set i = j + 1
endif
endif
endloop
return -1
endfunction
public function str_split takes string delim, string s returns H_string
local H_string r = H_string(0)
local integer delim_len = StringLength(delim)
local integer s_len = StringLength(s)
local integer i = 0
local integer j
if delim_len == 0 then
loop
exitwhen i >= s_len
set r = r.push(SubString(s, i, i + 1))
set i = i + 1
endloop
else
loop
set j = index_of_string(delim, s, i)
if j == -1 then
set r = r.push(SubString(s, i, s_len))
exitwhen true
else
set r = r.push(SubString(s, i, j))
endif
set i = j + delim_len
endloop
endif
return r
endfunction
public function slice_string_test takes nothing returns nothing
local H_string ss = H_string(0).push4("A", "B", "C", "D")
call assert("[slice_string_test] 1", 4 == ss.len)
call assert("[slice_string_test] 2", 4 == ss.cap)
call assert("[slice_string_test] 3", "[A B C D]" == ss.str())
call assert("[slice_string_test] 4", "A,B,C,D" == H_string_join(",", ss))
set ss = str_split(" ", "A B C")
call assert("[slice_string_test] 5", "ABC" == H_string_join("", ss))
call assert("[slice_string_test] 6", "A,B,C" == H_string_join(",", ss))
call assert("[slice_string_test] 7", "A B C" == H_string_join(" ", ss))
call assert("[slice_string_test] 8", "A:B C" != H_string_join(":", ss))
set ss = str_split("[]", "A[]B[]C")
call assert("[slice_string_test] 9", "ABC" == H_string_join("", ss))
call assert("[slice_string_test] 10", "A,B,C" == H_string_join(",", ss))
set ss = str_split("@", "ABC")
call assert("[slice_string_test] 11", ss.len == 1 and ss[0] == "ABC")
set ss = str_split("", "ABC")
call assert("[slice_string_test] 12", "ABC" == H_string_join("", ss))
endfunction
//
// H_unit
//
globals
private hashtable ht_unit = InitHashtable()
endglobals
/*private*/ struct H_unit_Array
integer refs = 1
integer cap
static method create takes integer cap returns thistype
local thistype a = thistype.allocate()
set a.cap = cap
return a
endmethod
method destroy takes nothing returns nothing
static if DEBUG_MODE then
local string s = "[H_unit_Array.destroy]: refs > 0; "
set s = s + " refs = " + I2S(refs)
call assert(s, refs > 0)
endif
set refs = refs - 1
if refs == 0 then
call FlushChildHashtable(ht_unit, integer(this))
call thistype.deallocate(this)
endif
endmethod
static method copy takes thistype dst, thistype src returns nothing
local integer i
local integer ii
static if DEBUG_MODE then
local string s = "[H_unit_Array.copy]: dst.cap >= src.cap; "
set s = s + "dst.cap = " + I2S(dst.cap) + ", src.cap = " + I2S(src.cap)
call assert(s, dst.cap >= src.cap)
endif
set i = 0
set ii = src.cap
loop
exitwhen i >= ii
call SaveUnitHandle(ht_unit, integer(dst), i, LoadUnitHandle(ht_unit, integer(src), i))
set i = i + 1
endloop
endmethod
endstruct // H_unit_Array
struct H_unit
H_unit_Array arr_ptr
integer offset
integer m_len
integer m_cap
method destroy takes nothing returns nothing
local H_unit_Array a
if this != H_unit(0) then
set a = arr_ptr
call H_unit.deallocate(this)
call a.destroy()
endif
endmethod
static method new_len_cap takes integer len, integer cap returns H_unit
local H_unit s
static if DEBUG_MODE then
local string m = "[H_unit.new_len_cap]: 1 <= cap; "
set m = m + "cap = " + I2S(cap)
call assert(m, 1 <= cap)
set m = "[H_unit.new_len_cap]: 0 <= len and len <= cap; "
set m = m + "len = " + I2S(len) + ", cap = " + I2S(cap)
call assert(m, 0 <= len and len <= cap)
endif
set s = H_unit.allocate()
set s.offset = 0
set s.m_len = len
set s.m_cap = cap
set s.arr_ptr = H_unit_Array.create(cap)
return s
endmethod
static method new_cap takes integer cap returns H_unit
return new_len_cap(0, cap)
endmethod
static method new_len takes integer len returns H_unit
return new_len_cap(len, len)
endmethod
method operator len takes nothing returns integer
return m_len
endmethod
method operator cap takes nothing returns integer
return m_cap
endmethod
method H takes integer b, integer e returns H_unit
local H_unit s
local string m
if e == End then
set e = this.len
endif
static if DEBUG_MODE then
set m = "[H_unit.H]: 0 <= b and b < len; "
set m = m + "b = " + I2S(b) + ", len = " + I2S(this.len)
call assert(m, 0 <= b and b < this.len)
set m = "[H_unit.H]: b < e and e <= cap; "
set m = m + "b = " + I2S(b) + ", e = " + I2S(e) + ", cap = " + I2S(this.cap)
call assert(m, b < e and e <= this.cap)
endif
set s = H_unit.allocate()
set s.arr_ptr = arr_ptr
set arr_ptr.refs = arr_ptr.refs + 1
set s.offset = offset + b
set s.m_len = e - b
set s.m_cap = s.arr_ptr.cap - s.offset
return s
endmethod
method operator[] takes integer index returns unit
static if DEBUG_MODE then
local string s = "[H_unit.operator[]]: 0 <= index and index < this.len; "
set s = s + "index = " + I2S(index) + ", len = " + I2S(this.len)
call assert(s, 0 <= index and index < this.len)
endif
return LoadUnitHandle(ht_unit, integer(arr_ptr), offset + index)
endmethod
method operator[]= takes integer index, unit val returns nothing
static if DEBUG_MODE then
local string s = "[H_unit.operator[]=]: 0 <= index and index < this.len; "
set s = s + "index = " + I2S(index) + ", len = " + I2S(this.len)
call assert(s, 0 <= index and index < this.len)
endif
call SaveUnitHandle(ht_unit, integer(arr_ptr), offset + index, val)
endmethod
method str takes nothing returns string
local string s = "["
local integer i
local integer ii
set ii = this.len
if ii > 0 then
set s = s + GetUnitName(this[0]) + "(" + I2S(GetHandleId(this[0])) + ")"
set i = 1
loop
exitwhen i >= ii
set s = s + " " + GetUnitName(this[i]) + "(" + I2S(GetHandleId(this[i])) + ")"
set i = i + 1
endloop
endif
set s = s + "]"
return s
endmethod
static method copy takes H_unit dst, H_unit src returns integer
local integer min_len
local integer i
set min_len = src.len
if min_len > dst.len then
set min_len = dst.len
endif
if (src.arr_ptr != dst.arr_ptr) or (dst.offset <= src.offset) then
set i = 0
loop
exitwhen i >= min_len
set dst[i] = src[i]
set i = i + 1
endloop
else
set i = min_len - 1
loop
exitwhen i < 0
set dst[i] = src[i]
set i = i - 1
endloop
endif
return min_len
endmethod
method ensure_can_push takes integer n returns nothing
local integer new_cap
local H_unit_Array new_arr_ptr
if m_len + n > m_cap then
set new_cap = 2*m_cap
loop
exitwhen new_cap >= m_len + n
set new_cap = 2*new_cap
endloop
if new_cap > arr_ptr.cap then
set new_arr_ptr = H_unit_Array.create(new_cap)
call H_unit_Array.copy(new_arr_ptr, arr_ptr)
call arr_ptr.destroy()
set arr_ptr = new_arr_ptr
endif
set m_cap = new_cap
endif
endmethod
method push takes unit x returns H_unit
if this == 0 then
set this = new_cap(1)
else
call ensure_can_push(1)
endif
call SaveUnitHandle(ht_unit, integer(arr_ptr), offset + m_len, x)
set m_len = m_len + 1
return this
endmethod
method clear takes nothing returns nothing
set m_len = 0
endmethod
endstruct // H_unit
//
// H_image
//
globals
private hashtable ht_image = InitHashtable()
endglobals
/*private*/ struct H_image_Array
integer refs = 1
integer cap
static method create takes integer cap returns thistype
local thistype a = thistype.allocate()
set a.cap = cap
return a
endmethod
method destroy takes nothing returns nothing
static if DEBUG_MODE then
local string s = "[H_image_Array.destroy]: refs > 0; "
set s = s + " refs = " + I2S(refs)
call assert(s, refs > 0)
endif
set refs = refs - 1
if refs == 0 then
call FlushChildHashtable(ht_image, integer(this))
call thistype.deallocate(this)
endif
endmethod
static method copy takes thistype dst, thistype src returns nothing
local integer i
local integer ii
static if DEBUG_MODE then
local string s = "[H_image_Array.copy]: dst.cap >= src.cap; "
set s = s + "dst.cap = " + I2S(dst.cap) + ", src.cap = " + I2S(src.cap)
call assert(s, dst.cap >= src.cap)
endif
set i = 0
set ii = src.cap
loop
exitwhen i >= ii
call SaveImageHandle(ht_image, integer(dst), i, LoadImageHandle(ht_image, integer(src), i))
set i = i + 1
endloop
endmethod
endstruct // H_image_Array
struct H_image
H_image_Array arr_ptr
integer offset
integer m_len
integer m_cap
method destroy takes nothing returns nothing
local H_image_Array a
if this != H_image(0) then
set a = arr_ptr
call H_image.deallocate(this)
call a.destroy()
endif
endmethod
static method new_len_cap takes integer len, integer cap returns H_image
local H_image s
static if DEBUG_MODE then
local string m = "[H_image.new_len_cap]: 1 <= cap; "
set m = m + "cap = " + I2S(cap)
call assert(m, 1 <= cap)
set m = "[H_image.new_len_cap]: 0 <= len and len <= cap; "
set m = m + "len = " + I2S(len) + ", cap = " + I2S(cap)
call assert(m, 0 <= len and len <= cap)
endif
set s = H_image.allocate()
set s.offset = 0
set s.m_len = len
set s.m_cap = cap
set s.arr_ptr = H_image_Array.create(cap)
return s
endmethod
static method new_cap takes integer cap returns H_image
return new_len_cap(0, cap)
endmethod
static method new_len takes integer len returns H_image
return new_len_cap(len, len)
endmethod
method operator len takes nothing returns integer
return m_len
endmethod
method operator cap takes nothing returns integer
return m_cap
endmethod
method H takes integer b, integer e returns H_image
local H_image s
local string m
if e == End then
set e = this.len
endif
static if DEBUG_MODE then
set m = "[H_image.H]: 0 <= b and b < len; "
set m = m + "b = " + I2S(b) + ", len = " + I2S(this.len)
call assert(m, 0 <= b and b < this.len)
set m = "[H_image.H]: b < e and e <= cap; "
set m = m + "b = " + I2S(b) + ", e = " + I2S(e) + ", cap = " + I2S(this.cap)
call assert(m, b < e and e <= this.cap)
endif
set s = H_image.allocate()
set s.arr_ptr = arr_ptr
set arr_ptr.refs = arr_ptr.refs + 1
set s.offset = offset + b
set s.m_len = e - b
set s.m_cap = s.arr_ptr.cap - s.offset
return s
endmethod
method operator[] takes integer index returns image
static if DEBUG_MODE then
local string s = "[H_image.operator[]]: 0 <= index and index < this.len; "
set s = s + "index = " + I2S(index) + ", len = " + I2S(this.len)
call assert(s, 0 <= index and index < this.len)
endif
return LoadImageHandle(ht_image, integer(arr_ptr), offset + index)
endmethod
method operator[]= takes integer index, image val returns nothing
static if DEBUG_MODE then
local string s = "[H_image.operator[]=]: 0 <= index and index < this.len; "
set s = s + "index = " + I2S(index) + ", len = " + I2S(this.len)
call assert(s, 0 <= index and index < this.len)
endif
call SaveImageHandle(ht_image, integer(arr_ptr), offset + index, val)
endmethod
method str takes nothing returns string
local string s = "["
local integer i
local integer ii
set ii = this.len
if ii > 0 then
set s = s + I2S(GetHandleId(this[0]))
set i = 1
loop
exitwhen i >= ii
set s = s + " " + I2S(GetHandleId(this[i]))
set i = i + 1
endloop
endif
set s = s + "]"
return s
endmethod
static method copy takes H_image dst, H_image src returns integer
local integer min_len
local integer i
set min_len = src.len
if min_len > dst.len then
set min_len = dst.len
endif
if (src.arr_ptr != dst.arr_ptr) or (dst.offset <= src.offset) then
set i = 0
loop
exitwhen i >= min_len
set dst[i] = src[i]
set i = i + 1
endloop
else
set i = min_len - 1
loop
exitwhen i < 0
set dst[i] = src[i]
set i = i - 1
endloop
endif
return min_len
endmethod
method ensure_can_push takes integer n returns nothing
local integer new_cap
local H_image_Array new_arr_ptr
if m_len + n > m_cap then
set new_cap = 2*m_cap
loop
exitwhen new_cap >= m_len + n
set new_cap = 2*new_cap
endloop
if new_cap > arr_ptr.cap then
set new_arr_ptr = H_image_Array.create(new_cap)
call H_image_Array.copy(new_arr_ptr, arr_ptr)
call arr_ptr.destroy()
set arr_ptr = new_arr_ptr
endif
set m_cap = new_cap
endif
endmethod
method push takes image x returns H_image
if this == 0 then
set this = new_cap(1)
else
call ensure_can_push(1)
endif
call SaveImageHandle(ht_image, integer(arr_ptr), offset + m_len, x)
set m_len = m_len + 1
return this
endmethod
method clear takes nothing returns nothing
set m_len = 0
endmethod
endstruct // H_image
//
// H_u8
//
globals
private hashtable ht_u8 = InitHashtable()
endglobals
/*private*/ struct H_u8_Array
integer refs = 1
integer cap
static method create takes integer cap returns thistype
local thistype a = thistype.allocate()
set a.cap = cap
return a
endmethod
method destroy takes nothing returns nothing
static if DEBUG_MODE then
local string s = "[H_u8_Array.destroy]: refs > 0; "
set s = s + " refs = " + I2S(refs)
call assert(s, refs > 0)
endif
set refs = refs - 1
if refs == 0 then
call FlushChildHashtable(ht_u8, integer(this))
call thistype.deallocate(this)
endif
endmethod
static method copy takes thistype dst, thistype src returns nothing
local integer i
local integer ii
static if DEBUG_MODE then
local string s = "[H_u8_Array.copy]: dst.cap >= src.cap; "
set s = s + "dst.cap = " + I2S(dst.cap) + ", src.cap = " + I2S(src.cap)
call assert(s, dst.cap >= src.cap)
endif
set i = 0
set ii = src.cap
loop
exitwhen i >= ii
call SaveInteger(ht_u8, integer(dst), i, LoadInteger(ht_u8, integer(src), i))
set i = i + 1
endloop
endmethod
endstruct // H_u8_Array
struct H_u8
H_u8_Array arr_ptr
integer offset
integer m_len
integer m_cap
method destroy takes nothing returns nothing
local H_u8_Array a
if this != H_u8(0) then
set a = arr_ptr
call H_u8.deallocate(this)
call a.destroy()
endif
endmethod
static method new_len_cap takes integer len, integer cap returns H_u8
local H_u8 s
static if DEBUG_MODE then
local string m = "[H_u8.new_len_cap]: 1 <= cap; "
set m = m + "cap = " + I2S(cap)
call assert(m, 1 <= cap)
set m = "[H_u8.new_len_cap]: 0 <= len and len <= cap; "
set m = m + "len = " + I2S(len) + ", cap = " + I2S(cap)
call assert(m, 0 <= len and len <= cap)
endif
set s = H_u8.allocate()
set s.offset = 0
set s.m_len = len
set s.m_cap = cap
set s.arr_ptr = H_u8_Array.create(cap)
return s
endmethod
static method new_cap takes integer cap returns H_u8
return new_len_cap(0, cap)
endmethod
static method new_len takes integer len returns H_u8
return new_len_cap(len, len)
endmethod
method operator len takes nothing returns integer
return m_len
endmethod
method operator cap takes nothing returns integer
return m_cap
endmethod
method H takes integer b, integer e returns H_u8
local H_u8 s
local string m
if e == End then
set e = this.len
endif
static if DEBUG_MODE then
set m = "[H_u8.H]: 0 <= b and b < len; "
set m = m + "b = " + I2S(b) + ", len = " + I2S(this.len)
call assert(m, 0 <= b and b < this.len)
set m = "[H_u8.H]: b < e and e <= cap; "
set m = m + "b = " + I2S(b) + ", e = " + I2S(e) + ", cap = " + I2S(this.cap)
call assert(m, b < e and e <= this.cap)
endif
set s = H_u8.allocate()
set s.arr_ptr = arr_ptr
set arr_ptr.refs = arr_ptr.refs + 1
set s.offset = offset + b
set s.m_len = e - b
set s.m_cap = s.arr_ptr.cap - s.offset
return s
endmethod
method operator[] takes integer index returns integer
static if DEBUG_MODE then
local string s = "[H_u8.operator[]]: 0 <= index and index < this.len; "
set s = s + "index = " + I2S(index) + ", len = " + I2S(this.len)
call assert(s, 0 <= index and index < this.len)
endif
return LoadInteger(ht_u8, integer(arr_ptr), offset + index)
endmethod
method operator[]= takes integer index, integer val returns nothing
static if DEBUG_MODE then
local string s = "[H_u8.operator[]=]: 0 <= index and index < this.len; "
set s = s + "index = " + I2S(index) + ", len = " + I2S(this.len)
call assert(s, 0 <= index and index < this.len)
endif
call SaveInteger(ht_u8, integer(arr_ptr), offset + index, val)
endmethod
method eq takes H_u8 rhs returns boolean
local integer i
local integer ii
if m_len != rhs.m_len then
return false
endif
set i = 0
set ii = m_len
loop
exitwhen i >= ii
if this[i] != rhs[i] then
return false
endif
set i = i + 1
endloop
return true
endmethod
method str takes nothing returns string
local string s = "["
local integer i
local integer ii
set ii = this.len
if ii > 0 then
set s = s + I2S(this[0])
set i = 1
loop
exitwhen i >= ii
set s = s + " " + I2S(this[i])
set i = i + 1
endloop
endif
set s = s + "]"
return s
endmethod
static method copy takes H_u8 dst, H_u8 src returns integer
local integer min_len
local integer i
set min_len = src.len
if min_len > dst.len then
set min_len = dst.len
endif
if (src.arr_ptr != dst.arr_ptr) or (dst.offset <= src.offset) then
set i = 0
loop
exitwhen i >= min_len
set dst[i] = src[i]
set i = i + 1
endloop
else
set i = min_len - 1
loop
exitwhen i < 0
set dst[i] = src[i]
set i = i - 1
endloop
endif
return min_len
endmethod
method ensure_can_push takes integer n returns nothing
local integer new_cap
local H_u8_Array new_arr_ptr
if m_len + n > m_cap then
set new_cap = 2*m_cap
loop
exitwhen new_cap >= m_len + n
set new_cap = 2*new_cap
endloop
if new_cap > arr_ptr.cap then
set new_arr_ptr = H_u8_Array.create(new_cap)
call H_u8_Array.copy(new_arr_ptr, arr_ptr)
call arr_ptr.destroy()
set arr_ptr = new_arr_ptr
endif
set m_cap = new_cap
endif
endmethod
static method assert_u8 takes string s, integer arg returns nothing
call assert(s + "0 <= arg and arg <= 255; arg = " + I2S(arg), 0 <= arg and arg <= 255)
endmethod
method push takes integer x returns H_u8
static if DEBUG_MODE then
call assert_u8("[H_u8.push] x: ", x)
endif
if this == 0 then
set this = new_cap(1)
else
call ensure_can_push(1)
endif
call SaveInteger(ht_u8, integer(arr_ptr), offset + m_len, x)
set m_len = m_len + 1
return this
endmethod
method push2 takes integer x0, integer x1 returns H_u8
local integer i
static if DEBUG_MODE then
call assert_u8("[H_u8.push2] x0: ", x0)
call assert_u8("[H_u8.push2] x1: ", x1)
endif
if this == 0 then
set this = new_cap(2)
else
call ensure_can_push(2)
endif
set i = offset + m_len
call SaveInteger(ht_u8, integer(arr_ptr), i, x0)
call SaveInteger(ht_u8, integer(arr_ptr), i+1, x1)
set m_len = m_len + 2
return this
endmethod
method push3 takes integer x0, integer x1, integer x2 returns H_u8
local integer i
static if DEBUG_MODE then
call assert_u8("[H_u8.push3] x0: ", x0)
call assert_u8("[H_u8.push3] x1: ", x1)
call assert_u8("[H_u8.push3] x2: ", x2)
endif
if this == 0 then
set this = new_cap(3)
else
call ensure_can_push(3)
endif
set i = offset + m_len
call SaveInteger(ht_u8, integer(arr_ptr), i, x0)
call SaveInteger(ht_u8, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_u8, integer(arr_ptr), i+2, x2)
set m_len = m_len + 3
return this
endmethod
method push4 takes integer x0, integer x1, integer x2, integer x3 returns H_u8
local integer i
static if DEBUG_MODE then
call assert_u8("[H_u8.push4] x0: ", x0)
call assert_u8("[H_u8.push4] x1: ", x1)
call assert_u8("[H_u8.push4] x2: ", x2)
call assert_u8("[H_u8.push4] x3: ", x3)
endif
if this == 0 then
set this = new_cap(4)
else
call ensure_can_push(4)
endif
set i = offset + m_len
call SaveInteger(ht_u8, integer(arr_ptr), i, x0)
call SaveInteger(ht_u8, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_u8, integer(arr_ptr), i+2, x2)
call SaveInteger(ht_u8, integer(arr_ptr), i+3, x3)
set m_len = m_len + 4
return this
endmethod
method push5 takes integer x0, integer x1, integer x2, integer x3, integer x4 returns H_u8
local integer i
static if DEBUG_MODE then
call assert_u8("[H_u8.push5] x0: ", x0)
call assert_u8("[H_u8.push5] x1: ", x1)
call assert_u8("[H_u8.push5] x2: ", x2)
call assert_u8("[H_u8.push5] x3: ", x3)
call assert_u8("[H_u8.push5] x4: ", x4)
endif
if this == 0 then
set this = new_cap(5)
else
call ensure_can_push(5)
endif
set i = offset + m_len
call SaveInteger(ht_u8, integer(arr_ptr), i, x0)
call SaveInteger(ht_u8, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_u8, integer(arr_ptr), i+2, x2)
call SaveInteger(ht_u8, integer(arr_ptr), i+3, x3)
call SaveInteger(ht_u8, integer(arr_ptr), i+4, x4)
set m_len = m_len + 5
return this
endmethod
method push6 takes integer x0, integer x1, integer x2, integer x3, integer x4, integer x5 returns H_u8
local integer i
static if DEBUG_MODE then
call assert_u8("[H_u8.push6] x0: ", x0)
call assert_u8("[H_u8.push6] x1: ", x1)
call assert_u8("[H_u8.push6] x2: ", x2)
call assert_u8("[H_u8.push6] x3: ", x3)
call assert_u8("[H_u8.push6] x4: ", x4)
call assert_u8("[H_u8.push6] x4: ", x5)
endif
if this == 0 then
set this = new_cap(6)
else
call ensure_can_push(6)
endif
set i = offset + m_len
call SaveInteger(ht_u8, integer(arr_ptr), i, x0)
call SaveInteger(ht_u8, integer(arr_ptr), i+1, x1)
call SaveInteger(ht_u8, integer(arr_ptr), i+2, x2)
call SaveInteger(ht_u8, integer(arr_ptr), i+3, x3)
call SaveInteger(ht_u8, integer(arr_ptr), i+4, x4)
call SaveInteger(ht_u8, integer(arr_ptr), i+5, x5)
set m_len = m_len + 6
return this
endmethod
method clear takes nothing returns nothing
set m_len = 0
endmethod
endstruct // H_u8
public function slice_u8_test takes nothing returns nothing
local H_u8 s
set s = H_u8(0)
call assert("[slice_u8_test] 1", 0 == s.len)
set s = H_u8(0).push4('A', 'B', 'C', 'D')
call assert("[slice_u8_test] 2", 4 == s.len)
call assert("[slice_u8_test] 3", "[65 66 67 68]" == s.str())
if false then
set s = s.push(256)
endif
endfunction
endlibrary // libH
library callfunc
globals
private force f
endglobals
private module init
private static method onInit takes nothing returns nothing
set f = CreateForce()
call ForceAddPlayer(f, Player(PLAYER_NEUTRAL_PASSIVE))
endmethod
endmodule
private struct initer extends array
implement init
endstruct
function call_func takes code func returns nothing
call ForForce(f, func)
endfunction
endlibrary
library libHsort uses callfunc, libassert
globals
integer cmp_result = 0
integer int_a = 0
integer int_b = 0
mutstr mutstr_a = 0
mutstr mutstr_b = 0
unit unit_a = null
unit unit_b = null
endglobals
function ints_cmp takes nothing returns nothing
set cmp_result = int_a - int_b
endfunction
function ints_sort_asc takes nothing returns nothing
set cmp_result = int_a - int_b
endfunction
function ints_sort_desc takes nothing returns nothing
set cmp_result = int_b - int_a
endfunction
function sort_H_int_insertion takes H_int s, code cmp_func returns nothing
local integer i = 1
local integer ii = s.len
local integer j
loop
exitwhen i >= ii
set int_a = s[i]
set j = i - 1
loop
if j < 0 then
exitwhen true
endif
set int_b = s[j]
call call_func(cmp_func)
if cmp_result >= 0 then
exitwhen true
endif
set s[j+1] = int_b
set j = j - 1
endloop
set s[j+1] = int_a
set i = i + 1
endloop
endfunction
public function sort_H_int_test takes nothing returns nothing
local H_int s = H_int(0).push6(3, 1, 2, 5, 4, 2)
call sort_H_int_insertion(s, function ints_sort_asc)
call assert("[sort_H_int_test] 1", s.eq(H_int(0).push6(1, 2, 2, 3, 4, 5)))
call sort_H_int_insertion(s, function ints_sort_desc)
call assert("[sort_H_int_test] 2", s.eq(H_int(0).push6(5, 4, 3, 2, 2, 1)))
endfunction
function binary_search_H_int takes H_int s, integer x, code cmp_func returns integer
local integer left = 0
local integer right = s.len - 1
local integer mid
set int_a = x
loop
if left > right then
exitwhen true
endif
set mid = left + (right - left)/2
set int_b = s[mid]
call call_func(cmp_func)
if cmp_result < 0 then
set right = mid - 1
elseif cmp_result > 0 then
set left = mid + 1
else
set left = mid
exitwhen true
endif
endloop
return left
endfunction
public function binary_search_H_int_test takes nothing returns nothing
// local H_int s = H_int(0)
local H_int s = H_int(0).push5(-5, 0, 5, 10, 15)
local integer x = 10
local integer i
if false then
set i = binary_search_H_int(s, x, function ints_cmp)
if i < s.len and x == s[i] then
call writeln("found x = " + I2S(x) + " at index: " + I2S(i))
else
call writeln("could not find x = " + I2S(x) + ", but it's index should be: " + I2S(i))
endif
endif
call assert("[binary_search_H_int_test] 1", 0 == binary_search_H_int(s, -6, function ints_cmp) and -6 != s[0])
call assert("[binary_search_H_int_test] 2", 0 == binary_search_H_int(s, -5, function ints_cmp) and -5 == s[0])
call assert("[binary_search_H_int_test] 3", 1 == binary_search_H_int(s, 0, function ints_cmp))
call assert("[binary_search_H_int_test] 4", 2 == binary_search_H_int(s, 1, function ints_cmp) and 2 != s[2])
call assert("[binary_search_H_int_test] 5", 3 == binary_search_H_int(s, 10, function ints_cmp))
call assert("[binary_search_H_int_test] 6", 4 == binary_search_H_int(s, 15, function ints_cmp))
call assert("[binary_search_H_int_test] 7", 5 == binary_search_H_int(s, 16, function ints_cmp) and 5 == s.len)
endfunction
function mutstrs_cmp takes mutstr a, mutstr b returns integer
local integer a_len = a.len
local integer b_len = b.len
local integer i
if a_len != b_len then
set i = 0
loop
if i == a_len then
return -1
endif
if i == b_len then
return 1
endif
if a[i] < b[i] then
return -1
endif
if a[i] > b[i] then
return 1
endif
set i = i + 1
endloop
else
set i = 0
loop
if i == a_len then
return 0
endif
if a[i] < b[i] then
return -1
endif
if a[i] > b[i] then
return 1
endif
set i = i + 1
endloop
endif
// unreachable
return 0
endfunction
function mutstrs_sort_asc takes nothing returns nothing
set cmp_result = mutstrs_cmp(mutstr_a, mutstr_b)
endfunction
function mutstrs_sort_desc takes nothing returns nothing
set cmp_result = mutstrs_cmp(mutstr_b, mutstr_a)
endfunction
function sort_H_mutstr_insertion takes H_int /*H_mutstr*/ s, code cmp_func returns nothing
local integer i = 1
local integer ii = s.len
local integer j
loop
exitwhen i >= ii
set mutstr_a = mutstr(s[i])
set j = i - 1
loop
if j < 0 then
exitwhen true
endif
set mutstr_b = mutstr(s[j])
call call_func(cmp_func)
if cmp_result >= 0 then
exitwhen true
endif
set s[j+1] = mutstr_b
set j = j - 1
endloop
set s[j+1] = mutstr_a
set i = i + 1
endloop
endfunction
private function H_mutstr_to_str takes H_int /*H_mutstr*/ s returns string
local string r
local integer i
set r = "["
if s.len > 0 then
set r = r + "\"" + mutstr(s[0]).str() + "\""
set i = 1
loop
exitwhen i >= s.len
set r = r + " " + "\"" + mutstr(s[i]).str() + "\""
set i = i + 1
endloop
set r = r + "]"
endif
return r
endfunction
public function sort_H_mutstr_test takes nothing returns nothing
local H_int /*H_mutstr*/ s = H_int(0)
local string t
set s = s.push(integer(mutstr.from_string("")))
set s = s.push(integer(mutstr.from_string("Q")))
set s = s.push(integer(mutstr.from_string("A")))
set s = s.push(integer(mutstr.from_string("a")))
set s = s.push(integer(mutstr.from_string("B")))
set s = s.push(integer(mutstr.from_string("?")))
set s = s.push(integer(mutstr.from_string("aa")))
set s = s.push(integer(mutstr.from_string("™")))
set s = s.push(integer(mutstr.from_string("p")))
set s = s.push(integer(mutstr.from_string("Z")))
set s = s.push(integer(mutstr.from_string("O")))
set s = s.push(integer(mutstr.from_string("Q")))
if false then
call writeln(H_mutstr_to_str(s))
call sort_H_mutstr_insertion(s, function mutstrs_sort_asc)
call writeln(H_mutstr_to_str(s))
call sort_H_mutstr_insertion(s, function mutstrs_sort_desc)
call writeln(H_mutstr_to_str(s))
endif
call sort_H_mutstr_insertion(s, function mutstrs_sort_asc)
set t = H_mutstr_to_str(s)
call assert("[sort_H_mutstr_test] 1", "[\"\" \"A\" \"B\" \"Q\" \"Q\" \"Z\" \"a\" \"aa\" \"O\" \"p\" \"™\" \"?\"]" == t)
call sort_H_mutstr_insertion(s, function mutstrs_sort_desc)
set t = H_mutstr_to_str(s)
call assert("[sort_H_mutstr_test] 2", "[\"?\" \"™\" \"p\" \"O\" \"aa\" \"a\" \"Z\" \"Q\" \"Q\" \"B\" \"A\" \"\"]" == t)
endfunction
function sort_H_unit_insertion takes H_unit s, code cmp_func returns nothing
local integer i = 1
local integer ii = s.len
local integer j
loop
exitwhen i >= ii
set unit_a = s[i]
set j = i - 1
loop
if j < 0 then
exitwhen true
endif
set unit_b = s[j]
call call_func(cmp_func)
if cmp_result >= 0 then
exitwhen true
endif
set s[j+1] = unit_b
set j = j - 1
endloop
set s[j+1] = unit_a
set i = i + 1
endloop
endfunction
function binary_search_H_unit takes H_unit s, unit x, code cmp_func returns integer
local integer left = 0
local integer right = s.len - 1
local integer mid
set unit_a = x
loop
if left > right then
exitwhen true
endif
set mid = left + (right - left)/2
set unit_b = s[mid]
call call_func(cmp_func)
if cmp_result < 0 then
set right = mid - 1
elseif cmp_result > 0 then
set left = mid + 1
else
set left = mid
exitwhen true
endif
endloop
return left
endfunction
endlibrary // libHsort
library libchr uses libH, libassert
globals
string array chr
integer array ascii_uc_letters_delta
integer array ascii_lc_letters_delta
integer array ascii_descender
integer array hex_char_val
endglobals
// we use a dummy ability with an effect field with the bytes 0x00 to 0xFF
// in hex it would look something like:
//
// 01010203 04050607 08090A0B 0C0D0E0F
// 10111213 14151617 18191A1B 1C1D1E1F
// 20212223 24252627 28292A2B 2C2D2E2F
// 30313233 34353637 38393A3B 3C3D3E3F
// 40414243 44454647 48494A4B 4C4D4E4F
// 50515253 54555657 58595A5B 5C5D5E5F
// 60616263 64656667 68696A6B 6C6D6E6F
// 70717273 74757677 78797A7B 7C7D7E7F
// 80818283 84858687 88898A8B 8C8D8E8F
// 90919293 94959697 98999A9B 9C9D9E9F
// A0A1A2A3 A4A5A6A7 A8A9AAAB ACADAEAF
// B0B1B2B3 B4B5B6B7 B8B9BABB BCBDBEBF
// C0C1C2C3 C4C5C6C7 C8C9CACB CCCDCECF
// D0D1D2D3 D4D5D6D7 D8D9DADB DCDDDEDF
// E0E1E2E3 E4E5E6E7 E8E9EAEB ECEDEEEF
// F0F1F2F3 F4F5F6F7 F8F9FAFB FCFDFEFF
//
// because effect fields in the OE are stored as strings, they use a zero byte '00'
// to designate the end of the string (note that we have '0101', and not '0001' at the start)
//
// we can now use the native GetAbilityEffectById to load the bytes into a jass string
// which we can then break into bytes and store them into an array for easier access
//
// note that we have to call GetAbilityEffectById twice because it uses the ',' character (2C)
// as a separator (the effect field is really a list of effects)
//
private function chr_init takes nothing returns nothing
local string s = GetAbilityEffectById('UTF8', EFFECT_TYPE_EFFECT, 0) + "," + GetAbilityEffectById('UTF8', EFFECT_TYPE_EFFECT, 1)
local integer i = 0
static if DEBUG_MODE then
call assert("[chr_init] 1: 256 == StringLength(s); StringLength(s) = " + I2S(StringLength(s)), 256 == StringLength(s))
endif
set chr[255] = ""
set i = 0
loop
exitwhen i >= 256
set chr[i] = SubString(s, i, i + 1)
set i = i + 1
endloop
set chr[0] = ""
static if DEBUG_MODE then
call assert("[chr_init] 2: chr[','] == \",\"; chr[','] = " + chr[','], chr[','] == ",")
call assert("[chr_init] 3: chr['A'] == \"A\"; chr['A'] = " + chr['A'], chr['A'] == "A")
call assert("[chr_init] 4: chr['~'] == \"~\"; chr['~'] = " + chr['~'], chr['~'] == "~")
call assert("[chr_init] 5", "?" == chr[0xCE] + chr[0xA0])
call assert("[chr_init] 6", "O" == chr[0xCE] + chr[0xA9]) // 'CE A9' is the UTF8 encoding for 'GREEK CAPITAL LETTER OMEGA'
endif
endfunction
/*
// In patch 1.29 blizzard have made changes to their jass type checker. One can't
// trick it anymore (as far as I know), such that they would be able to make type casts.
// This type casting technique no longer works in 1.29+
//
// we use casting from string to integer and back because it allows us to implement
// the ord function in a much easier and efficient way comared to using the
// StringHash native, with weird lookup table and fixups
// (StringHash is not case sensitive, '\' hashes the same as '/')
//
globals
integer libchr_integer // not used, needed to fool jasshelper
integer l__libchr_integer
string libchr_string // not used, needed to fool jasshelper
string l__libchr_string
endglobals
private function set_integer takes integer i returns nothing
set l__libchr_integer = i
return // prevent jasshelper from inlining this function
endfunction
private function set_string takes string s returns nothing
set l__libchr_string = s
return // prevent jasshelper from inlining this function
endfunction
// this function "confuses" jass' typechecker/bytecode generator, I guess...
function libchr_enable_string_integer_typecasting takes nothing returns nothing
local string libchr_integer
local integer libchr_string
endfunction
//# +nosemanticerror
function cast_int_to_string takes integer i returns string
call set_integer(i)
return l__libchr_integer
endfunction
//# +nosemanticerror
function cast_string_to_int takes string s returns integer
call set_string(s)
return l__libchr_string
endfunction
globals
string ord_unique_prefix
integer ord_base
endglobals
function ord takes string c returns integer
return cast_string_to_int(ord_unique_prefix + c) - ord_base
endfunction
private function ord_init takes nothing returns nothing
local integer i
local string s
set ord_unique_prefix = chr[0xEE] + chr[0x80] + chr[0x80] // codepoint 0xE000 encoded in UTF8
set i = 0x01
loop
exitwhen i > 0xFF
set s = ord_unique_prefix + chr[i]
set i = i + 1
endloop
set ord_base = cast_string_to_int(ord_unique_prefix)
static if DEBUG_MODE then
set i = 0x00
loop
exitwhen i > 0xFF
call assert("[ord_init] i == ord(chr[i]; i = " + I2S(i), i == ord(chr[i]))
set i = i + 1
endloop
endif
endfunction
*/
function ord takes string c returns integer
local integer a = 0
// call assert("[ord] 1 == StringLength(c), StringLength(c) = " + I2S(StringLength(c)), 1 == StringLength(c))
loop
// exitwhen a > 0xFF
if c == chr[a] then
return a
endif
set a = a + 1
endloop
// unreachable
return -1
endfunction
private function ascii_letters_delta_init takes nothing returns nothing
local integer c
set ascii_uc_letters_delta[255] = 0
set ascii_lc_letters_delta[255] = 0
set c = 'A'
loop
exitwhen c > 'Z'
set ascii_uc_letters_delta[c] = 32
set c = c + 1
endloop
set c = 'a'
loop
exitwhen c > 'z'
set ascii_lc_letters_delta[c] = -32
set c = c + 1
endloop
endfunction
private function ascii_descender_init takes nothing returns nothing
set ascii_descender[127] = 0
set ascii_descender['g'] = 1
set ascii_descender['j'] = 1
set ascii_descender['p'] = 1
set ascii_descender['q'] = 1
set ascii_descender['y'] = 1
set ascii_descender[','] = 1
set ascii_descender[';'] = 1
endfunction
private function hex_char_val_init takes nothing returns nothing
set hex_char_val[127] = 0
set hex_char_val['0'] = 0
set hex_char_val['1'] = 1
set hex_char_val['2'] = 2
set hex_char_val['3'] = 3
set hex_char_val['4'] = 4
set hex_char_val['5'] = 5
set hex_char_val['6'] = 6
set hex_char_val['7'] = 7
set hex_char_val['8'] = 8
set hex_char_val['9'] = 9
set hex_char_val['A'] = 10
set hex_char_val['B'] = 11
set hex_char_val['C'] = 12
set hex_char_val['D'] = 13
set hex_char_val['E'] = 14
set hex_char_val['F'] = 15
set hex_char_val['a'] = 10
set hex_char_val['b'] = 11
set hex_char_val['c'] = 12
set hex_char_val['d'] = 13
set hex_char_val['e'] = 14
set hex_char_val['f'] = 15
endfunction
private module init
private static method onInit takes nothing returns nothing
call chr_init()
// call ord_init()
call ascii_letters_delta_init()
call ascii_descender_init()
call hex_char_val_init()
endmethod
endmodule
private struct initer extends array
implement init
endstruct
function H_u8_to_str takes H_u8 s returns string
local string r = ""
local integer i = 0
local integer ii = s.len
loop
exitwhen i >= ii
set r = r + chr[s[i]]
set i = i + 1
endloop
return r
endfunction
function ascii_to_upper takes H_u8 s returns nothing
local integer i = 0
local integer ii = s.len
local integer b
loop
exitwhen i >= ii
set b = s[i]
set s[i] = b + ascii_lc_letters_delta[b]
set i = i + 1
endloop
endfunction
function ascii_to_lower takes H_u8 s returns nothing
local integer i = 0
local integer ii = s.len
local integer b
loop
exitwhen i >= ii
set b = s[i]
set s[i] = b + ascii_uc_letters_delta[b]
set i = i + 1
endloop
endfunction
function is_ascii_uc takes integer c returns boolean
return ascii_uc_letters_delta[c] != 0
endfunction
function is_ascii_lc takes integer c returns boolean
return ascii_lc_letters_delta[c] != 0
endfunction
function is_ascii_descender takes integer c returns boolean
return ascii_descender[c] != 0
endfunction
function hex_val takes integer c returns integer
return hex_char_val[c]
endfunction
endlibrary // libchr
library libmutstr uses libH, libchr, libassert, callfunc
struct mutstr
H_u8 buf = H_u8(0)
// when we get mutated the 'str()' method recomputes the string
// and stores it in 'cached_str'
string cached_str = ""
boolean dirty = false
method destroy takes nothing returns nothing
call buf.destroy()
call deallocate(this)
endmethod
method operator len takes nothing returns integer
return buf.len
endmethod
method operator[] takes integer index returns integer
return buf[index]
endmethod
method operator[]= takes integer index, integer x returns nothing
set dirty = true
set buf[index] = x
endmethod
method push takes integer x returns nothing
set dirty = true
set buf = buf.push(x)
endmethod
method push2 takes integer x0, integer x1 returns nothing
set dirty = true
set buf = buf.push(x0)
set buf = buf.push(x1)
endmethod
method push3 takes integer x0, integer x1, integer x2 returns nothing
set dirty = true
set buf = buf.push(x0)
set buf = buf.push(x1)
set buf = buf.push(x2)
endmethod
method push4 takes integer x0, integer x1, integer x2, integer x3 returns nothing
set dirty = true
set buf = buf.push(x0)
set buf = buf.push(x1)
set buf = buf.push(x2)
set buf = buf.push(x3)
endmethod
method push5 takes integer x0, integer x1, integer x2, integer x3, integer x4 returns nothing
set dirty = true
set buf = buf.push(x0)
set buf = buf.push(x1)
set buf = buf.push(x2)
set buf = buf.push(x3)
set buf = buf.push(x4)
endmethod
method push6 takes integer x0, integer x1, integer x2, integer x3, integer x4, integer x5 returns nothing
set dirty = true
set buf = buf.push(x0)
set buf = buf.push(x1)
set buf = buf.push(x2)
set buf = buf.push(x3)
set buf = buf.push(x4)
set buf = buf.push(x5)
endmethod
// op-limit workaround
//
static H_u8 buf_ef
static string s_ef
static method push_string_ef takes nothing returns nothing
local string s = s_ef
local integer a = 0
local integer aa = StringLength(s)
loop
exitwhen a == aa
set buf_ef = buf_ef.push(ord(SubString(s, a, a + 1)))
set a = a + 1
endloop
endmethod
method push_string takes string s returns nothing
local integer i
local integer s_len
set s_len = StringLength(s)
if s_len == 0 then
if buf == 0 then
set buf = H_u8.new_cap(1)
endif
return
endif
set dirty = true
if buf == 0 then
set buf = H_u8.new_cap(s_len)
else
call buf.ensure_can_push(s_len)
endif
// we can't aford to implement it like this
// because our ord function got much slower in patch 1.29 (linear search)
//
// set i = 0
// loop
// exitwhen i == s_len
// set buf = buf.push(ord(SubString(s, i, i + 1)))
// set i = i + 1
// endloop
set buf_ef = buf
set s_ef = s
call call_func(function thistype.push_string_ef)
set buf = buf_ef
endmethod
method push_H_u8 takes H_u8 s returns nothing
local integer i
local integer s_len
set s_len = s.len
if s_len == 0 then
return
endif
set dirty = true
if buf == H_u8(0) then
set buf = H_u8.new_cap(s_len)
else
call buf.ensure_can_push(s_len)
endif
set i = 0
loop
exitwhen i == s_len
set buf = buf.push(s[i])
set i = i + 1
endloop
endmethod
method str takes nothing returns string
if not dirty then
return cached_str
endif
set dirty = false
set cached_str= H_u8_to_str(buf)
return cached_str
endmethod
static method from_string takes string s returns mutstr
local mutstr m = mutstr.create()
call m.push_string(s)
return m
endmethod
static method copy takes mutstr rhs returns mutstr
local mutstr s = mutstr.create()
set s.buf = H_u8.new_len_cap(rhs.buf.len, rhs.buf.cap)
call H_u8.copy(s.buf, rhs.buf)
set s.dirty = true
return s
endmethod
endstruct // mutstr
endlibrary // libmutstr
library Jass initializer init
public keyword List
globals
public key LPAREN
public key RPAREN
public key RBRACKET
public key LBRACKET
public key NEQ
public key EQ
public key LEQ
public key LT
public key GEQ
public key GT
public key PLUS
public key MINUS
public key MUL
public key DIV
public key AND
public key OR
public key NOT
public key CALL
public key SET
public key LOCAL
public key GLOBALS
public key ENDGLOBALS
public key TYPE
public key EXTENDS
public key NATIVE
public key CONSTANT
public key FUNCTION
public key TAKES
public key RETURNS
public key RETURN
public key IF
public key ELSEIF
public key THEN
public key ELSE
public key ENDIF
public key LOOP
public key EXITWHEN
public key ENDLOOP
public key ENDFUNCTION
public key NULL
public key TrueTok
public key FalseTok
public key ARRAY
public key NL
public key COMMA
public key NOTHING
public key IDENT
public key INTEGER
public key REAL
public key STRING
public key CODE
public key BOOL
public key HEX
public key OCT
public key RAWCODE
public key EqualTok
public constant integer EOF = 0
public string array NAMES
public string array OPERATORS
private integer array hex
public List Nil = 0
endglobals
public struct List
integer head = 0
List tail = 0
endstruct
public function cons takes integer hd, List tl returns List
local List l = List.create()
set l.head = hd
set l.tail = tl
return l
endfunction
public keyword Statement
public keyword NormalDecl
public keyword Expr
private function B2S takes boolean b returns string
if b then
return "true"
else
return "false"
endif
endfunction
private function print takes string s returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 120, s)
endfunction
private function trace takes string s returns real
call print(s)
return 0.0
endfunction
private function error takes string s returns nothing
local integer i
// silence
// call print(s)
set i = 1/0
endfunction
private function assert takes boolean b, string s returns nothing
if not b then
call error("assert: "+ s)
endif
endfunction
/*************************\
* * * * * * * * * * * * * *
* *
* *
* *
* A S T *
* *
* *
* *
* * * * * * * * * * * * * *
\*************************/
public struct Ast
stub method toString takes nothing returns string
return ""
endmethod
endstruct
public struct Toplevel extends Ast
endstruct
public struct VarDecl extends Ast
endstruct
public struct Globals extends Toplevel
List /*VarDecl*/ glbls
static method create takes List defs returns thistype
local thistype this = allocate()
set .glbls = defs
return this
endmethod
method toString takes nothing returns string
local string ret = "(globals \n"
local List l = glbls
loop
exitwhen l == Nil
set ret = ret + VarDecl(l.head).toString() +"\n"
set l = l.tail
endloop
return ret + ")\n"
endmethod
endstruct
public struct TypeDef extends Toplevel
string base
string new
static method create takes string base, string new returns thistype
local thistype this = allocate()
set .base = base
set .new = new
return this
endmethod
method toString takes nothing returns string
return "(type "+ new +" "+ base +")\n"
endmethod
endstruct
public struct TypeAndName extends Ast
string type
string name
static method create takes string typ, string name returns thistype
local thistype this = allocate()
set .name = name
set .type = typ
return this
endmethod
method toString takes nothing returns string
return .type +" : "+ .name
endmethod
endstruct
public struct Native extends Toplevel
boolean const
string name
List /*name, type*/ params
string returntype
static method create takes boolean const, string name, List paramlist, string returntype returns thistype
local thistype this = allocate()
set .const = const
set .name = name
set .params = paramlist
set .returntype = returntype
return this
endmethod
method toString takes nothing returns string
local List l = params
local string ret = "(native "+ name +" "
loop
exitwhen l == Nil
set ret = ret + TypeAndName(l.head).toString() +" "
set l = l.tail
endloop
set ret = ret + "-> "+ returntype +")\n"
return ret
endmethod
endstruct
public struct Function extends Toplevel
boolean const
string name
List /*name, type*/ params
string returntype
List /*Statement*/ body
static method create takes boolean const, string name, List paramlist, string returntype, List body returns thistype
local thistype this = allocate()
set .const = const
set .name = name
set .params = paramlist
set .returntype = returntype
set .body = body
return this
endmethod
method toString takes nothing returns string
local List l = params
local string ret = "(function "+ name +" "
loop
exitwhen l == Nil
set ret = ret + TypeAndName(l.head).toString() +" "
set l = l.tail
endloop
set ret = ret + "-> "+ returntype +"\n"
set l = body
loop
exitwhen l == Nil
set ret = ret + Statement(l.head).toString()
set l = l.tail
endloop
return ret + ")\n"
endmethod
endstruct
public struct ArrayDecl extends VarDecl
string name
string typ
static method create takes string t, string name returns thistype
local thistype this = allocate()
set .name = name
set .typ = t
return this
endmethod
method toString takes nothing returns string
return "(def "+ name +" : "+ typ +"-array)"
endmethod
endstruct
public struct NormalDecl extends VarDecl
boolean const
string name
string typ
Expr init
static method create takes boolean const, string t, string name, Expr init returns thistype
local thistype this = allocate()
set .name = name
set .typ = t
set .init = init
return this
endmethod
method toString takes nothing returns string
if init == 0 then
return "(def "+ name +" : "+ typ +")"
else
return "(def "+ name +" : "+ typ +" "+ init.toString() +")"
endif
endmethod
endstruct
public struct Statement extends Ast
endstruct
public struct Var extends Ast
endstruct
public struct Expr extends Ast
endstruct
public struct ArrayVar extends Var
string name
Expr idx
static method create takes string name, Expr idx returns thistype
local thistype this = allocate()
set .name = name
set .idx = idx
return this
endmethod
method toString takes nothing returns string
return "([] "+ name + " " + idx.toString() +")"
endmethod
endstruct
public struct NormalVar extends Var
string name
static method create takes string name returns thistype
local thistype this = allocate()
set .name = name
return this
endmethod
method toString takes nothing returns string
return name
endmethod
endstruct
public struct Set extends Statement
Var var
Expr value
static method create takes Var v, Expr e returns thistype
local thistype this = allocate()
set .var = v
set .value = e
return this
endmethod
method toString takes nothing returns string
return "(set "+ var.toString() +" "+ value.toString() +")\n"
endmethod
endstruct
public struct Call extends Ast
string name
List /*Expr*/ args
static method create takes string s, List /*Expr*/ l returns thistype
local thistype this = allocate()
set name = s
set .args = l
return this
endmethod
method toString takes nothing returns string
local string ret="(" + .name
local List l = args
loop
exitwhen l == 0
set ret = ret +" "+ Expr(l.head).toString()
set l = l.tail
endloop
return ret+")"
endmethod
endstruct
public struct CallStmt extends Statement
Call cll
static method create takes string s, List /*Expr*/ l returns thistype
local thistype this = allocate()
set .cll = Call.create(s, l)
return this
endmethod
method toString takes nothing returns string
return "(call "+ .cll.toString() +")\n"
endmethod
endstruct
public struct Local extends Statement
VarDecl vd
static method create takes VarDecl vd returns thistype
local thistype this = allocate()
set .vd = vd
return this
endmethod
method toString takes nothing returns string
return "(local "+ .vd.toString() +")\n"
endmethod
endstruct
public struct Loop extends Statement
List /*Statement*/ body
static method create takes List l returns thistype
local thistype this = allocate()
set .body = l
return this
endmethod
method toString takes nothing returns string
local string ret = "(loop \n"
local List l = body
loop
exitwhen l == Nil
set ret = ret + Statement(l.head).toString()
set l = l.tail
endloop
return ret +")\n"
endmethod
endstruct
public struct If extends Statement
Expr cond
List /*Statement*/ thenBody
List /*Statement*/ elseBody
static method create takes Expr cond, List thenBody, List elseBody returns thistype
local thistype this = allocate()
set .cond = cond
set .thenBody = thenBody
set .elseBody = elseBody
return this
endmethod
method toString takes nothing returns string
local string ret
local List it
local List tmp
set ret = "(if " + cond.toString() +" (\n"
set it = thenBody
loop
exitwhen it == Nil
set ret = ret + Statement(it.head).toString()
set it=it.tail
endloop
set ret = ret + ")("
set it = elseBody
loop
exitwhen it == Nil
set ret = ret + Statement(it.head).toString()
set it=it.tail
endloop
set ret = ret + "))\n"
return ret
endmethod
endstruct
public struct Exitwhen extends Statement
Expr cond
static method create takes Expr r returns thistype
local thistype this = allocate()
set .cond = r
return this
endmethod
method toString takes nothing returns string
return "(exitwhen "+ cond.toString() +")\n"
endmethod
endstruct
public struct Return extends Statement
Expr value
static method create takes Expr r returns thistype
local thistype this = allocate()
set .value = r
return this
endmethod
method toString takes nothing returns string
if value == 0 then
return "(return)\n"
else
return "(return "+ value.toString() +")\n"
endif
endmethod
endstruct
public struct Integer extends Expr
integer val
static method create takes integer s returns thistype
local thistype this = allocate()
set .val = s
return this
endmethod
method toString takes nothing returns string
return I2S(.val)
endmethod
endstruct
public struct Rawcode extends Expr
string val
static method create takes string s returns thistype
local thistype this = allocate()
set .val = s
return this
endmethod
method toString takes nothing returns string
return "'"+ .val +"'"
endmethod
endstruct
public struct Real extends Expr
real val
static method create takes real s returns thistype
local thistype this = allocate()
set .val = s
return this
endmethod
method toString takes nothing returns string
return R2SW(.val, 0, 5)
endmethod
endstruct
public struct String extends Expr
string val
static method create takes string s returns thistype
local thistype this = allocate()
set .val = s
return this
endmethod
method toString takes nothing returns string
return "\""+ .val +"\""
endmethod
endstruct
public struct Code extends Expr
string name
static method create takes string s returns thistype
local thistype this = allocate()
set .name = s
return this
endmethod
method toString takes nothing returns string
return "(function "+name+")"
endmethod
endstruct
public struct Null extends Expr
method toString takes nothing returns string
return "null"
endmethod
endstruct
public struct Bool extends Expr
boolean val
static method create takes boolean s returns thistype
local thistype this = allocate()
set .val = s
return this
endmethod
method toString takes nothing returns string
return B2S(.val)
endmethod
endstruct
public struct CallExpr extends Expr
Call cll
static method create takes string s, List /*Expr*/ l returns thistype
local thistype this = allocate()
set .cll = Call.create(s, l)
return this
endmethod
method toString takes nothing returns string
return .cll.toString()
endmethod
endstruct
public struct VarExpr extends Expr
Var var
static method create takes Var var returns thistype
local thistype this = allocate()
set .var = var
return this
endmethod
method toString takes nothing returns string
return .var.toString()
endmethod
endstruct
/*************************\
* * * * * * * * * * * * * *
* *
* *
* *
* L E X E R *
* *
* *
* *
* * * * * * * * * * * * * *
\*************************/
public struct Token
integer type
string value
static method create takes integer k, string v returns thistype
local thistype this = allocate()
set .type = k
set .value = v
return this
endmethod
method toString takes nothing returns string
return NAMES[.type]+" "+.value
endmethod
endstruct
private function startsWith takes string input, string t returns boolean
return SubString(input, 0, StringLength(t)) == t
endfunction
private function isDelim takes string s returns boolean
return s == " " or s == "\t" or s =="\n" or s=="\r" or s=="[" or s=="]" /*
*/ or s == "(" or s == ")" or s == "," or s =="." or s== "<" or s==">" /*
*/ or s == "/" or s=="*" or s== "+" or s =="-" or s=="$" or s== "=" /*
*/ or s == "\"" or s =="'" or s == "!"
endfunction
private function isDigit takes string s returns boolean
return s == "0" or s =="1" or s=="2" or s=="3" or s=="4" or s=="5" /*
*/ or s=="6" or s=="7" or s=="8" or s=="9"
endfunction
private function isLetter takes string s returns boolean
set s = StringCase(s, false)
return s=="a" or s=="b" or s=="c" or s=="d" or s=="e" or s=="f" /*
*/ or s=="g" or s=="h" or s=="i" or s=="j" or s=="k" or s=="l" /*
*/ or s=="m" or s=="n" or s=="o" or s=="p" or s=="q" or s=="r" /*
*/ or s=="s" or s=="t" or s=="u" or s=="v" or s=="w" or s=="x" /*
*/ or s=="y" or s=="z"
endfunction
private function isHex takes string s returns boolean
set s = StringCase(s, false)
return isDigit(s) or s=="a" or s=="b" or s=="c" or s=="d" /*
*/ or s == "e" or s=="f"
endfunction
private function isOct takes string s returns boolean
return s == "0" or s =="1" or s=="2" or s=="3" or s=="4" or s=="5" /*
*/ or s=="6" or s=="7"
endfunction
private function token takes string input, string t returns boolean
local integer i = StringLength(t)
return SubString(input, 0, i) == t and (isDelim(SubString(input, i, i+1)) or i==StringLength(input))
endfunction
private function op takes string input, string t returns boolean
return startsWith(input, t)
endfunction
private function hash takes string s returns integer
return StringHash(s) / 3183177 + 641
endfunction
private function hex2int takes string h returns integer
local integer i = 0
local integer sum = 0
local integer len = StringLength(h)
loop
exitwhen i >= len
set sum = sum*16 + hex[hash(SubString(h, i, i+1))]
set i = i +1
endloop
return sum
endfunction
private function oct2int takes string h returns integer
local integer i = 0
local integer sum = 0
local integer len = StringLength(h)
loop
exitwhen i >= len
set sum = sum*8 + hex[hash(SubString(h, i, i+1))]
set i = i +1
endloop
return sum
endfunction
public struct Lexer
private string input
private integer pos
private integer length
static method create takes string input returns thistype
local thistype this = allocate()
set .pos = 0
set .input = input
set .length = StringLength(.input)
return this
endmethod
private method slurpWhitespace takes nothing returns nothing
local string c = SubString(.input, .pos, .pos+1)
loop
exitwhen c != " " and c != "\t"
set .pos = .pos +1
set c = SubString(.input, .pos, .pos+1)
endloop
endmethod
private method parseIdent takes nothing returns Token
local integer p = .pos
local string c = SubString(.input, p, p +1)
local Token r
loop
exitwhen isDelim(c) or p >=.length
set p = p +1
set c = SubString(.input, p, p+1)
endloop
set r = Token.create(IDENT, SubString(.input, .pos, p))
set .pos = p
return r
endmethod
private method parseDotFloat takes nothing returns Token
local integer start = .pos
loop
exitwhen not isDigit(SubString(.input, .pos, .pos+1))
set .pos = .pos +1
endloop
return Token.create(REAL, "0." + SubString(.input, start, .pos))
endmethod
private method parseHex takes nothing returns Token
local integer start = .pos
loop
exitwhen not isHex(SubString(.input, .pos, .pos+1))
set .pos = .pos +1
endloop
return Token.create(INTEGER, I2S(hex2int(SubString(.input, start, .pos))))
endmethod
private method parseNumber takes nothing returns Token
local integer start = .pos
local boolean isReal = false
loop
exitwhen not isDigit(SubString(.input, .pos, .pos+1))
set .pos = .pos +1
endloop
if SubString(.input, .pos, .pos+1) == "." then
set isReal = true
set .pos = .pos +1
loop
exitwhen not isDigit(SubString(.input, .pos, .pos+1))
set .pos = .pos +1
endloop
endif
if isReal then
return Token.create(REAL, SubString(.input, start, .pos))
else
return Token.create(INTEGER, SubString(.input, start, .pos))
endif
endmethod
private method parseOct takes nothing returns Token
local integer start = .pos
local boolean isReal = false
loop
exitwhen not isDigit(SubString(.input, .pos, .pos+1))
set .pos = .pos +1
endloop
if SubString(.input, .pos, .pos+1) == "." then
set isReal = true
set .pos = .pos +1
loop
exitwhen not isDigit(SubString(.input, .pos, .pos+1))
set .pos = .pos +1
endloop
endif
if isReal then
return Token.create(REAL, SubString(.input, start, .pos))
else
// we may have parsed non-oct digits. for now we'll just ignore this possibility
return Token.create(INTEGER, I2S(oct2int(SubString(.input, start, .pos))))
endif
endmethod
private method parseRawcode takes nothing returns Token
local integer start = .pos
local string c
local Token t
loop
set c = SubString(.input, .pos, .pos +1)
if c == "\\" then
set .pos = .pos +1
elseif c == "'" then
exitwhen true
elseif .pos >= .length then
call error("Unclosed rawcode")
endif
set .pos = .pos +1
endloop
set t = Token.create(RAWCODE, SubString(.input, start, .pos))
set .pos = .pos +1
return t
endmethod
private method parseString takes nothing returns Token
local integer start = .pos
local string c
local Token t
loop
set c = SubString(.input, .pos, .pos +1)
if c == "\\" then
set .pos = .pos +1
elseif c == "\"" then
exitwhen true
elseif .pos >= .length then
call error("Unclosed string")
endif
set .pos = .pos +1
endloop
set t = Token.create(STRING, SubString(.input, start, .pos))
set .pos = .pos +1
return t
endmethod
method next takes nothing returns Token
local string c
local string cur
call .slurpWhitespace()
set c = SubString(.input, .pos, .pos +1)
set cur = SubString(.input, .pos, .length)
// TODO: comments
if c == "\r" or c == "\n" then
loop
exitwhen c != "\r" and c!= "\n" and c!=" " and c!="\t"
set .pos = .pos +1
set c = SubString(.input, .pos, .pos+1)
endloop
return Token.create(NL, "")
elseif c == "\"" then
set .pos = .pos +1
return .parseString()
elseif c == "'" then
set .pos = .pos +1
return .parseRawcode()
elseif token(cur, "call") then
set .pos = .pos +4
return Token.create(CALL, "")
elseif token(cur, "set") then
set .pos = .pos +3
return Token.create(SET, "")
elseif token(cur, "return") then
set .pos = .pos +6
return Token.create(RETURN, "")
elseif token(cur, "loop") then
set .pos = .pos +4
return Token.create(LOOP, "")
elseif token(cur, "exitwhen") then
set .pos = .pos +8
return Token.create(EXITWHEN, "")
elseif token(cur, "endloop") then
set .pos = .pos +7
return Token.create(ENDLOOP, "")
elseif token(cur, "if") then
set .pos = .pos +2
return Token.create(IF, "")
elseif token(cur, "then") then
set .pos = .pos +4
return Token.create(THEN, "")
elseif token(cur, "elseif") then
set .pos = .pos +6
return Token.create(ELSEIF, "")
elseif token(cur, "else") then
set .pos = .pos +4
return Token.create(ELSE, "")
elseif token(cur, "endif") then
set .pos = .pos +5
return Token.create(ENDIF, "")
elseif token(cur, "function") then
set .pos = .pos +8
return Token.create(FUNCTION, "")
elseif token(cur, "takes") then
set .pos = .pos +5
return Token.create(TAKES, "")
elseif token(cur, "returns") then
set .pos = .pos +7
return Token.create(RETURNS, "")
elseif token(cur, "nothing") then
set .pos = .pos +7
return Token.create(NOTHING, "")
elseif token(cur, "endfunction") then
set .pos = .pos +11
return Token.create(ENDFUNCTION, "")
elseif token(cur, "constant") then
set .pos = .pos +8
return Token.create(CONSTANT, "")
elseif token(cur, "native") then
set .pos = .pos +6
return Token.create(NATIVE, "")
elseif token(cur, "globals") then
set .pos = .pos +7
return Token.create(GLOBALS, "")
elseif token(cur, "endglobals") then
set .pos = .pos +10
return Token.create(ENDGLOBALS, "")
elseif token(cur, "type") then
set .pos = .pos +4
return Token.create(TYPE, "")
elseif token(cur, "extends") then
set .pos = .pos +7
return Token.create(EXTENDS, "")
elseif token(cur, "local") then
set .pos = .pos +5
return Token.create(LOCAL, "")
elseif token(cur, "array") then
set .pos = .pos +5
return Token.create(ARRAY, "")
elseif token(cur, "null") then
set .pos = .pos +4
return Token.create(NULL, "")
elseif token(cur, "true") then
set .pos = .pos +4
return Token.create(TrueTok, "")
elseif token(cur, "false") then
set .pos = .pos +5
return Token.create(FalseTok, "")
elseif token(cur, "and") then
set .pos = .pos +3
return Token.create(AND, "")
elseif token(cur, "or") then
set .pos = .pos +2
return Token.create(OR, "")
elseif token(cur, "not") then
set .pos = .pos +3
return Token.create(NOT, "")
elseif op(cur, "<=") then
set .pos = .pos +2
return Token.create(LEQ, "")
elseif op(cur, ">=") then
set .pos = .pos +2
return Token.create(GEQ, "")
elseif op(cur, "==") then
set .pos = .pos +2
return Token.create(EQ, "")
elseif op(cur, "!=") then
set .pos = .pos +2
return Token.create(NEQ, "")
elseif (c=="<") then
set .pos = .pos +1
return Token.create(LT, "")
elseif (c==">") then
set .pos = .pos +1
return Token.create(GT, "")
elseif (c=="+") then
set .pos = .pos +1
return Token.create(PLUS, "")
elseif (c=="-") then
set .pos = .pos +1
return Token.create(MINUS, "")
elseif (c=="*") then
set .pos = .pos +1
return Token.create(MUL, "")
elseif (c=="/") then
set .pos = .pos +1
return Token.create(DIV, "")
elseif (c==",") then
set .pos = .pos +1
return Token.create(COMMA, "")
elseif (c=="(") then
set .pos = .pos +1
return Token.create(LPAREN, "")
elseif (c==")") then
set .pos = .pos +1
return Token.create(RPAREN, "")
elseif (c=="[") then
set .pos = .pos +1
return Token.create(LBRACKET, "")
elseif (c=="]") then
set .pos = .pos +1
return Token.create(RBRACKET, "")
elseif (c=="=") then
set .pos = .pos +1
return Token.create(EqualTok, "")
elseif isLetter(c) then
return .parseIdent()
elseif startsWith(cur, "0x") or startsWith(cur, "0X") then
set .pos = .pos +2
return .parseHex()
elseif c == "$" then
set .pos = .pos +1
return .parseHex()
elseif c == "0" then
return .parseOct()
elseif isDigit(c) then
return .parseNumber()
elseif c == "." then
set .pos = .pos +1
return .parseDotFloat()
endif
call error("lex error: unexpected "+ c)
return 0
endmethod
method hasNext takes nothing returns boolean
call .slurpWhitespace()
return .pos < .length
endmethod
endstruct
/*************************\
* * * * * * * * * * * * * *
* *
* *
* *
* P A R S E R *
* *
* *
* *
* * * * * * * * * * * * * *
\*************************/
private function mkOp takes integer t, Expr lhs, Expr rhs returns Ast
local List l = cons(lhs, cons(rhs, Nil)) // [lhs, rhs]
local Ast ret = CallExpr.create(OPERATORS[t], l)
//call print("mkOp: "+ ret.toString())
return ret
endfunction
public struct Parser
private Lexer l
private Token cur
private static If LastElseif
private method operator sym takes nothing returns integer
return cur.type
endmethod
static method create takes string s returns thistype
local thistype this = allocate()
set .l = Lexer.create(s)
return this
endmethod
method nextsym takes nothing returns integer
if l.hasNext() then
if cur != 0 then
call cur.destroy()
endif
set cur = l.next()
//call print("nextsym: "+NAMES[cur.type])
return cur.type
else
//call print("nextsym error: lexer empty")
set cur.type = EOF
return EOF
endif
endmethod
private method accept takes integer t returns boolean
if sym == t then
call nextsym()
return true
else
return false
endif
endmethod
private method expect takes integer t returns nothing
if sym != t then
call error("Parser error: Expected "+ NAMES[t] +" but got "+ NAMES[sym])
endif
call nextsym()
endmethod
/*
AND
OR
LT GT EQ NEQ LEQ GEQ
NOT
MINUS PLUS
MUL DIV
*/
private method parseArgs takes nothing returns List
local Expr e
if accept(RPAREN) then
return Nil
else
set e = .p1()
if accept(COMMA) then
return cons(e, parseArgs())
else
call expect(RPAREN)
return cons(e, Nil)
endif
endif
endmethod
private method p6 takes nothing returns Ast
local Ast r
local string v
local Ast tmp
local List l
loop
exitwhen not accept(PLUS)
endloop
if accept(MINUS) then
return CallExpr.create("-", cons(.p6(), Nil))
endif
if sym == IDENT then
set v = cur.value
call nextsym()
if accept(LPAREN) then
return CallExpr.create(v, .parseArgs())
elseif accept(LBRACKET) then
set tmp = .p1()
call expect(RBRACKET)
return VarExpr.create(ArrayVar.create(v, Expr(tmp)))
else
return VarExpr.create(NormalVar.create(v))
endif
elseif sym == INTEGER then
set v = cur.value
call nextsym()
return Integer.create(S2I(v))
elseif sym == RAWCODE then
set v = cur.value
call nextsym()
return Rawcode.create(v)
elseif sym == REAL then
set v = cur.value
call nextsym()
return Real.create(S2R(v))
elseif sym == STRING then
set v = cur.value
call nextsym()
return String.create(v)
elseif accept(FUNCTION) then
set v = cur.value
call expect(IDENT)
return Code.create(v)
elseif accept(TrueTok) then
return (Bool.create(true))
elseif accept(FalseTok) then
return (Bool.create(false))
elseif accept(NULL) then
return Null.create()
elseif accept(LPAREN) then
set r = .p1()
call expect(RPAREN)
return r
endif
call error("Expected an expression")
return 0
endmethod
private method p5 takes nothing returns Ast
local Ast lhs = .p6()
local integer t
loop
exitwhen sym != MUL and sym != DIV
set t = sym
call nextsym()
set lhs = mkOp(t, lhs, .p6())
endloop
return (lhs)
endmethod
private method p4 takes nothing returns Ast
local Ast lhs
local integer t
if accept(NOT) then
set lhs = CallExpr.create("not", cons(.p4(), Nil))
else
set lhs = .p5()
endif
loop
exitwhen sym != MINUS and sym != PLUS
set t = sym
call nextsym()
set lhs = mkOp(t, lhs, .p5())
endloop
return (lhs)
endmethod
private method p3 takes nothing returns Ast
local Ast lhs = .p4()
local integer t
loop
exitwhen sym != LT and sym != GT and sym != EQ and sym != NEQ /*
*/ and sym != LEQ and sym != GEQ
set t = sym
call nextsym()
set lhs = mkOp(t, lhs, .p4())
endloop
return (lhs)
endmethod
private method p2 takes nothing returns Ast
local Ast lhs = .p3()
loop
exitwhen sym != OR
call nextsym()
set lhs = mkOp(OR, lhs, .p3())
endloop
return (lhs)
endmethod
private method p1 takes nothing returns Ast
local Ast lhs = .p2()
loop
exitwhen sym != AND
call nextsym()
set lhs = mkOp(AND, lhs, .p2())
endloop
return (lhs)
endmethod
private method parseLVar takes nothing returns Var
local string v = cur.value
local Expr tmp
call expect(IDENT)
if accept(LBRACKET) then
set tmp = .p1()
call expect(RBRACKET)
return VarExpr.create(ArrayVar.create(v, Expr(tmp)))
else
return VarExpr.create(NormalVar.create(v))
endif
endmethod
private method parseStatements takes nothing returns List
local Statement t = pStmt()
if t == 0 then
return Nil
else
call expect(NL)
return cons(t, parseStatements())
endif
endmethod
private method parseElseIfs takes nothing returns List
local Expr c
local If e
if accept(ELSEIF) then
set c = p1()
call expect(THEN)
call expect(NL)
set e = If.create(c, parseStatements(), parseElseIfs())
if e.elseBody == Nil then
set LastElseif = e
endif
return cons(e, Nil)
else
return Nil
endif
endmethod
private method parseIf takes nothing returns Statement
local Expr cond = .p1()
local List thenBody
local List elseBody = 0
local List elseifs
call expect(THEN)
call expect(NL)
set thenBody = parseStatements()
set LastElseif = 0
set elseifs = parseElseIfs()
call assert(LastElseif.elseBody == 0, "last elseif body zero")
if accept(ELSE) then
call expect(NL)
set elseBody = parseStatements()
endif
call expect(ENDIF)
if elseifs == Nil then
return If.create(cond, thenBody, elseBody)
else
set LastElseif.elseBody = elseBody
return If.create(cond, thenBody, elseifs)
endif
endmethod
private method pStmt takes nothing returns Statement
local string n
local List l
local Ast tmp
if accept(CALL) then
set n = cur.value
call expect(IDENT)
call expect(LPAREN)
set l = .parseArgs()
return CallStmt.create(n, l)
elseif accept(SET) then
set tmp = parseLVar()
call expect(EqualTok)
return Set.create(Var(tmp), .p1())
elseif accept(LOCAL) then
return Local.create(parseVarDecl(false))
elseif accept(RETURN) then
if sym == NL then
return Return.create(0)
else
return Return.create(.p1())
endif
elseif accept(EXITWHEN) then
return Exitwhen.create(.p1())
elseif accept(LOOP) then
call expect(NL)
set tmp = Loop.create(parseStatements())
call expect(ENDLOOP)
return tmp
elseif accept(IF) then
return parseIf()
endif
return 0
endmethod
private method parseVarDecl takes boolean const returns VarDecl
local string name
local string t
local Expr init = 0
set t = cur.value
call expect(IDENT)
if accept(ARRAY) then
set name = cur.value
call expect(IDENT)
return ArrayDecl.create(t, name)
else
set name = cur.value
call expect(IDENT)
if accept(EqualTok) then
set init = .p1()
endif
endif
return NormalDecl.create(const, t, name, init)
endmethod
private method parseGlobalVarDecls takes nothing returns List
local VarDecl vd
if accept(CONSTANT) then
set vd = parseVarDecl(true)
call expect(NL)
return cons(vd, parseGlobalVarDecls())
elseif accept(ENDGLOBALS) then
return Nil
elseif accept(NL) then
return parseGlobalVarDecls()
else
set vd = parseVarDecl(false)
call expect(NL)
return cons(vd, parseGlobalVarDecls())
endif
endmethod
private method pTypeAndName takes nothing returns TypeAndName
local string t
local string n
set t = cur.value
call expect(IDENT)
set n = cur.value
call expect(IDENT)
return TypeAndName.create(t, n)
endmethod
private method parseParamlist2 takes nothing returns List
local TypeAndName e
if accept(RETURNS) then
return Nil
else
set e = pTypeAndName()
if accept(COMMA) then
return cons(e, parseParamlist2())
else
call expect(RETURNS)
return cons(e, Nil)
endif
endif
endmethod
private method parseParamlist takes nothing returns List
if accept(NOTHING) then
call expect(RETURNS)
return Nil
else
return parseParamlist2()
endif
endmethod
private method parseReturntype takes nothing returns string
local string t
if accept(NOTHING) then
return "nothing"
else
set t = cur.value
call expect(IDENT)
return t
endif
endmethod
private method pNative takes boolean const returns Native
local string name
local string returntype
local List paramlist
set name = cur.value
call expect(IDENT)
call expect(TAKES)
set paramlist = parseParamlist()
set returntype = parseReturntype()
return Native.create(const, name, paramlist, returntype)
endmethod
private method pFunction takes boolean const returns Function
local string name
local string returntype
local List paramlist
local List body
set name = cur.value
call expect(IDENT)
call expect(TAKES)
set paramlist = parseParamlist()
set returntype = parseReturntype()
call expect(NL)
set body = parseStatements()
call expect(ENDFUNCTION)
return Function.create(const, name, paramlist, returntype, body)
endmethod
private method pNativeOrFunction takes boolean const returns Toplevel
if accept(NATIVE) then
return pNative(const)
else
return pFunction(const)
endif
endmethod
method pToplevel takes nothing returns Toplevel
local Toplevel t
local string n
local string b
if accept(GLOBALS) then
call expect(NL)
set t = Globals.create(parseGlobalVarDecls())
call expect(NL)
return t
elseif accept(TYPE) then
set n = cur.value
call expect(IDENT)
call expect(EXTENDS)
set b = cur.value
call expect(IDENT)
set t = TypeDef.create(b, n)
call expect(NL)
return t
elseif accept(CONSTANT) then
call pNativeOrFunction(true)
elseif accept(NATIVE) then
set t = pNative(false)
call expect(NL)
return t
elseif accept(FUNCTION) then
set t = pFunction(false)
call expect(NL)
return t
endif
call error("Not a toplevel decleration")
return 0
endmethod
method parseToplevel takes nothing returns Toplevel
call nextsym()
return pToplevel()
endmethod
private method pProgram takes nothing returns List /*Toplevel*/
if accept(NL) then
return pProgram()
elseif accept(EOF) then
return Nil
else
return cons(pToplevel(), pProgram())
endif
endmethod
method parseExpr takes nothing returns Expr
call nextsym()
return Expr(.p1())
endmethod
method parseStatement takes nothing returns Statement
local Statement t
call nextsym()
set t = pStmt()
if t == 0 then
call error("Not a statement")
endif
call expect(NL)
return t
endmethod
method parseProgram takes nothing returns List /*Toplevel*/
call nextsym()
return pProgram()
endmethod
endstruct
private function init takes nothing returns nothing
local string charmap = "0123456789abcdef"
local integer i = 0
local integer len = StringLength(charmap)
loop
exitwhen i == len
set hex[hash(SubString(charmap, i, i+1))] = i
set i = i +1
endloop
set NAMES[LPAREN] = "LPAREN"
set NAMES[RPAREN] = "RPAREN"
set NAMES[RBRACKET] = "RBRACKET"
set NAMES[LBRACKET] = "LBRACKET"
set NAMES[NEQ] = "NEQ"
set NAMES[EQ] = "EQ"
set NAMES[LEQ] = "LEQ"
set NAMES[LT] = "LT"
set NAMES[GEQ] = "GEQ"
set NAMES[GT] = "GT"
set NAMES[PLUS] = "PLUS"
set NAMES[MINUS] = "MINUS"
set NAMES[MUL] = "MUL"
set NAMES[DIV] = "DIV"
set NAMES[AND] = "AND"
set NAMES[OR] = "OR"
set NAMES[NOT] = "NOT"
set NAMES[CALL] = "CALL"
set NAMES[SET] = "SET"
set NAMES[LOCAL] = "LOCAL"
set NAMES[GLOBALS] = "GLOBALS"
set NAMES[ENDGLOBALS] = "ENDGLOBALS"
set NAMES[TYPE] = "TYPE"
set NAMES[EXTENDS] = "EXTENDS"
set NAMES[NATIVE] = "NATIVE"
set NAMES[CONSTANT] = "CONSTANT"
set NAMES[FUNCTION] = "FUNCTION"
set NAMES[TAKES] = "TAKES"
set NAMES[RETURNS] = "RETURNS"
set NAMES[RETURN] = "RETURN"
set NAMES[IF] = "IF"
set NAMES[ELSEIF] = "ELSEIF"
set NAMES[THEN] = "THEN"
set NAMES[ELSE] = "ELSE"
set NAMES[ENDIF] = "ENDIF"
set NAMES[LOOP] = "LOOP"
set NAMES[EXITWHEN] = "EXITWHEN"
set NAMES[ENDLOOP] = "ENDLOOP"
set NAMES[ENDFUNCTION] = "ENDFUNCTION"
set NAMES[NULL] = "NULL"
set NAMES[TrueTok] = "TRUE"
set NAMES[FalseTok] = "FALSE"
set NAMES[ARRAY] = "ARRAY"
set NAMES[NL] = "NL"
set NAMES[COMMA] = "COMMA"
set NAMES[NOTHING] = "NOTHING"
set NAMES[IDENT] = "IDENT"
set NAMES[RAWCODE] = "RAWCODE"
set NAMES[INTEGER] = "INTEGER"
set NAMES[STRING] = "STRING"
set NAMES[REAL] = "REAL"
set NAMES[EqualTok] = "EQUAL"
set NAMES[EOF] = "EOF"
set OPERATORS[MINUS] = "-"
set OPERATORS[PLUS] = "+"
set OPERATORS[MUL] = "*"
set OPERATORS[DIV] = "/"
set OPERATORS[EQ] = "=="
set OPERATORS[NEQ] = "!="
set OPERATORS[LEQ] = "<="
set OPERATORS[GEQ] = ">="
set OPERATORS[LT] = "<"
set OPERATORS[GT] = ">"
set OPERATORS[AND] = "and"
set OPERATORS[OR] = "or"
set OPERATORS[NOT] = "not"
endfunction
endlibrary
library entrypoint initializer init uses libH, libmutstr, libassert, Jass
globals
constant real Tau = 6.2831853
string Rect_File_Name = "misc\\rect.blp"
endglobals
function rtos takes real r returns string
return R2SW(r, 1, 2)
endfunction
function is_point_in_rect takes real x, real y, real minx, real miny, real maxx, real maxy returns boolean
if x < minx or x > maxx or y < miny or y > maxy then
return false
endif
return true
endfunction
globals
integer R
integer G
integer B
integer A
endglobals
function get_rgba takes integer i returns nothing
local boolean is_neg = false
if i < 0 then
set is_neg = true
set i = i + 0x80000000
endif
set A = i - i / 0x100 * 0x100
set i = i / 0x100
set B = i - i / 0x100 * 0x100
set i = i / 0x100
set G = i - i / 0x100 * 0x100
set i = i / 0x100
set R = i
if is_neg then
set R = R + 0x80
endif
endfunction
function parse_hex takes string ss returns integer
local mutstr s = mutstr.from_string(ss)
local integer red
local integer green
local integer blue
local integer alpha
local integer color
set red = 16*hex_val(s[0]) + hex_val(s[1])
set green = 16*hex_val(s[2]) + hex_val(s[3])
set blue = 16*hex_val(s[4]) + hex_val(s[5])
set alpha = 16*hex_val(s[6]) + hex_val(s[7])
call s.destroy()
set color = red*0x1000000 + green*0x10000 + blue*0x100 + alpha
return color
endfunction
function rect_wh takes real x, real y, real w, real h, integer color, string img_path returns image
local image r
if color != 0 then
set img_path = Rect_File_Name
endif
set r = CreateImage(img_path, /*
*/ w, h, 0.0, /*
*/ x, y, 0.0, /*
origin: */ 0.0, 0.0, 0.0, /*
image-type: */ 2 /*
*/)
call assert("[rect_wh] invalid image path: \"" + img_path + "\"", -1 != GetHandleId(r))
call SetImageRenderAlways(r, true)
if color != 0 then
call get_rgba(color)
call SetImageColor(r, R, G, B, A)
endif
return r
endfunction
struct Img_Text
mutstr text
real x
real y
real w // letter width
real h // letter height
integer color = 0xFFFFFFFF
H_image imgs = 0
real minx
real miny
real maxx
real maxy
boolean centered = false
endstruct
function img_text_clear takes Img_Text t returns nothing
local integer a = t.imgs.len
loop
exitwhen a == 0
set a = a - 1
call DestroyImage(t.imgs[a])
endloop
call t.imgs.clear()
endfunction
function img_text_redraw_line takes Img_Text t returns nothing
local mutstr s = t.text
local real x = t.x
local real y = t.y
local real w = t.w
local real h = t.h
local real initial_x = x
local real dx
local real dy
local real yy
local real hh
local real ddy
local integer c
local image img
local integer initial_red
local integer initial_green
local integer initial_blue
local integer initial_alpha
local integer red
local integer green
local integer blue
local integer alpha
local integer a
call img_text_clear(t)
call get_rgba(t.color)
set initial_red = R
set initial_green = G
set initial_blue = B
set initial_alpha = A
set red = initial_red
set green = initial_green
set blue = initial_blue
set alpha = initial_alpha
set dx = w - 0.25*w
set dy = -h
set ddy = -0.25*h
set y = y - h
set a = 0
loop
set c = s[a]
exitwhen c == 0
if c == 10 then
set x = initial_x
set y = y + dy
set a = a + 1
elseif c == ' ' then
set x = x + dx
set a = a + 1
elseif c == '|' and s[a + 1] == 'c' then
set a = a + 2
set alpha = 16*hex_val(s[a]) + hex_val(s[a + 1])
set red = 16*hex_val(s[a + 2]) + hex_val(s[a + 3])
set green = 16*hex_val(s[a + 4]) + hex_val(s[a + 5])
set blue = 16*hex_val(s[a + 6]) + hex_val(s[a + 7])
set a = a + 8
elseif c == '|' and s[a + 1] == 'r' then
set alpha = initial_alpha
set red = initial_red
set green = initial_green
set blue = initial_blue
set a = a + 2
else
set yy = y
if is_ascii_descender(c) then
set yy = yy + ddy
endif
set hh = h
if c == 'j' then
set hh = hh - ddy
endif
set img = CreateImage("fonts\\font-1\\" + I2S(c) + ".blp", /*
*/ w, hh, 0.0, /*
*/ x, yy, 0.0, /*
origin: */ 0.0, 0.0, 0.0, /*
image-type: */ 1 /*
*/)
if -1 == GetHandleId(img) then
set img = CreateImage("fonts\\font-1\\63.blp", /*
*/ w, h, 0.0, /*
*/ x, y, 0.0, /*
origin: */ 0.0, 0.0, 0.0, /*
image-type: */ 1 /*
*/)
call assert("[img_text] 1", -1 != GetHandleId(img))
endif
call SetImageRenderAlways(img, true)
call SetImageColor(img, red, green, blue, alpha)
set t.imgs = t.imgs.push(img)
set x = x + dx
// set y = y + dy
set a = a + 1
endif
endloop
endfunction
function img_text_compute_width takes Img_Text t returns real
local real w = t.w
local real dx = w - 0.25*w
local integer a
local integer c
local real width = 0.0
set a = 0
loop
set c = t.text[a]
exitwhen c == 0
if c != '|'then
set width = width + dx
set a = a + 1
else
set c = t.text[a + 1]
if c == 'c' then
set a = a + 10
elseif c == 'r' then
set a = a +2
else
set width = width + dx
set a = a + 1
endif
endif
endloop
return width
endfunction
function img_text_recenter takes Img_Text t returns nothing
local real text_width = img_text_compute_width(t)
set t.x = t.minx + 0.5*(t.maxx - t.minx - text_width)
set t.y = t.miny + 0.5*(t.maxy - t.miny + t.h)
endfunction
function img_text_redraw takes Img_Text t returns nothing
if t.centered then
call img_text_recenter(t)
endif
call img_text_redraw_line(t)
endfunction
function img_text_set_text takes Img_Text t, string text returns nothing
call t.text.buf.clear()
call t.text.push_string(text)
call t.text.push(0)
call img_text_redraw(t)
endfunction
function img_text_push_string takes Img_Text t, string s returns nothing
set t.text.buf.m_len = t.text.buf.m_len - 1 // overwrite '\0'
call t.text.push_string(s)
call t.text.push(0)
endfunction
function img_text_set_color takes Img_Text t, integer color returns nothing
set t.color = color
call img_text_redraw(t)
endfunction
function img_text_centered takes string text, real minx, real miny, real maxx, real maxy, real w, real h, integer color returns Img_Text
local Img_Text t = Img_Text.create()
set t.minx = minx
set t.miny = miny
set t.maxx = maxx
set t.maxy = maxy
set t.w = w
set t.h = h
set t.color = color
set t.centered = true
set t.text = mutstr.from_string(text)
// push a 0 byte so that we don't have to check
// if the a+1 element is within range
call t.text.push(0)
call img_text_recenter(t)
call img_text_redraw_line(t)
return t
endfunction
function img_text_line takes string text, real x, real y, real w, real h, integer color returns Img_Text
local Img_Text t = Img_Text.create()
set t.x = x
set t.y = y
set t.w = w
set t.h = h
set t.color = color
set t.text = mutstr.from_string(text)
call t.text.push(0)
call img_text_redraw_line(t)
return t
endfunction
globals
boolean mouse_down = false
real mouse_down_x = 0.0
real mouse_down_y = 0.0
real mouse_up_x = 0.0
real mouse_up_y = 0.0
endglobals
function on_mouse takes nothing returns nothing
local eventid ev = GetTriggerEventId()
if ev == EVENT_PLAYER_MOUSE_DOWN then
set mouse_down = true
set mouse_down_x = BlzGetTriggerPlayerMouseX()
set mouse_down_y = BlzGetTriggerPlayerMouseY()
else
set mouse_down = false
set mouse_up_x = BlzGetTriggerPlayerMouseX()
set mouse_up_y = BlzGetTriggerPlayerMouseY()
endif
endfunction
function mouse_init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_MOUSE_DOWN)
call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_MOUSE_UP)
call TriggerAddAction(t, function on_mouse)
endfunction
struct Button
real x
real y
real w
real h
image rect
Img_Text text
boolean m_clicked = false
integer user_data = 0
integer back_color
integer text_color
string icon_path
method is_icon_button takes nothing returns boolean
return icon_path != ""
endmethod
method clicked takes nothing returns boolean
if m_clicked then
set m_clicked = false
return true
endif
return false
endmethod
endstruct
globals
Button active_button = 0
Button array buttons
integer buttons_len = 0
Button last_clicked_btn = 0
endglobals
function button_new takes nothing returns Button
local Button b = Button.create()
set buttons[buttons_len] = b
set buttons_len = buttons_len + 1
return b
endfunction
function text_button_create takes string text, real x, real y, real w, real h, integer back_color, integer text_color returns Button
local Button b = button_new()
local real font_size
set b.icon_path = ""
set b.back_color = back_color
set b.text_color = text_color
set b.x = x
set b.y = y
set b.w = w
set b.h = h
set b.rect = rect_wh(x, y, w, h, back_color, "")
set font_size = 0.34*h
if font_size > 0.34*w then
set font_size = 0.34*w
endif
set b.text = img_text_centered(text, x, y, x+w, y+h, font_size, font_size, text_color)
return b
endfunction
function icon_button_create takes string icon_path, real x, real y, real w, real h, integer user_data returns Button
local Button b = button_new()
set b.icon_path = icon_path
set b.x = x
set b.y = y
set b.w = w
set b.h = h
set b.rect = rect_wh(x, y, w, h, 0, icon_path)
set b.user_data = user_data
return b
endfunction
function button_draw_active takes Button b returns nothing
local real x
local real y
local real w
local real h
if b.is_icon_button() then
call DestroyImage(b.rect)
set x = b.x + (1.0/8.0)*b.w
set y = b.y + (1.0/8.0)*b.h
set w = b.w - (1.0/4.0)*b.w
set h = b.h - (1.0/4.0)*b.h
set b.rect = rect_wh(x, y, w, h, 0, b.icon_path)
else
call get_rgba(b.text_color)
call SetImageColor(b.rect, R, G, B, A)
call img_text_set_color(b.text, b.back_color)
endif
endfunction
function button_draw_normal takes Button b returns nothing
if b.is_icon_button() then
call DestroyImage(b.rect)
set b.rect = rect_wh(b.x, b.y, b.w, b.h, 0, b.icon_path)
else
call get_rgba(b.back_color)
call SetImageColor(b.rect, R, G, B, A)
call img_text_set_color(b.text, b.text_color)
endif
endfunction
function is_point_in_button takes real x, real y, Button b returns boolean
return is_point_in_rect(x, y, b.x, b.y, b.x + b.w, b.y + b.h)
endfunction
function on_button_click takes nothing returns nothing
local Button b
local integer a
if EVENT_PLAYER_MOUSE_DOWN == GetTriggerEventId() then
set a = 0
loop
exitwhen a == buttons_len
set b = buttons[a]
if is_point_in_button(mouse_down_x, mouse_down_y, b) then
set active_button = b
call button_draw_active(b)
exitwhen true
endif
set a = a + 1
endloop
elseif active_button != 0 then
set b = active_button
set active_button = 0
if is_point_in_button(mouse_up_x, mouse_up_y, b) then
set b.m_clicked = true
set last_clicked_btn = b
endif
call button_draw_normal(b)
endif
endfunction
function button_init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_MOUSE_DOWN)
call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_MOUSE_UP)
call TriggerAddAction(t, function on_button_click)
endfunction
globals
Img_Text user_input
string expr_str
boolean clear_before_push = false
Jass_Parser parser
string eval_err = ""
real eval_result = 0.0
endglobals
function error takes string s returns nothing
set eval_err = "|cFFFF0000" + s + "|r"
call I2S(1/0)
endfunction
function synax_error takes nothing returns nothing
call error("Syntax Error")
endfunction
function args_error takes nothing returns nothing
call error("Args Error")
endfunction
function count_args takes Jass_List list returns integer
local integer result = 0
local Jass_List node = list
loop
exitwhen node == 0
set result = result + 1
set node = node.tail
endloop
return result
endfunction
function assert_args takes integer expected, integer given returns nothing
if expected != given then
call args_error()
endif
endfunction
function eval takes Jass_Expr e returns real
local integer ty = e.getType()
local Jass_Call fn
local real result
local string name
local integer args_count
local real a
local real b
if ty == Jass_Integer.typeid then
set result = Jass_Integer(e).val
elseif ty == Jass_Real.typeid then
set result = Jass_Real(e).val
elseif ty == Jass_VarExpr.typeid then
set result = eval(Jass_VarExpr(e).var)
elseif ty == Jass_NormalVar.typeid then
set name = Jass_NormalVar(e).name
if name == "tau" then
set result = Tau
elseif name == "pi" then
set result = bj_PI
elseif name == "e" then
set result = bj_E
else
call assert("[eval] unknown var '" + name + "'", false)
endif
elseif ty == Jass_CallExpr.typeid then
set result = eval(Jass_CallExpr(e).cll)
elseif ty == Jass_Call.typeid then
set fn = Jass_Call(e)
set name = fn.name
set args_count = count_args(fn.args)
if name == "+" then
set a = eval(fn.args.head)
set b = eval(fn.args.tail.head)
set result = a + b
elseif name == "-" then
set args_count = count_args(fn.args)
if args_count == 2 then
set a = eval(fn.args.head)
set b = eval(fn.args.tail.head)
set result = a - b
else
call assert("[eval] op-, args: " + I2S(args_count), args_count == 1)
set a = eval(fn.args.head)
set result = -a
endif
elseif name == "*" then
set a = eval(fn.args.head)
set b = eval(fn.args.tail.head)
set result = a * b
elseif name == "/" then
set a = eval(fn.args.head)
set b = eval(fn.args.tail.head)
if b == 0.0 then
call error("Divide by 0")
else
set result = a / b
endif
elseif name == "sin" then
call assert_args(1, args_count)
set a = eval(fn.args.head)
set result = Sin(a)
elseif name == "cos" then
call assert_args(1, args_count)
set a = eval(fn.args.head)
set result = Cos(a)
elseif name == "tan" then
call assert_args(1, args_count)
set a = eval(fn.args.head)
set result = Tan(a)
elseif name == "asin" then
call assert_args(1, args_count)
set a = eval(fn.args.head)
set result = Asin(a)
elseif name == "acos" then
call assert_args(1, args_count)
set a = eval(fn.args.head)
set result = Acos(a)
elseif name == "atan" then
call assert_args(1, args_count)
set a = eval(fn.args.head)
set result = Atan(a)
elseif name == "atan2" then
call assert_args(2, args_count)
set a = eval(fn.args.head)
set b = eval(fn.args.tail.head)
set result = Atan2(a, b)
elseif name == "sqrt" then
call assert_args(1, args_count)
set a = eval(fn.args.head)
set result = SquareRoot(a)
elseif name == "pow" then
call assert_args(2, args_count)
set a = eval(fn.args.head)
set b = eval(fn.args.tail.head)
set result = Pow(a, b)
else
call synax_error()
endif
else
call assert("[eval]: unknown expr type " + I2S(ty), false)
endif
call e.destroy()
return result
endfunction
function compute_result_ef takes nothing returns nothing
local Jass_Expr e
local real result
call ClearTextMessages()
// call writeln("EVALUATING: |" + expr_str + "|")
set parser = Jass_Parser.create(expr_str)
set eval_err = "|cFFFF0000Syntax Error|r"
set e = parser.parseExpr() // can stop the execution
set eval_err = ""
// call writeln(e.toString())
set eval_result = eval(e)
call parser.destroy()
endfunction
function compute_result takes nothing returns boolean
local string s
local integer a
set expr_str = user_input.text.str()
call call_func(function compute_result_ef)
if eval_err != "" then
call img_text_set_text(user_input, eval_err)
set clear_before_push = true
return false
else
if eval_result == R2I(eval_result) then
set s = I2S(R2I(eval_result))
else
set s = R2SW(eval_result, 1, 6)
set a = StringLength(s)
loop
if "0" != SubString(s, a - 1, a) then
exitwhen true
endif
set a = a - 1
endloop
if "." == SubString(s, a - 1, a) then
set a = a - 1
endif
set s = SubString(s, 0, a)
endif
call img_text_set_text(user_input, s)
return true
endif
endfunction
struct Calc_Btn
Button b
string pushed_string
method clicked takes nothing returns boolean
return b.clicked()
endmethod
endstruct
globals
Calc_Btn c_btn
Calc_Btn eq_btn
Calc_Btn undo_btn
H_int history = 0
integer history_index = 0
endglobals
function history_clear takes nothing returns nothing
local integer a
local mutstr s
set a = 0
loop
exitwhen a == history.len
set s = history[a]
call s.destroy()
set a = a + 1
endloop
call history.clear()
set history_index = 0
endfunction
function history_push takes mutstr s returns nothing
set history = history.push(mutstr.copy(s))
set history_index = history_index + 1
endfunction
function calc_btn takes string text, string pushed_string, real x, real y, real w, real h, integer back_color, integer text_color returns Calc_Btn
local Calc_Btn b = Calc_Btn.create()
set b.b = text_button_create(text, x, y ,w, h, back_color, text_color)
set b.b.user_data = b
set b.pushed_string = pushed_string
return b
endfunction
function on_btn_click takes nothing returns nothing
local Calc_Btn b
local mutstr s
if last_clicked_btn == 0 then
return
endif
set b = last_clicked_btn.user_data
set last_clicked_btn = 0
if b == c_btn then
call history_clear()
call img_text_set_text(user_input, "")
elseif b == eq_btn then
if compute_result() then
call history_push(user_input.text)
endif
elseif b == undo_btn then
// if clear_before_push then
set clear_before_push = false
// endif
if history_index > 1 then
set s = history[history_index - 2]
call mutstr(history[history_index - 1]).destroy()
set history.m_len = history.m_len - 1
set history_index = history_index - 1
call img_text_set_text(user_input, s.str())
else
call history_clear()
call img_text_set_text(user_input, "")
endif
else
if clear_before_push then
set clear_before_push = false
call img_text_set_text(user_input, "")
endif
call img_text_push_string(user_input, b.pushed_string)
call img_text_redraw(user_input)
call history_push(user_input.text)
endif
endfunction
function calc_init takes nothing returns nothing
local real offset_x
local real offset_y
local real x
local real y
local real dx
local real dy
local real w
local real h
local real font_size
local image background
local integer back_color
local integer text_color
set back_color = 0x262626FF
set text_color = 0x00EEc099
set font_size = 32.0
set w = 48.0
set h = 48.0
set dx = w - 1.0
set dy = -(h - 1.0)
// set background = rect_wh(-512.0, -512.0, 1024.0, 1024.0, 0xFB31F8FF, "")
set background = rect_wh(-512.0, -512.0, 1024.0, 1024.0, back_color, "")
set background = rect_wh(-1024.0, -512.0, 1024.0, 1024.0, back_color, "")
set background = rect_wh(500.0, -512.0, 1024.0, 1024.0, back_color, "")
set user_input = img_text_line("", -288.0, 80, 0.5*font_size, 0.5*font_size, text_color)
set offset_x = 0.0
set offset_y = 0.0
set x = offset_x
set y = offset_y
call calc_btn("(", "(", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn(",", ",", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn(")", ")", x, y, w, h, back_color, text_color)
set x = x + dx
set undo_btn = calc_btn("undo", "", x, y, 2.0*w, h, back_color, text_color)
set y = y + dy
set x = offset_x
call calc_btn("7", "7", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("8", "8", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("9", "9", x, y, w, h, back_color, text_color)
set y = y + dy
set x = offset_x
call calc_btn("4", "4", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("5", "5", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("6", "6", x, y, w, h, back_color, text_color)
set y = y + dy
set x = offset_x
call calc_btn("1", "1", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("2", "2", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("3", "3", x, y, w, h, back_color, text_color)
set y = y + dy
set x = offset_x
call calc_btn("0", "0", x, y, 2.0*w, h, back_color, text_color)
set x = x + 2.0*dx
call calc_btn(".", ".", x, y, w, h, back_color, text_color)
set x = offset_x + 3*dx
set y = offset_y + dy
call calc_btn("/", "/", x, y, w, h, back_color, text_color)
set y = y + dy
call calc_btn("*", "*", x, y, w, h, back_color, text_color)
set y = y + dy
call calc_btn("-", "-", x, y, w, h, back_color, text_color)
set y = y + dy
call calc_btn("+", "+", x, y, w, h, back_color, text_color)
set x = offset_x + 4*dx
set y = offset_y + dy
set c_btn = calc_btn("C", "", x, y, w, h, back_color, text_color)
set y = y + dy
set eq_btn = calc_btn("=", "", x, y + 2.0*dy, w, 3.0*h, back_color, text_color)
set w = 96.0
set dx = w - 1.0
set offset_x = 0.0 - 3*dx
set offset_y = 0.0
set x = offset_x
set y = offset_y
call calc_btn("tau", "tau", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("pi", "pi", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("e", "e", x, y, w, h, back_color, text_color)
set y = y + dy
set x = offset_x
call calc_btn("sin", "sin(", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("cos", "cos(", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("tan", "tan(", x, y, w, h, back_color, text_color)
set y = y + dy
set x = offset_x
call calc_btn("asin", "asin(", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("acos", "acos(", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("atan", "atan(", x, y, w, h, back_color, text_color)
set y = y + dy
set x = offset_x
call calc_btn("atan2", "atan2(", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("sqrt", "sqrt(", x, y, w, h, back_color, text_color)
set x = x + dx
call calc_btn("pow", "pow(", x, y, w, h, back_color, text_color)
call TimerStart(CreateTimer(), 1.0/32.0, true, function on_btn_click)
endfunction
function camera takes real z, real dist returns nothing
call SetCameraField(CAMERA_FIELD_ROTATION, 90.0, 0.0)
call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, 270.0, 0.0)
call PanCameraToTimed(0.0, -64.0, 0.0)
call SetCameraField(CAMERA_FIELD_ZOFFSET, z, 0.0)
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, dist, 0.0)
endfunction
function camera_update takes nothing returns nothing
call camera(600.0, 0.0)
endfunction
function camera_init takes nothing returns nothing
call camera(600.0, 0.0)
call TimerStart(CreateTimer(), 1.0/32.0, true, function camera_update)
endfunction
function main_init takes nothing returns nothing
call mouse_init()
call button_init()
call calc_init()
call camera_init()
endfunction
function visibility_init takes nothing returns nothing
call FogEnable(false)
call FogMaskEnable(false)
endfunction
private function init takes nothing returns nothing
call visibility_init()
call main_init()
endfunction
endlibrary