- Joined
- Nov 7, 2014
- Messages
- 571
Autocall - automatic function calling from player chat commands
I think this library can be useful while developing a map. Exploring native functions or testing jass functions for edge cases (although manually) without having to do any hooking (parsing, registering, etc.) should save time in my opinion.
Edit: Attached the "big list" of native wrappers. The safe version of a native starts with an 's':
I think this library can be useful while developing a map. Exploring native functions or testing jass functions for edge cases (although manually) without having to do any hooking (parsing, registering, etc.) should save time in my opinion.
JASS:
//! novjass
Autocall - automatic function calling from player chat commands
Description:
Autocall automates the process of calling functions from chat commands.
Both natives and jass functions are supported. Jass functions have the advantage
of being type checkable (using "bytecde introspection", finally a good use for BJ functions).
Syntax:
Only chat commands that begin with '&' or '$' are processed:
Variable declaration:
$my_integer : integer
$my_real : real
$my_string : string
$my_handle : handle
$my_boolean : boolean
variable declaration with initialization:
$my_integer : integer = 1
$my_real : real = 6.28
$my_string : string = "stuff inside double quotes"
$my_handle : handle = Player 0
$my_boolean : boolean = true
Predefined variables:
$i: integer
$r: real
$s: string
$h: handle
$b: boolean
Types:
integer:
can be specified in base 10, 16 and 256:
$i = 126 -- base 10
$i = 0x7E -- base 16 (hex)
$i = 'Hpal' -- base 256
real:
real arguments have a '.':
$r = 1.0
$r = 6.28
string:
$s = "stuff inside double quotes"
handle:
$h = P(0) -- Player(0)
$h = {6.28,2.71} -- Location(6.28, 2.71)
$h = CreateUnit P(0) 'hpea' 0.0 0.0 270.0
boolean:
the following values denote boolean true:
T, t, true
the following vlaues denote boolean false:
F, f, false
Calling functions:
with no arguments and no return value:
&DoNothing
with no arguments and a return value:
$r = GetRandomDirectionDeg
with an argument and no return value:
&BJDebugMsg "foo"
with arguments and a return value:
$r = RMinBJ 1.5 2.5
note:
because we dont know the arguments and their types of native functions (without having a big list)
calling native functions with incorrect arguments can result in a game crash
(be careful with typos when calling natives)
jass functions have the advantage of being type checkable and thus safe
Examples:
$my_hero : handle = CreateUnit P(0) 'Hpal' -512.0 1024.0 270.0
&SetHeroLevelBJ $my_hero 5 F
creates a Paladin hero at location x:-512.0, y: 1024.0 facing 270.0 degrees
and sets the hero level to 5 without showing eyecandy
$g : handle = CreateNUnitsAtLoc 4 'hpea' P(1) {512.0,1024.0} 45.0
$random_unit : handle = GroupPickRandomUnit $g
&KillUnit $random_unit
creates 4 blue peasants at location x: 512.0, y: 1024.0 facing 45.0 degrees and adds them to a unit group
selectes a random unit from the unit group
and kills the randomly selected peasant
&Autocall_list_vars
calls the public function list_vars defined in the Autocall library
//! endnovjass
library Autocall initializer init uses BytecodeBoilerplate
private function dd takes string s returns nothing
call BJDebugMsg(s)
endfunction
globals
public boolean error
endglobals
private function exit takes nothing returns nothing
set error = true
if 1 / 0 == 1 then
endif
endfunction
private function unreachable takes string s returns nothing
call dd("unreachable: " + s)
call exit()
endfunction
private function unimplemented takes string s returns nothing
call dd("unimplemented: " + s)
call exit()
endfunction
//# +nosemanticerror
private function r2i takes real r returns integer
return r
return r // prevent jasshelper from inlining in non debug mode
endfunction
private function i2i takes integer i returns integer
return i
return i
endfunction
public function real_as_int takes real r returns integer
return i2i(r2i(r))
endfunction
//# +nosemanticerror
private function i2r takes integer i returns real
return i
return i
endfunction
private function r2r takes real r returns real
return r
return r
endfunction
public function int_as_real takes integer i returns real
return r2r(i2r(i))
endfunction
public function from_hex takes string s returns integer
local string hex_digits = "0123456789ABCDEF"
local integer result = 0
local string ch
local integer i
local integer j
set s = StringCase(s, /*upper: */ true)
set i = 0
loop
set ch = SubString(s, i, i + 1)
exitwhen ch == ""
set j = 0
loop
exitwhen j > 15
if ch == SubString(hex_digits, j, j + 1) then
set result = result * 16 + j
exitwhen true
endif
set j = j + 1
endloop
set i = i + 1
endloop
return result
endfunction
public function to_hex takes integer i returns string
local string hex_digits = "0123456789ABCDEF"
local string result = ""
local integer n
local integer d
local integer t
local boolean is_neg
set is_neg = i < 0
if is_neg then
set i = i + 0x80000000
endif
set n = 1
loop
exitwhen n > 7
set t = i / 16
set d = i - t * 16
set i = t
set result = SubString(hex_digits, d, d + 1) + result
set n = n + 1
endloop
if is_neg then
set i = i + 0x8
endif
set result = SubString(hex_digits, i, i + 1) + result
return result
endfunction
public function from_base_256 takes string oid returns integer
local string map = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
local integer result = 0
local integer i
local integer j
local string c
set i = 0
loop
set c = SubString(oid, i, i + 1)
exitwhen c == ""
set j = 0
loop
if c == SubString(map, j, j + 1) then
set result = result*256 + (j + 33)
exitwhen true
else
set j = j + 1
endif
endloop
set i = i + 1
endloop
return result
endfunction
public function bool_to_str takes boolean b returns string
if b then
return "true"
else
return "false"
endif
endfunction
globals
private string array T
private integer T_count = 0
endglobals
private function tokenize takes nothing returns nothing
local string s = GetEventPlayerChatString()
local integer si
local integer i
local string ch
set T_count = 0
set i = 0
loop
loop
set ch = SubString(s, i, i + 1)
exitwhen ch != " "
set i = i + 1
endloop
exitwhen ch == ""
set si = i
if SubString(s, i, i + 1) == "\"" then
set i = i + 1
loop
set ch = SubString(s, i, i + 1)
if ch == "\\" then
set i = i + 1
if SubString(s, i, i + 1) == "\"" then
set i = i + 1
endif
elseif ch == "\"" then
set i = i + 1
exitwhen true
else
exitwhen ch == ""
set i = i + 1
endif
endloop
else
loop
set i = i + 1
set ch = SubString(s, i, i + 1)
exitwhen ch == " " or ch == ""
endloop
endif
set T_count = T_count + 1
set T[T_count] = SubString(s, si, i)
endloop
endfunction
globals
private integer array bc
private code bc_code
private integer bc_offset
endglobals
globals
private integer result_integer = 0xFF
private integer result_integer_stid
private real result_real = 0.0
private integer result_real_stid
private real result_real2 = 0.0
private integer result_real2_stid
private string result_string = ""
private integer result_string_stid
private handle result_handle = null
private integer result_handle_stid
private boolean result_boolean = false
private integer result_boolean_stid
// used for finding the address of a jass function's bytecode
private code var_code = null
private integer var_code_stid
private integer empty_string_stid
private integer Value_h_stid // the h member of struct Value
private integer first_blizzardj_func_stid
private integer second_string_handle_table_entry_stid // first is ""
private integer Player_stid
endglobals
private function stid takes string s returns integer
return stid_from_handle(s)
endfunction
private function result_vars_and_funcs_stid_init takes nothing returns nothing
set bc[8190] = 0
set bc_code = I2C(Memory[get_array_struct_from_name(SCOPE_PRIVATE + "bc")/4 + 12/4])
set result_integer_stid = stid(SCOPE_PRIVATE + "result_integer")
set result_real_stid = stid(SCOPE_PRIVATE + "result_real")
set result_real2_stid = stid(SCOPE_PRIVATE + "result_real2")
set result_string_stid = stid(SCOPE_PRIVATE + "result_string")
set result_handle_stid = stid(SCOPE_PRIVATE + "result_handle")
set result_boolean_stid = stid(SCOPE_PRIVATE + "result_boolean")
set var_code_stid = stid(SCOPE_PRIVATE + "var_code")
set empty_string_stid = stid("")
// doesn't work because vJass randomises the name (different number of underscores)
// set Value_h_stid = stid("s__Autocall__Value_h")
// see Value_h_stid_init
set first_blizzardj_func_stid = stid("BJDebugMsg")
set second_string_handle_table_entry_stid = stid(I2SH(2))
set Player_stid = stid("Player")
endfunction
private function X takes nothing returns integer
set bc_offset = bc_offset + 1
return bc_offset
endfunction
private function call_bc_begin takes nothing returns nothing
set bc_offset = -1
endfunction
private function call_bc_end takes nothing returns nothing
call ForForce(bj_FORCE_PLAYER[0], bc_code)
endfunction
private /*enum*/ struct Value_Type extends array
static constant integer Whoknows = 0x00
static constant integer Integer = 0x04 // digits: 0 .. 9 (base 10) or 'XXXX' (base 256) or 0x89ABCDEF (base 16/hex)
static constant integer Real = 0x05 // only digits and a single dot: 6.28
static constant integer String = 0x06 // "stuff inside double quotes"
static constant integer Handle = 0x07 // point/location literals: {6.28,2.71}, player literals: P(0 .. 15) and stuff returned from native functions different from integers, reals, etc.
static constant integer Boolean = 0x08 // true = T, t, true, false = F, f, false
endstruct
globals
private string array value_type_to_string
endglobals
private function value_type_to_string_init takes nothing returns nothing
set value_type_to_string[0] = "nothing"
set value_type_to_string[Value_Type.Integer] = "integer"
set value_type_to_string[Value_Type.Real] = "real"
set value_type_to_string[Value_Type.String] = "string"
set value_type_to_string[Value_Type.Handle] = "handle"
set value_type_to_string[Value_Type.Boolean] = "boolean"
endfunction
private function value_type_to_str takes integer value_type returns string
return value_type_to_string[value_type]
endfunction
private function value_type_from_str takes string s returns integer
if s == "integer" then
return Value_Type.Integer
elseif s == "real" then
return Value_Type.Real
elseif s == "string" then
return Value_Type.String
elseif s == "handle" then
return Value_Type.Handle
elseif s == "boolean" then
return Value_Type.Boolean
else
return Value_Type.Whoknows
endif
endfunction
private keyword Variable
private keyword Value
private function Value_h_stid_init takes nothing returns nothing
set Value(0).h = null
set Value_h_stid = Memory[C2I(function Value_h_stid_init)/4 + (8 + 8 + 8 + 8 + 4)/4]
endfunction
private function var_get_value takes Variable var returns nothing
local integer ty
if var == 0 then
call dd("error: var_get_value var == 0")
call exit()
endif
call call_bc_begin()
set ty = var.ty
if ty == Value_Type.Integer then
set bc[X()] = 0x0E010400
set bc[X()] = var.name_stid
set bc[X()] = 0x11010000
set bc[X()] = result_integer_stid
elseif ty == Value_Type.Real then
set bc[X()] = 0x0E010500
set bc[X()] = var.name_stid
set bc[X()] = 0x11010000
set bc[X()] = result_real_stid
elseif ty == Value_Type.String then
set bc[X()] = 0x0E010600
set bc[X()] = var.name_stid
set bc[X()] = 0x11010000
set bc[X()] = result_string_stid
elseif ty == Value_Type.Handle then
set bc[X()] = 0x0E010700
set bc[X()] = var.name_stid
set bc[X()] = 0x11010000
set bc[X()] = result_handle_stid
elseif ty == Value_Type.Boolean then
set bc[X()] = 0x0E010800
set bc[X()] = var.name_stid
set bc[X()] = 0x11010000
set bc[X()] = result_boolean_stid
else
call unimplemented("var_get_value Value_Type ty: " + I2S(ty))
endif
set bc[X()] = 0x27000000
set bc[X()] = 0x00000000
call call_bc_end()
endfunction
private function var_set_value takes Variable var, Value value returns nothing
local integer ty
if var.ty != value.ty then
call dd("error: cannot assign variable " + var.name + " (" + value_type_to_str(var.ty) + ") to value " + value.to_str() + " (" + value_type_to_str(value.ty) + ")")
call exit()
endif
call call_bc_begin()
set ty = value.ty
if ty == Value_Type.Integer then
set bc[X()] = 0x0C010400
set bc[X()] = value.i
set bc[X()] = 0x11010000
set bc[X()] = var.name_stid
elseif ty == Value_Type.Real then
set bc[X()] = 0x0C010500
set bc[X()] = real_as_int(value.r)
set bc[X()] = 0x11010000
set bc[X()] = var.name_stid
elseif ty == Value_Type.String then
set bc[X()] = 0x0C010600
set bc[X()] = stid(value.s)
set bc[X()] = 0x11010000
set bc[X()] = var.name_stid
elseif ty == Value_Type.Handle then
set bc[X()] = 0x0C020400
set bc[X()] = integer(value)
set bc[X()] = 0x10010207
set bc[X()] = Value_h_stid
set bc[X()] = 0x11010000
set bc[X()] = var.name_stid
elseif ty == Value_Type.Boolean then
set bc[X()] = 0x0C010800
if value.b then
set bc[X()] = 0x00000001
else
set bc[X()] = 0x00000000
endif
set bc[X()] = 0x11010000
set bc[X()] = var.name_stid
else
call unimplemented("var_set_value Value_Type ty: " + I2S(ty))
endif
set bc[X()] = 0x27000000
set bc[X()] = 0x00000000
call call_bc_end()
endfunction
globals
private hashtable ht = InitHashtable()
endglobals
private function get_var_or_null takes string name returns Variable
return Variable(LoadInteger(ht, 0, SH2I(name)))
endfunction
private function set_var_exist takes Variable var returns nothing
call SaveInteger(ht, 0, SH2I(var.name), var)
endfunction
private struct Value
Value_Type ty
integer i // set only when ty == Value_Type.Integer
real r // when ty == Value_Type.Real
string s // when ty == Value_Type.String
handle h // when ty == Value_Type.Handle
boolean b // when ty == Value_Type.Boolean
static method public_allocate takes nothing returns thistype
return allocate()
endmethod
static method from_str takes string s returns thistype
local thistype this = allocate()
local integer slen = StringLength(s)
local Variable var
local integer ty
local integer i
local string first_char
local string last_char
local string c
local integer sign
local real x
local real y
set first_char = SubString(s, 0, 1)
if first_char == "$" then
set var = get_var_or_null(s)
if var == 0 then
call dd("error: use of undeclared variable " + s)
call exit()
endif
set this.ty = var.ty
call var_get_value(var)
set ty = var.ty
if ty == Value_Type.Integer then
set this.i = result_integer
elseif ty == Value_Type.Real then
set this.r = result_real
elseif ty == Value_Type.String then
set this.s = result_string
elseif ty == Value_Type.Handle then
set this.h = result_handle
elseif ty == Value_Type.Boolean then
set this.b = result_boolean
else
call unreachable("Value.from_str Value_Type ty: " + I2S(ty))
endif
return this
endif
set c = first_char
if c == "-" then
set sign = -1
set s = SubString(s, 1, slen)
set c = SubString(s, 0, 1)
else
set sign = 1
endif
if c == "0" or c == "1" or c == "2" or c == "3" or c == "4" or c == "5" or c == "6" or c == "7" or c == "8" or c == "9" then
if c == "0" then
set c = SubString(s, 1, 2)
if c == "x" or c == "X" then
if slen > 10 then
call dd("error: invalid base 16 integer literal: " + s + ", can be at most 8 digits") // 8 = 10 - 2 (0x prefix)
call exit()
endif
set this.ty = Value_Type.Integer
set this.i = from_hex(SubString(s, 2, slen)) * sign
return this
endif
endif
set i = 1
loop
exitwhen i == slen or "." == SubString(s, i, i + 1)
set i = i + 1
endloop
if i == slen then
set this.ty = Value_Type.Integer
set this.i = S2I(s) * sign
else
set this.ty = Value_Type.Real
set this.r = S2R(s) * sign
endif
return this
endif
set last_char = SubString(s, slen - 1, slen)
if first_char == "'" and last_char == "'" then
if slen > 6 then
call dd("error: invalid base 256 integer literal: " + s + ", can be at most 4 digits") // 4 = 6 - 2 (surrounding single quotes)
call exit()
endif
set this.ty = Value_Type.Integer
set this.i = from_base_256(SubString(s, 1, slen - 1))
return this
endif
if first_char == "\"" then
set this.ty = Value_Type.String
if last_char == "\"" then
set this.s = SubString(s, 1, slen - 1)
else
set this.s = SubString(s, 1, slen)
endif
return this
endif
if first_char == "{" and last_char == "}" then
set i = 1
loop
set c = SubString(s, i, i + 1)
exitwhen c == "," or c == "}"
set i = i + 1
endloop
if c == "}" then
call dd("error: invalid point literal: " + s + ", missing ','")
call exit()
else // if c == "," then
set this.ty = Value_Type.Handle
set x = S2R(SubString(s, 1, i))
set y = S2R(SubString(s, i + 1, slen - 1))
set this.h = Location(x, y)
return this
endif
endif
if SubString(s, 0, 2) == "P(" and last_char == ")" then
set i = S2I(SubString(s, 2, slen - 1))
if not (0 <= i and i <= 15) then
call dd("error: invalid player literal: " + s + ", expected P(0 .. 15)")
call exit()
else
set this.ty = Value_Type.Handle
set this.h = Player(i)
return this
endif
endif
if s == "T" or s == "t" or s == "true" then
set this.ty = Value_Type.Boolean
set this.b = true
return this
elseif s == "F" or s == "f" or s == "false" then
set this.ty = Value_Type.Boolean
set this.b = false
return this
endif
return 0
endmethod
method to_str takes nothing returns string
local string result = ""
local integer ty
set ty = this.ty
if ty == Value_Type.Integer then
set result = result + I2S(this.i)
elseif ty == Value_Type.Real then
set result = result + R2S(this.r)
elseif ty == Value_Type.String then
set result = result + "\"" + this.s + "\""
elseif ty == Value_Type.Handle then
set result = result + I2S(GetHandleId(this.h))
elseif ty == Value_Type.Boolean then
set result = result + bool_to_str(this.b)
else
call unimplemented("Value.to_str Value_Type ty: " + I2S(ty))
endif
return result
endmethod
endstruct
private function value_from_str_strict takes string s returns Value
local Value result = Value.from_str(s)
if result == 0 then
call dd("error: unexpected " + s)
call exit()
endif
return result
endfunction
private function value_from_str_or_null takes string s returns Value
return Value.from_str(s)
endfunction
globals
private Variable array vars
private integer vars_count = 0
endglobals
private struct Variable
string name // all variable names begin with '$' so that they don't collide with user declared ones
integer name_stid
Value_Type ty
// value is stored... wherever blizzard stores global variables' values (the globar variables hashtable probably)
static method create takes string name, Value_Type ty returns thistype
local thistype this = get_var_or_null(name)
if this != 0 then
return this
endif
set this = allocate()
set this.name = name
set this.name_stid = stid(name)
set this.ty = ty
call set_var_exist(this)
set vars_count = vars_count + 1
set vars[vars_count] = this
call call_bc_begin()
if ty == Value_Type.Integer then
set bc[X()] = 0x06040000
set bc[X()] = this.name_stid
set bc[X()] = 0x0C010400
set bc[X()] = 0x00000000
set bc[X()] = 0x11010000
set bc[X()] = this.name_stid
elseif ty == Value_Type.Real then
set bc[X()] = 0x06050000
set bc[X()] = this.name_stid
set bc[X()] = 0x0C010500
set bc[X()] = 0x00000000
set bc[X()] = 0x11010000
set bc[X()] = this.name_stid
elseif ty == Value_Type.String then
set bc[X()] = 0x06060000
set bc[X()] = this.name_stid
set bc[X()] = 0x0C010600
set bc[X()] = empty_string_stid
set bc[X()] = 0x11010000
set bc[X()] = this.name_stid
elseif ty == Value_Type.Handle then
set bc[X()] = 0x06070000
set bc[X()] = this.name_stid
set bc[X()] = 0x0C010700 // use type 02 instead?
set bc[X()] = 0x00000000
set bc[X()] = 0x11010000
set bc[X()] = this.name_stid
elseif ty == Value_Type.Boolean then
set bc[X()] = 0x06080000
set bc[X()] = this.name_stid
set bc[X()] = 0x0C010800
set bc[X()] = 0x00000000
set bc[X()] = 0x11010000
set bc[X()] = this.name_stid
else
call unimplemented("Variable.create, Value_Type ty: " + I2S(ty))
endif
set bc[X()] = 0x27000000
set bc[X()] = 0x00000000
call call_bc_end()
return this
endmethod
method to_str takes nothing returns string
local string result = this.name + " : " + value_type_to_str(ty) + " = "
local integer ty
local Value value
set value = Value.public_allocate()
set value.ty = this.ty
call var_get_value(this)
set ty = this.ty
if ty == Value_Type.Integer then
set value.i = result_integer
elseif ty == Value_Type.Real then
set value.r = result_real
elseif ty == Value_Type.String then
set value.s = result_string
elseif ty == Value_Type.Handle then
set value.h = result_handle
elseif ty == Value_Type.Boolean then
set value.b = result_boolean
else
call unimplemented("Variable.to_str Value_Type ty: " + I2S(ty))
endif
set result = result + value.to_str()
call value.destroy()
return result
endmethod
endstruct
globals
private Value array args
private integer args_count = 0
endglobals
private function args_destroy takes nothing returns nothing
local integer i = 0
loop
exitwhen i >= args_count
call args[i].destroy()
set i = i + 1
endloop
set args_count = 0
endfunction
private function fill_args takes nothing returns nothing
local Value arg
local integer i
call args_destroy()
set i = 2 // T[1] is the function name
loop
exitwhen i > T_count
set args[args_count] = value_from_str_strict(T[i])
set args_count = args_count + 1
set i = i + 1
endloop
endfunction
private function push_args takes nothing returns nothing
local Value arg
local integer ty
local integer i
set i = 0
loop
exitwhen i >= args_count
set arg = args[i]
set ty = arg.ty
if ty == Value_Type.Integer then
set bc[X()] = 0x0C010400
set bc[X()] = arg.i
elseif ty == Value_Type.Real then
set bc[X()] = 0x0C010500
set bc[X()] = real_as_int(arg.r)
elseif ty == Value_Type.String then
set bc[X()] = 0x0C010600
set bc[X()] = stid(arg.s)
elseif ty == Value_Type.Handle then
set bc[X()] = 0x0C020400
set bc[X()] = integer(arg)
set bc[X()] = 0x10010207
set bc[X()] = Value_h_stid
elseif ty == Value_Type.Boolean then
set bc[X()] = 0x0C010800
if arg.b then
set bc[X()] = 0x00000001
else
set bc[X()] = 0x00000000
endif
else
call unimplemented("push_args Value_Type.ty: " + I2S(ty))
endif
set bc[X()] = 0x13010000
set bc[X()] = 0x00000000
set i = i + 1
endloop
endfunction
private /*enum*/ struct Func_Type extends array
static constant integer Non_Function = 0
static constant integer Jass_Function = 1
static constant integer Native_Function = 2
endstruct
private struct Func_Info extends array
static Func_Type func_type
static string func_name
static integer func_name_stid
static integer func_decl_addr // set only when func_type == Func_Type.Jass_Function
endstruct
private function func_info_from_name takes string func_name returns integer
local integer func_name_stid = stid(func_name)
local Func_Info result = 69105
set result.func_name = func_name
if func_name_stid < first_blizzardj_func_stid then
set result.func_type = Func_Type.Native_Function
set result.func_name_stid = func_name_stid
set result.func_decl_addr = 0
// Note: result.func_name_stid might not really be a native function,
// if it isn't, then when we call it we are going to crash!
elseif func_name_stid < second_string_handle_table_entry_stid then
set result.func_type = Func_Type.Jass_Function
set result.func_name_stid = func_name_stid
// Note: result.func_name_stid might not really be a jass function,
// if it isn't, then when we try to find its addr (with the 0x0F) we
// are going to crash!
call call_bc_begin()
set bc[X()] = 0x0F010000
set bc[X()] = func_name_stid
set bc[X()] = 0x11010000
set bc[X()] = var_code_stid
set bc[X()] = 0x0E010400
set bc[X()] = var_code_stid
set bc[X()] = 0x11010000
set bc[X()] = result_integer_stid
set bc[X()] = 0x27000000
set bc[X()] = 0x00000000
call call_bc_end()
set result.func_decl_addr = result_integer - 8
else
set result.func_type = Func_Type.Non_Function
set result.func_name_stid = func_name_stid
set result.func_decl_addr = 0
endif
return result
endfunction
private function call_function takes Func_Info fi, Variable result returns nothing
local integer ty
local Value result_value
local boolean type_error
local integer bc_arg_ty
local Value arg
local integer ret_type
local string func_signature
local string input_signature
local integer offset
local integer instr
local integer opcode
local integer i
// T looks like:
// FunctionName arg1 arg2 ... argn
// or
// &FunctionName arg1 arg2 ... argn
//
// i.e T[1] stores the function's name, T[2] the first argument, etc.
if fi.func_type == Func_Type.Non_Function then
call dd("error: function " + fi.func_name + " does not exist")
call exit()
endif
if result != 0 then
set result_value = Value.public_allocate()
set result_value.ty = result.ty
endif
call fill_args()
if fi.func_type == Func_Type.Native_Function then
call call_bc_begin()
call push_args()
set bc[X()] = 0x15000000
set bc[X()] = fi.func_name_stid
if result != 0 then
set bc[X()] = 0x11000000
set ty = result.ty
if ty == Value_Type.Integer then
set bc[X()] = result_integer_stid
elseif ty == Value_Type.Real then
set bc[X()] = result_real_stid
elseif ty == Value_Type.String then
set bc[X()] = result_string_stid
elseif ty == Value_Type.Handle then
set bc[X()] = result_handle_stid
elseif ty == Value_Type.Boolean then
set bc[X()] = result_boolean_stid
else
call unimplemented("call_function(Func_Type.Jass_Function) Value_Type ty: " + I2S(ty))
endif
endif
set bc[X()] = 0x27000000
set bc[X()] = 0x00000000
call call_bc_end()
else // if fi.func_type == Func_Type.Jass_Function then
set offset = fi.func_decl_addr
if result != 0 then
set ret_type = (Memory[offset/4] * 0x100) / 0x01000000
if result.ty != ret_type then
call dd("error: cannot assign the result (" + value_type_to_str(ret_type) + ") of function " + fi.func_name + " to variable " + result.name + " (" + value_type_to_str(result.ty) + ")")
call exit()
endif
endif
set offset = offset + 8
set type_error = false
set i = 0
loop
set instr = Memory[offset/4]
set opcode = instr / 0x01000000
exitwhen opcode != 0x08
set bc_arg_ty = (instr * 0x100) / 0x01000000
if value_type_to_str(bc_arg_ty) == null then
call unimplemented("bc_arg_ty: " + I2S(bc_arg_ty))
call exit()
endif
if i >= args_count then
set type_error = true
exitwhen true
endif
set arg = args[i]
if bc_arg_ty != arg.ty then
set type_error = true
exitwhen true
endif
set offset = offset + 8
set i = i + 1
endloop
if type_error or i != args_count then
set func_signature = "takes"
set offset = fi.func_decl_addr + 8
loop
set instr = Memory[offset/4]
set opcode = instr / 0x01000000
exitwhen opcode != 0x08
set bc_arg_ty = (instr * 0x100) / 0x01000000
set func_signature = func_signature + " " + value_type_to_str(bc_arg_ty)
set offset = offset + 8
endloop
set input_signature = "given"
set i = 0
loop
exitwhen i >= args_count
set arg = args[i]
set input_signature = input_signature + " " + value_type_to_str(arg.ty)
set i = i + 1
endloop
call dd("error: " + fi.func_name + " mismatched arguments:")
call dd(func_signature)
call dd(input_signature)
call exit()
endif
call call_bc_begin()
call push_args()
set bc[X()] = 0x16000000
set bc[X()] = fi.func_name_stid
set bc[X()] = 0x0B000000 + (args_count * 0x00010000)
set bc[X()] = 0x00000000
if result != 0 then
set bc[X()] = 0x11000000
set ty = result.ty
if ty == Value_Type.Integer then
set bc[X()] = result_integer_stid
elseif ty == Value_Type.Real then
set bc[X()] = result_real_stid
elseif ty == Value_Type.String then
set bc[X()] = result_string_stid
elseif ty == Value_Type.Handle then
set bc[X()] = result_handle_stid
elseif ty == Value_Type.Boolean then
set bc[X()] = result_boolean_stid
else
call unimplemented("call_function(Func_Type.Jass_Function) Value_Type ty: " + I2S(ty))
endif
endif
set bc[X()] = 0x27000000
set bc[X()] = 0x00000000
call call_bc_end()
endif
if result != 0 then
set ty = result.ty
if ty == Value_Type.Integer then
set result_value.i = result_integer
elseif ty == Value_Type.Real then
set result_value.r = result_real
elseif ty == Value_Type.String then
set result_value.s = result_string
elseif ty == Value_Type.Handle then
set result_value.h = result_handle
else // if ty == Value_Type.Boolean then
set result_value.b = result_boolean
endif
call var_set_value(result, result_value)
call result_value.destroy()
endif
endfunction
private function call_function_by_name takes string func_name, Variable result returns nothing
local Func_Info fi = func_info_from_name(func_name) // func_name is without &
call call_function(fi, result)
endfunction
globals
public string chat_string
public player trigger_player
endglobals
private function main takes nothing returns nothing
local string first_char
local string func_name
local string var_name
local Variable var
local integer value_type
local boolean is_var_assignment
local Value value
local Func_Info fi
local integer ty
local integer i
set error = false
set chat_string = GetEventPlayerChatString()
set trigger_player = GetTriggerPlayer()
call ForForce(bj_FORCE_PLAYER[0], function tokenize)
set first_char = SubString(T[1], 0, 1)
if first_char != "$" and first_char != "&" then
return
endif
if first_char == "&" then
set func_name = SubString(T[1], 1, StringLength(T[1]))
call call_function_by_name(func_name, /*no result:*/ 0)
return
endif
// first_char == "$"
set var_name = T[1]
if T_count == 1 then
set var = get_var_or_null(var_name)
if var == 0 then
call dd("error: use of undeclared variable " + var_name)
call exit()
else
call dd(var.to_str())
return
endif
endif
set is_var_assignment = false
if T[2] == ":" then // variable declaration
set value_type = value_type_from_str(T[3])
if value_type == Value_Type.Whoknows then
call dd("error: unknown type " + T[3] + ", expected: integer, real, string, handle, boolean, point or player")
call exit()
endif
set var = get_var_or_null(var_name)
if var != 0 then
call dd("error: redeclaration of variable " + var.name + " : " + value_type_to_str(var.ty))
call exit()
endif
set var = Variable.create(T[1], value_type)
if T_count == 3 then
return
endif
if T[4] == "=" then
set is_var_assignment = true
// T looks like:
// $var : <type> = ...
// shift the tokens left so that it looks like:
// $var = ...
//
set i = 4
loop
exitwhen i > T_count
set T[i - 2] = T[i]
set i = i + 1
endloop
set T_count = T_count - 2
endif
elseif T[2] == "=" then
set is_var_assignment = true
set var = get_var_or_null(var_name)
if var == 0 then
call dd("error: use of undeclared variable " + var_name)
call exit()
endif
endif
if is_var_assignment then
if T_count == 2 then
call dd("error: expected a literal or function call after '='")
call exit()
endif
set value = value_from_str_or_null(T[3])
if value != 0 then
call var_set_value(var, value)
else
set fi = func_info_from_name(T[3])
if fi.func_type == Func_Type.Non_Function then
call dd("error: unexpected " + T[3])
call exit()
endif
// T looks like:
// $var = Function_Name ...
// shift the tokens left so that it looks like:
// Function_Name ...
//
set i = 3
loop
exitwhen i > T_count
set T[i - 2] = T[i]
set i = i + 1
endloop
set T_count = T_count - 2
call call_function(fi, var)
call dd(var.to_str())
endif
return
endif
call dd("error: syntax error: " + chat_string)
call exit()
endfunction
private function init takes nothing returns nothing
local trigger t
call result_vars_and_funcs_stid_init()
call Value_h_stid_init()
call value_type_to_string_init()
call Variable.create("$i", Value_Type.Integer)
call Variable.create("$r", Value_Type.Real)
call Variable.create("$s", Value_Type.String)
call Variable.create("$h", Value_Type.Handle)
call Variable.create("$b", Value_Type.Boolean)
set t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(1), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(2), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(3), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(4), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(5), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(6), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(7), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(8), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(9), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(10), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(11), "", false)
// call TriggerRegisterPlayerChatEvent(t, Player(12), "", false)
call TriggerAddAction(t, function main)
endfunction
public function list_vars takes nothing returns nothing
local Variable var
local integer i
set i = 1
loop
exitwhen i > vars_count
set var = vars[i]
call dd(var.to_str())
set i = i + 1
endloop
if vars_count > 16 then
call dd("hint: open the Log (F12)")
endif
endfunction
endlibrary
Edit: Attached the "big list" of native wrappers. The safe version of a native starts with an 's':
CreateUnit => sCreateUnit
Attachments
Last edited: