Is there a way to "test" what kind of Data Type a variable is?

I Think I solved the kind of Data Problem!

Here is a link with a working example of my solution. By using this handler. My code can always identify with precsion(ish) the exact kind of Data you typed in the console.

Hmm. Apparently ChoiceScript recognizes everything as a sequence of chars, even numbers. :frowning:.
Now I will work in a way to convert any variable value into a string! (And vice-versa)

Now, the code:

Checking the input:

*label check_input
*params this_value 


*comment now check value input 
*comment is boolean?
*if (this_value = true) or (this_value = false)
	*set kind_of_data "BOOLEAN"
	*page_break END OF TEST! ${this_value} Is a boolean
	*return 

*comment is number?
*gosub is_number (this_value) "truth" "this_value"
*if truth
	*set kind_of_data "NUMBER"
	*page_break END OF TEST! ${this_value} Is a number
	*return 

*comment is string?
*comment This is a String 
*set kind_of_data "STRING"
*page_break END OF TEST! ${this_value} is a String 

*return 

Check Input:

Check if is a number (Now working with negatives and decimals!)

*comment === FUNCTION IS_NUMBER =========
*comment ------------------------------
*comment Determines if a given string is a number.
*comment ------------------------------
*comment 	params:
*comment		mystring(string): the value to test
*comment		
*comment		retrieve(address string): the name (address) 
*comment		of the variable to receive the return.
*comment ------------------------------
*comment	returns: 
*comment		(retrieve) true if p_string is a number, else
*comment		false
*comment
*comment		return(address): optional returning for
*comment 		numbers of negligible values.
*comment =======================
*comment Auxiliary IS _THIS A NUMBER?? ==
*comment =======================
*comment *gosub is_number (string) (return)

*label is_number
*params mystring retrieve
*temp isnumber true
*temp sanitized ""
*temp p_address "void"
*temp count_param (param_count)

*temp point 0
*temp i 0
*temp j 1

*if count_param > 2
	*set p_address param_3
	*goto continue_is_number
*label continue_is_number
*comment checking if negative
*if (("${mystring#1}" = ("-")) and ((length(mystring)>1)))
	*set sanitized "-"
	*set j+1
	*goto For_is_number	
*label For_is_number
*comment For (i=0; i<10; i++)
*comment For (j=1; j<length(param_1); j++)
*if (j <= (length(mystring)))
	*if isnumber
		*comment check if number 4 sure
		*goto For_is_number_j
	*else
		*comment NOT a NUMBER!
		*set {retrieve} false
		*return

*else
	*comment NOT A NUMBER!
	*set {retrieve} false
	*return
	
	
*label For_is_number_j
*if i < 10
	*if ("${mystring#j}" = ("${i}"))
		*comment DEBUG: FOUND NUMBER i ${i} at J ${j} in "${mystring#j}"
		*set sanitized (sanitized&("${i}"))
		*set j +1
		*set i 0
		*if j > (length(mystring))
			*comment the string IS A NUMBER!
			*set {retrieve} true
			*return
		*comment lets check next char in da string			
		*goto For_is_number
	*if ("${mystring#j}" != ("${i}"))
		*comment lets compare with 1 to 9.
		*set i +1
		*goto For_is_number_j
*if ("${mystring#j}" = ("."))
	*set point +1
	*if (j = (length(mystring)))
		*set isnumber false
		*set {retrieve} false
		*return
	*if ((("${mystring#(j+1)}") = "0") and ((j+2) > (length(mystring))))
		*set {p_address} (sanitized)
		*set {retrieve} true
		*return
	*if (((("${mystring#(j+1)}") = "0") and (("${mystring#(j+2)}") = "0")))
		*set {p_address} (sanitized)
		*set {retrieve} true
		*return
	*if point > 1
		*set isnumber false
		*set {retrieve} false
		*return
	*else
		*set sanitized (sanitized&("."))
		*set j +1
		*set i 0
		*goto For_is_number
		
*else
	*comment NOT a NUMBER!
	*set isnumber false
	*set {retrieve} false
	*return
			

I’m not convinced there is such a thing as a variable type. For example:

*temp var1 false
*temp var2 “false”
*temp var3 123
*temp var4 “123”

var1 is ${var1}
var2 is ${var2}
var3 is ${var3}
var4 is ${var4}

Gives you:

var1 is false
var2 is false
var3 is 123
var4 is 123

I’ve had no trouble setting a ‘boolean’ value (defined as *temp var1 false) to be a string value (bad practice I know but…)

I suspect you may have to examine the contents but even then I don’t know how you distinguish false and “false”

Purely as a matter of interest, why do you need to know in advance?

Edit: the statement *if (var) will barf with “Neither true nor false: var” if the value is neither true nor false, but it can be the string “true” or the string “false”

2 Likes

I am messing around with string manipulation. Trying to er… emulate dynamic allocation by simulating a [virtual] disk inside a string. :man_facepalming:t5:

One of the issues I’ve found is how to identify properly what is being stored (A number? A boolean? A string) since each data type occupy a pre-determined (although scalable) size in (not!bits)… er a [chain of chars].

Accessed by fixed addresses easily calculable by a set of flags and indexers that are stored in reserved addresses of the er… “disk” itself. :man_facepalming: :person_facepalming:t5: ::

So I can exploit the same mechanics we used to use in pointer’s arithmetic to “jump around” back in my C programming days.
Data that is stored, load and updated without having to read the entire damn string everytime one’s need to retrieve a [number] stored at [position25*size_of_number] between the characters (1) to (16) in said sequence of characteres.

hmm ok

I cooked a example of what I mean by “test” what kind of Data Type is being read:
check_input Function

try to input some number (must be integer though), random name, “false” or “true” and see the result.
It does not work with decimals though, as my (still to be coded) handler converts (by the grace of God and Anime) any float or negative number to a string representing an integer with reserved flag addresses in said string to identify if the stored number id is negative or fractional.

It works by:

*temp my_var ""
*temp return ""
*temp truth false

*label begin 
This is a test for a Data Type identifier. In theory it is capable to identify the content of one or more variables as:

1 - Numbers (currently works only with integers though)
*line_break
2 - Boolean (Just "true" or "false")
*line_break
3 - Strings (anything rl)
*line_break

Do you wanna test?
*input_text my_value
*gosub check_input my_var "return"


*page_break END OF TEST return

check_input (arguments):


*label check_input
*params 
*temp this_variable param_1
*temp returning param_2
*temp alfabeta "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"

*comment variables can't begin with numbers
*gosub is_number (this_variable#1) returning
*if truth
	*bug INVALID VARIABLE NAME, MUST START WITH A LETTER
	*return 
	
*comment check if the variable begins with a letter
*gosub is_letter (this_variable#1) returning
*if truth = false
	*bug INVALID VARIABLE NAME, MUST START WITH A LETTER
	*return 
	
*gosub compare_strings (this_variable) "alfabeta" returning
*if truth = false
	*bug INVALID VARIABLE NAME. ${this_variable} "${this_variable#j}" should not have operators or other than simple unaccented letters and numbers.
	*return 

FUNCTIONS:
Is_number (args), Is_letter(args) etc…


*comment === FUNCTION IS_NUMBER =========
*comment ------------------------------
*comment Determines if a given string is a number.
*comment ------------------------------
*comment 	params:
*comment		mystring(string): the value to test
*comment		
*comment		retrieve(address string): the name (address) 
*comment		of the variable to receive the return.
*comment ------------------------------
*comment	returns: 
*comment		(retrieve) true if p_string is a number, else false
*comment =======================
*comment Auxiliary IS _THIS A NUMBER?? ==
*comment =======================
*comment *gosub is_number (string) (return)

*label is_number
*params mystring retrieve
*temp isnumber true
*temp i 0
*temp j 1
*label For_is_number
*comment For (i=0; i<10; i++)
*comment For (j=1; j<length(param_1); j++)
*if (j <= (length(mystring)))
	*if isnumber
		*comment check if number 4 sure
		*goto For_is_number_j
	*else
		*comment NOT a NUMBER!
		*set {retrieve} false
		*return

*else
	*comment NOT A NUMBER!
	*set {retrieve} false
	*return
	
	
*label For_is_number_j
*if i < 10
	*if ("${mystring#j}" = ("${i}"))
		*comment DEBUG: FOUND NUMBER i ${i} at J ${j} in "${mystring#j}"
		*set j +1
		*set i 0
		*if j > (length(mystring))
			*comment the string IS A NUMBER!
			*set {retrieve} true
			*return
		*comment lets check next char in da string			
		*goto For_is_number
	*if ("${mystring#j}" != ("${i}"))
		*comment lets compare with 1 to 9.
		*set i +1
		*goto For_is_number_j
*else
	*comment NOT a NUMBER!
	*set isnumber false
	*set {retrieve} false
	*return
			


*comment === FUNCTION IS_LETTER =========
*comment ------------------------------
*comment Determines if a given string is a alphabetical l337.
*comment ------------------------------
*comment 	params:
*comment		my_string(string): the value to test
*comment		
*comment		compare(address string): the name (address) 
*comment		of the string we will use to compare it to

*comment		
*comment ------------------------------
*comment	returns: 
*comment		(retrieve) true if p_string is a letter, 
*comment 		else false
*comment =======================
*comment Auxiliary IS _THIS A LETTER?? ==
*comment =======================
*comment *gosub is_letter (string) (return)

*label is_letter
*params
*temp letters "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"

*gosub compare_strings (param_1) "letters" param_2

*return 


*comment === FUNCTION GET(value of char) =========
*comment ------------------------------
*comment Get position of a char inside a string.
*comment ------------------------------
*comment 	params:
*comment		mystring(string): the char to compare
*comment		
*comment		retrieve(address string): the name (address) 
*comment		of the variable to receive the return.
*comment ------------------------------
*comment	returns: 
*comment		(retrieve) true if p_string is a letter, 
*comment 		else false
*comment =======================
*comment Auxiliary IS _THIS A LETTER?? ==
*comment =======================
*comment *gosub is_letter (string) (return)
*label get_value_of_char
*params 

*gosub compare_strings (param_1) param_2 "truth" param_3

*return 

*label compare_strings
*params 
*temp s_tring1 param_1 
*temp s_tring2 {param_2} 
*temp retrieve param_3
*temp isletter true

*temp i 1
*temp j 1
*label For_compare_string
*comment For (i=0; i<10; i++)
*comment For (j=1; j<length(param_1); j++)
*if (j <= (length(s_tring1)))
	*if isletter
		*comment check string at j position
		*goto For_compare_string_j
	*else
		*comment NOT in the string set!
		*set {retrieve} false
		*return

*else
	*comment NOT content of string!
	*set {retrieve} false
	*return
	
	
*label For_compare_string_j
*if i <= (length(s_tring2))
	*if (s_tring1#j) = (s_tring2#i)
		
		*comment if ("${s_tring1#j}" = (${s_tring2#i})
		*comment DEBUG: FOUND char i ${i} at J ${j} in "${s_tring1#j}"
		*set j +1
		*set i 1
		*if j > (length(s_tring1))
			*comment the string IS content!
			*set {retrieve} true
			*return
		*comment lets check next char in da string			
		*goto For_compare_string
	*if (s_tring1#j) != (s_tring2#i)
		*comment lets compare with 1 to string-end.
		*set i +1
		*goto For_compare_string_j
*else
	*comment NOT content!
	*set isletter false
	*set {retrieve} false
	*return

:frowning: :confused:

Sorry for the messy code. I wrote it just now…

People always diss regular expressions but, man, when you need them you really need them.

Random thought:

Could you predefine each letter as a variable with a type for static lookup. (you can’t start a variable name with a number so you would have to prepend a letter, say ‘a’)
e.g.

*comment 1 is letter, 2 is a number
*create aa 1
*create ab 1
*create ac 1

*create a0 2
*create a1 2
etc

This way, you could check each character for number/letter with a simple lookup. Might be a bit quicker.

Of course, variable names are case-insensitive, so if you care about capitalisation it won’t work.

Edit: I now realise this won’t work for names like a- a! etc. You would have to filter out the illegal characters first so you might not gain much.

2 Likes

Hmm. It would make the checking out quickly, but would also use a awful number of lines to declare all these variables.

Well reminded. Variable names in choiceScript must always be lower case, right?

Guess I have a function that converts all the characters of a string to lower case lying somewhere in my dropbox. I guess its time to brush out the dust of my old subroutines…

You can define a variable as upper case but it treats it as lower.
*temp MYVAR
*temp myvar
will result in a duplicated variable name error.

(as an old C programmer myself, I typically define ‘constants’ in upper case and variables in lower case)

1 Like

It’s actually easier to filter illegal characters as they are not part of the reserved set of characters the substring is allowed to use. So anytime my loop came to the end of the list of permitted characters whithout a positive match. It will return a false value. (And the position of the illegal symbol, if so I wish)

That’s is the easyer part. The hard part is when I am dealing with strings that may have reserved characters in it.
U know… Like when you save something with a “^” while using twigger_fluff just to mess with their syntax interpreter he he he. :supervillain:

Since choicescript technically don’t have upper case as a option for naming variables. I guess I just will bruteforce any possible variable the user create to lower case before saving it in the “disk”.

(Hmm a prefix naming convention for constants in Choisescript code to make it readable is not a bad idea… whe kind of use a sufix convention for arrays and loops sometimes…)

AFAIK it’s not possible to write to a specific position. If you *create my_string "abcd", you can read a specific position (e.g., ${my_string#3} = c), but attempting to write (*set my_string#3 "f") results in an error.

So you need to parse the whole string when updating regardless.

What are you planning on using dynamic allocation for? Is it just for fun, or do you have a practical use case in mind? If it’s for a game, there’s probably a simpler way get the job done.

1 Like

That’s true. But when you parse the whole string just to retrieve a simple substring you will experience a lot of lag due inefective way data is stored. Data blocks with fixed size in chars kinda fix that.

Its just for a framework. I intent on use it as a base to implement more complex Data Structures as well as making tuples easier and more intuitive to use in games that make heavy use of it. (Complex games even if text based are a pain to code without it)

A little context: often while I was closing the game mechanics of a project, I always had these problems with long lists of variables…

for example: Suppose you are coding a Sailing Ship game, like Daikoukai Jidai, where you can capture and add other ships to your active fleet.

The fleet management aspect of such game occurs in such way that, where you should be a Commodore of a sailing Fleet, your game will have to deal with a scalable list of ships each with their own set of characteristics.

But, how can you store and acess the info of any freaking possible ship you capture whithout having to pre *create any and all possible ships you will be able to capture, buy or build in game?

The original Daikoukai Jidai deal with this issue by limiting the number of ships you can handle whithout an in-game explanation. But still there is still the issue of keeping track of all other ships active or inative (moored, being build, etc) player’s ships in the world.

And how to deal with the data of a fleet with 20 ir more ships sailing all iver the world? Keeping tracking of all their stats and stuff?

Another example, you are coding a “Lord of The Rings” style fantasy game, and your party just meet a Orc Warband. Will you declare the variables for each and any individual orc in said band? They are persistent in-game? If your player character lures them out of your trail by crossing a creek, how will the game keep their variables active for use in other future scenes?

Will you code it all manually? For any possible creature hou may encounter during any possible route your party take to reach Mordor?

Another example, in [エイティーズ ] An 日本の物語 !! , a game where the player should deal with the management of a team of Designers and Software Engineers, I had to deal with designing a way to keep track of dozen of employees, but since the player would only deal with a small number of them (large teams are possible, but highly unadvisable in software development), I needed a way to “store” and keep track of a undefined number of employees.

I decided by creating a simple function that “generates”, based upon a seed, said undefined number of employable characters procedurally to ease the burden of having to write any and all these NPCs in rhe actual Scene file.

The problem is due to how Choicescript deal with *temp variables, I can set as much as I like of them in one scene, but as soon as the game change to the next scene, by *finish or *goto_scene command, I lost any and all temp variable created.

So how deal with this problem?

Currently we can have respite in being able to *create_array in the Startup Page. But how I define the size of the arrays that will receive the data of all the current and ex-
Currently we can have respite in being able to *create_array in the Startup Page. But how I define the size of the arrays that will receive the data of all the current and ex-employees that the MC interacted both in Nintendo, Sega and in his own company?

Now think about it, what if I could simple *define a Class as “Employee” with all the information I will need and *store it in a structured linked list or any other structure that would give ne the ability to “save” theses little guys in a global variable for the game to use if and only if necessary?

These are a couple of the reasons I begin working in this solution. I simply want to create complex games whithout having to reinvent the wheel and code myself to exaustion every time I need a way to deal with reading long lists of variables whithout causing lag due to the program sweeping each char inside said list to retrieve a little strip inside it.

Imagine the ability to left all this work to a predefined set of function that will administrate all this data for you. Giving the choiceScript author the ability to only focus in the story and leaving the heavy lifting of coding for the Data handlers deal??

But in all these examples, don’t you know beforehand what type of data you’re using? Did I miss something?

1 Like

Yes, I (the human programmer) always know if myself are declaring a variable for a number, a string or a boolean.

I have been struggling in teaching my program to receive a variable, look at its content and:

1 - look at it and identify if what it is receiving is a variable with a number, a string or a boolean

2 - check if the variable is already declared. If not put it in the list of stored variables by alfabetical order.

3 - convert the value of this variable into a string of fixed size. (It store booleans in sequences of one char of “0” or “1”; numbers in chains of 16 chars; and strings in chains of 30 or more chars)

4 - insert this edited string in a ‘address’ fixed inside a bigger data storage global string that I call “disk” for shortening.

5 - update the disk string with a couple flags to ease the reading of any data stored in the string.

6 - (…) do Data Processing Stuff

7 - Profit!?

I am currently working on parts 1 and 3. My program, in order to store it properly. Need to “learn” how to identify what kind of variable it is storing in the “disk” in order to er… Do the (pseudo) memory allocation thingy my data acess functions use to navigate the “disk” by acessing directly the position where the contents of the variable are stored. (The data address position value is always stored 4 chars to the right of where the name of the variable is stored in the alfabetical list of declared variables stored in the same disk. So the program reads only what he needs in order to acess the stored Data and ignores the rest of the string.)

Example of the “disk” string:

…|1023048*********|111049***** …

The “|” symbol is the point where the reader ‘jumps’ to begin reading the content of the Data Block. Stopping at the “*” symbol.

This means that: “1023048*” means [1][0] 23048 where the first 2 chars to the left are flags meaning true and false

True [1] that its a fractional number and [0] means that its signal is positive.

So the output will be = 230.48

This is just one example of how it (would) work…

I’d urge you to take a look at the code for 🧰 [TOOL] CSLIB - ChoiceScript Library, if you haven’t already. It already provides some similar functionality (is_number) and uses similar concepts (e.g. an alphabetical lookup). Plus, if you find yourself implementing something that it doesn’t provide, it’d be great if you’d consider contributing it to that project, as the aim of CSLIB is not overly dissimilar to your words below:

Imagine the ability to left all this work to a predefined set of function that will administrate all this data for you. Giving the choiceScript author the ability to only focus in the story and leaving the heavy lifting of coding for the Data handlers deal??

I do think there’s limitations to what you can do at runtime though. Static analysis has more promise, at least for global variables (temps are more difficult), as you can infer the type from the initial value (though there are some edge cases, like the boolean case StephenHart mentioned). That’s probably not helpful to you here though.


Personally, now we have create_array, I find most of my data structure usage is covered by creating arrays that are big enough to hold any foreseeable amount of values. But I long since accepted that bullying ChoiceScript into doing anything too complicated, whilst very fun, is rarely worth it in practice (but that’s just a personal opinion of course!).


A final thought: is there anything stopping you from storing the associated data type in the “memory”? If you’re asking the user for numerical input, a simple implementation could be storing “num:${data}”.

2 Likes

Wow! thanks friend! And thanks for the link. CS_Lib is a blessing! But I couldn’t locate the alphabetical ordering function. Is it in the CS_string scene?

I would be honored, but don’t CS_Lib asks the user to create a return variable in the startup and have a certain way to operate?

I Agree wholeheartedly. But sometimes we have to. Is not a matter of choice. (no pun intended)

Bingo! That is pretty much how I will store it. There is an address inside the “memory” string where I store the Names of the variables and an one digit tag that identify the kind of Data stored. (1 for boolean, 2 for number and 3 for string). followed by a number that marks the position of its data content inside the string.

I took inspiration in Gödel numbers. When you multiply the number stored in the “position” tag for the size of the data in the type tag you get the exact position inside the string where it is stored.

2 Likes

Sorry, miscommunication there, I was referring to how the LOWERCASE function (in cslib_string) uses a “lookup table” similar to what Stephen suggested: Is there a way to "test" what kind of Data Type a variable is? - #4 by StephenHart

There’s no sorting functions in cslib yet, though contributions would again be very welcome.

Yes, it requires a single variable definition, but it looks like from your first post you’re already doing something similar (albeit with temps not globals). There’s some contribution guidelines at cslib/CONTRIBUTING.md at main · ChoicescriptIDE/cslib · GitHub. But don’t let them put you off, if you’re ever keen to make a contribution I’d be more than happy to guide you through them.

1 Like

Hmm. Maybe I solved? hm, yes! I T-Think I (maybe?) solved the “kind of Data” Problem!

By clicking in this: link

You will encounter a working example of my solution.

Using this handler. My code can always identify with precision(ish) the exact kind of Data you typed in the console. Try out!

Very nice. I like that it can distinguish between false and “false”.

I found only one way to break it (sorry, old QA habits die hard)

3.14 is a number

but

3.14159265358979323846264338327950288419716939937510
58209749445923078164062862089986280348253421170679
82148086513282306647093844609550582231725359408128
48111745028410270193852110555964462294895493038196
44288109756659334461284756482337867831652712019091
45648566923460348610454326648213393607260249141273
72458700660631558817488152092096282925409171536436
78925903600113305305488204665213841469519415116094
33057270365759591953092186117381932611793105118548
07446237996274956735188575272489122793818301194912
98336733624406566430860213949463952247371907021798
60943702770539217176293176752384674818467669405132
00056812714526356082778577134275778960917363717872
14684409012249534301465495853710507922796892589235
42019956112129021960864034418159813629774771309960
51870721134999999837297804995105973173281609631859
50244594553469083026425223082533446850352619311881
71010003137838752886587533208381420617177669147303
59825349042875546873115956286388235378759375195778
18577805321712268066130019278766111959092164201989

is a string.

I’m guessing there is a length limit somewhere?

Is your choicescript code available or is it still a bit rough for public consumption?

1 Like

Actually, everything the function receives is treated like a string. I simply used some rules to make it “interpret” some strings as numbers (f ex: it begins with a “-”? Has a “.”? Then is a number! Its a 4 character string? Is true? 5 char string? false?) He also recognize 0 as false and 1 as true. It wasn’t my intention though. I guess the choicescript built in interpreter has a will on itself for certain values?

I re-posted the updated version in the first post of the thread. Beware its horribly messy and full of nonsensical comments! :smiley:

There is more code, but its untested. In principle, my function was supposed to accept numbers in the interval of -999999999999 to 999999999999999 only. Until I learn how to wide this universe of possibilities by sacrificing performance in detriment of heavy charactere character manipulation. And self editing blocks of data (by abusing a metadata block saved in the very string where the numbers are stored) to control the acess points of these growing blocks of data-types…

1 Like