initial
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// GameMonkey highlighter written by Matthew Riek and Greg Douglas
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// language name
|
||||
|
||||
Language: GameMonkey Script
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// default file filter
|
||||
// note: if more than one extension is associated, eg:
|
||||
// C/C++ files (*.c,*.cpp,*.h,*.hpp)|*.c;*.cpp;*.h;*.hpp
|
||||
|
||||
Filter: GameMonkey Script files (*.gm)|*.gm
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// help file which will be invokend when F1 is pressed
|
||||
|
||||
HelpFile:
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// language case sensitivity
|
||||
// 0 - no
|
||||
// 1 - yes
|
||||
|
||||
CaseSensitive: 1
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// comment type: LineComment - comment to the end of line
|
||||
// BlockCommentBeg - block comment begin, it could be
|
||||
// multiline
|
||||
// BlockCommentEnd - block comment end
|
||||
|
||||
LineComment: //
|
||||
BlockCommentBeg: /*
|
||||
BlockCommentEnd: */
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// identifier characters
|
||||
// note: characters shouldn't be delimited, except arrays
|
||||
// array of chars could be defined as from_char..to_char
|
||||
|
||||
IdentifierBegChars: a..z A..Z _
|
||||
IdentifierChars: a..z A..Z _ 0..9
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// numeric constants begin characters
|
||||
// note: characters shouldn't be delimited, except arrays
|
||||
// array of chars could be defined as from_char..to_char
|
||||
// number always starts with 0..9 except when NumConstBeg
|
||||
// defines other
|
||||
|
||||
NumConstBegChars: 0..9
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// numeric constants characters
|
||||
// note: characters shouldn't be delimited, except arrays
|
||||
// array of chars could be defined as from_char..to_char
|
||||
// number always starts with 0..9 except when NumConstBeg
|
||||
// defines other
|
||||
|
||||
NumConstChars: 0..9 abcdefxABCDEFX .
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// escape character
|
||||
|
||||
EscapeChar:
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// keyword table
|
||||
// note: delimited with spaces, lines could be wrapped
|
||||
// you may divide keywords into two groups which can be
|
||||
// highlighted differently
|
||||
|
||||
// op codes
|
||||
|
||||
KeyWords1: if
|
||||
else
|
||||
for
|
||||
foreach
|
||||
in
|
||||
and
|
||||
or
|
||||
while
|
||||
dowhile
|
||||
function
|
||||
return
|
||||
continue
|
||||
break
|
||||
null
|
||||
global
|
||||
local
|
||||
member
|
||||
table
|
||||
true
|
||||
false
|
||||
this
|
||||
|
||||
KeyWords2:
|
||||
|
||||
KeyWords3:
|
||||
debug
|
||||
typeId
|
||||
typeName
|
||||
typeRegisterOperator
|
||||
typeRegisterVariable
|
||||
sysCollectGarbage
|
||||
sysGetMemoryUsage
|
||||
sysGetDesiredMemoryUsageHard
|
||||
sysGetDesiredMemoryUsageSoft
|
||||
sysSetDesiredMemoryUsageHard
|
||||
sysSetDesiredMemoryUsageSoft
|
||||
sysSetDesiredMemoryUsageAuto
|
||||
sysTime
|
||||
doString
|
||||
globals
|
||||
threadTime
|
||||
threadId
|
||||
threadAllIds
|
||||
threadKill
|
||||
threadKillAll
|
||||
thread
|
||||
yield
|
||||
exit
|
||||
assert
|
||||
sleep
|
||||
signal
|
||||
block
|
||||
stateSet
|
||||
stateSetOnThread
|
||||
stateGet
|
||||
stateGetLast
|
||||
stateSetExitFunction
|
||||
tableCount
|
||||
tableDuplicate
|
||||
print
|
||||
format
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// string delimiter: StringBegChar - string begin char
|
||||
// StringEndChar - string end char
|
||||
// MultilineStrings - enables multiline strings, as perl
|
||||
// has it
|
||||
|
||||
StringBegChar: "`'
|
||||
StringEndChar: "`'
|
||||
MultilineStrings: 0
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// use preprocessor: 0 - no
|
||||
// 1 - yes
|
||||
// note: if yes, '#' and statements after it will be
|
||||
// highlighted with Preprocessor defined colors
|
||||
|
||||
UsePreprocessor: 0
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// highlight line: 0 - no
|
||||
// 1 - yes
|
||||
// note: if yes, current line will be highlighted
|
||||
|
||||
CurrLineHighlighted: 0
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// colors
|
||||
// note: first value is foreground, second is background color
|
||||
// and third (optional) represents font attribute:
|
||||
// B - bold
|
||||
// I - italic
|
||||
// U - underline
|
||||
// S - strike out
|
||||
// attributes can be combined: eg. B or BI
|
||||
// as value, it could be used any standard windows color:
|
||||
// clBlack, clMaroon, clGreen, clOlive, clNavy,
|
||||
// clPurple, clTeal, clGray, clSilver, clRed, clLime,
|
||||
// clYellow, clBlue, clFuchsia, clAqua, clLtGray,
|
||||
// clDkGray, clWhite, clScrollBar, clBackground,
|
||||
// clActiveCaption, clInactiveCaption, clMenu, clWindow,
|
||||
// clWindowFrame, clMenuText, clWindowText, clCaptionText,
|
||||
// clActiveBorder, clInactiveBorder, clAppWorkSpace,
|
||||
// clHighlight, clHighlightText, clBtnFace, clBtnShadow,
|
||||
// clGrayText, clBtnText, clInactiveCaptionText,
|
||||
// clBtnHighlight, cl3DDkShadow, cl3DLight, clInfoText,
|
||||
// clInfoBk
|
||||
// as value, it could be used hex numeric constant too:
|
||||
// $BBGGRR - BB: blue, GG: green, RR: red, eg: $FF6A00
|
||||
|
||||
SpaceCol: $00D0D0D0 clNavy
|
||||
Keyword1Col: clYellow clNavy
|
||||
Keyword2Col: clYellow clNavy
|
||||
Keyword3Col: $00FFC0C0 clNavy
|
||||
IdentifierCol: $00D0D0D0 clNavy
|
||||
CommentCol: $00C0C000 clNavy
|
||||
NumberCol: clLime clNavy
|
||||
StringCol: clLime clNavy
|
||||
SymbolCol: clWhite clNavy
|
||||
PreprocessorCol: $00FFFF40 clNavy
|
||||
SelectionCol: clNavy $00D0D0D0
|
||||
CurrentLineCol: clBlack clYellow
|
||||
|
||||
OverrideTxtFgColor: 0
|
||||
BlockAutoindent: 0
|
||||
BlockBegStr:
|
||||
BlockEndStr:
|
||||
MatchedBracesCol: $008080FF clNavy
|
||||
@@ -0,0 +1,2 @@
|
||||
LANGSPEC:GM.SPC
|
||||
KEYWORDS:GM.KEY
|
||||
@@ -0,0 +1,80 @@
|
||||
[-COMMENT-:GLOBAL]
|
||||
# GM GameMonkey LANGUAGE KEYWORDS FILE FOR CRIMSON EDITOR
|
||||
# FIRST EDITED BY Greg
|
||||
|
||||
[KEYWORDS0:GLOBAL]
|
||||
if
|
||||
else
|
||||
for
|
||||
foreach
|
||||
in
|
||||
and
|
||||
or
|
||||
while
|
||||
dowhile
|
||||
function
|
||||
return
|
||||
continue
|
||||
break
|
||||
null
|
||||
global
|
||||
local
|
||||
member
|
||||
table
|
||||
true
|
||||
false
|
||||
this
|
||||
|
||||
[KEYWORDS1:GLOBAL]
|
||||
debug
|
||||
typeId
|
||||
typeName
|
||||
typeRegisterOperator
|
||||
typeRegisterVariable
|
||||
sysCollectGarbage
|
||||
sysGetMemoryUsage
|
||||
sysSetDesiredMemoryUsageHard
|
||||
sysSetDesiredMemoryUsageSoft
|
||||
sysSetDesiredMemoryUsageAuto
|
||||
sysGetDesiredMemoryUsageHard
|
||||
sysGetDesiredMemoryUsageSoft
|
||||
sysTime
|
||||
doString
|
||||
globals
|
||||
threadTime
|
||||
threadId
|
||||
threadAllIds
|
||||
threadKill
|
||||
threadKillAll
|
||||
thread
|
||||
yield
|
||||
exit
|
||||
assert
|
||||
sleep
|
||||
signal
|
||||
block
|
||||
stateSet
|
||||
stateSetOnThread
|
||||
stateGet
|
||||
stateGetLast
|
||||
stateSetExitFunction
|
||||
tableCount
|
||||
tableDuplicate
|
||||
print
|
||||
format
|
||||
|
||||
[KEYWORDS2:GLOBAL]
|
||||
|
||||
[KEYWORDS3:GLOBAL]
|
||||
|
||||
[KEYWORDS4:GLOBAL]
|
||||
|
||||
[KEYWORDS5:GLOBAL]
|
||||
|
||||
[KEYWORDS6:GLOBAL]
|
||||
|
||||
[KEYWORDS7:GLOBAL]
|
||||
|
||||
[KEYWORDS8:GLOBAL]
|
||||
|
||||
[KEYWORDS9:GLOBAL]
|
||||
@@ -0,0 +1,34 @@
|
||||
# GM GameMonkey LANGUAGE SPECIFICATION FILE FOR CRIMSON EDITOR
|
||||
# FIRST EDITED BY Greg
|
||||
|
||||
$CASESENSITIVE=YES
|
||||
$DELIMITERS=~`!@#$%^&*()-+=|\{}[]:;"',.<>/?
|
||||
|
||||
# There are currently no preprocessor commands in GM, but there maybe sometime
|
||||
# $KEYWORDPREFIX=#
|
||||
|
||||
# Bit of a hack to highlight binary constants
|
||||
# Will need to set Variable color to match constants
|
||||
#$BINARYMARK=0x Be nice if this was available
|
||||
# The below lines don't behave as expected
|
||||
#$VARIABLEPREFIX=0b
|
||||
#$SPECIALVARIABLECHARS=0b
|
||||
|
||||
$HEXADECIMALMARK=0x
|
||||
|
||||
$ESCAPECHAR=\
|
||||
$QUOTATIONMARK1="
|
||||
$QUOTATIONMARK2='
|
||||
|
||||
# In the current version of CrimsonEditor (3.51), the $QUOTATIONMARK3
|
||||
# tag isn't supported, but maybe it will be in the future...
|
||||
#$QUOTATIONMARK3=`
|
||||
|
||||
$LINECOMMENT=//
|
||||
$BLOCKCOMMENTON=/*
|
||||
$BLOCKCOMMENTOFF=*/
|
||||
$INDENTATIONON={
|
||||
$INDENTATIONOFF=}
|
||||
$PAIRS1=()
|
||||
$PAIRS2=[]
|
||||
$PAIRS3={}
|
||||
@@ -0,0 +1,76 @@
|
||||
;PSPad user HighLighter definition file
|
||||
[Settings]
|
||||
Name=GameMonkey Script
|
||||
HTMLGroup=0
|
||||
Label=0
|
||||
FileType=*.gm
|
||||
CommentString=
|
||||
SlashComment=1
|
||||
CComment=1
|
||||
SlashComment=1
|
||||
IndentChar=
|
||||
UnIndentChar=
|
||||
TabWidth=2
|
||||
CaseSensitive=1
|
||||
SingleQuote=1
|
||||
DoubleQuote=1
|
||||
KeyWordChars=_
|
||||
CodeExplorer=ftUnknown
|
||||
[KeyWords]
|
||||
[ReservedWords]
|
||||
and=
|
||||
break=
|
||||
continue=
|
||||
dowhile=
|
||||
else=
|
||||
false=
|
||||
for=
|
||||
foreach=
|
||||
function=
|
||||
global=
|
||||
if=
|
||||
in=
|
||||
local=
|
||||
member=
|
||||
null=
|
||||
or=
|
||||
return=
|
||||
table=
|
||||
this=
|
||||
true=
|
||||
while=
|
||||
[KeyWords2]
|
||||
assert=
|
||||
block=
|
||||
debug=
|
||||
doString=
|
||||
exit=
|
||||
format=
|
||||
globals=
|
||||
print=
|
||||
signal=
|
||||
sleep=
|
||||
stateGet=
|
||||
stateGetLast=
|
||||
stateSet=
|
||||
stateSetExitFunction=
|
||||
stateSetOnThread=
|
||||
sysCollectGarbage=
|
||||
sysGetDesiredMemoryUsage=
|
||||
sysGetMemoryUsage=
|
||||
sysSetDesiredMemoryUsage=
|
||||
sysTime=
|
||||
tableCount=
|
||||
tableDuplicate=
|
||||
thread=
|
||||
threadId=
|
||||
threadIds=
|
||||
threadKill=
|
||||
threadKillAll=
|
||||
threadTime=
|
||||
typeId=
|
||||
typeName=
|
||||
typeRegisterOperator=
|
||||
typeRegisterVariable=
|
||||
yield=
|
||||
[KeyWords3]
|
||||
BIN
vscript/languages/gm/bin/SciLexer.dll
Normal file
BIN
vscript/languages/gm/bin/SciLexer.dll
Normal file
Binary file not shown.
BIN
vscript/languages/gm/bin/StripCR.exe
Normal file
BIN
vscript/languages/gm/bin/StripCR.exe
Normal file
Binary file not shown.
BIN
vscript/languages/gm/bin/bison.exe
Normal file
BIN
vscript/languages/gm/bin/bison.exe
Normal file
Binary file not shown.
BIN
vscript/languages/gm/bin/flex.exe
Normal file
BIN
vscript/languages/gm/bin/flex.exe
Normal file
Binary file not shown.
BIN
vscript/languages/gm/bin/gmd.exe
Normal file
BIN
vscript/languages/gm/bin/gmd.exe
Normal file
Binary file not shown.
BIN
vscript/languages/gm/bin/gme.exe
Normal file
BIN
vscript/languages/gm/bin/gme.exe
Normal file
Binary file not shown.
BIN
vscript/languages/gm/bin/gml.exe
Normal file
BIN
vscript/languages/gm/bin/gml.exe
Normal file
Binary file not shown.
324
vscript/languages/gm/doc/ChangeLog.txt
Normal file
324
vscript/languages/gm/doc/ChangeLog.txt
Normal file
@@ -0,0 +1,324 @@
|
||||
-----------------------------------------------------------
|
||||
GameMonkey Script Change Log
|
||||
|
||||
Newer entries are at the bottom.
|
||||
Dates are dd/mm/yy
|
||||
|
||||
Note that very minor changes like a renamed variable or
|
||||
modified comment may not appear in this list.
|
||||
-----------------------------------------------------------
|
||||
|
||||
|
||||
22/12/03
|
||||
o Package 1.0 ready for public release.
|
||||
|
||||
02/01/04
|
||||
o Updated script reference doc with system functions, compiler string concat and other.
|
||||
|
||||
08/01/04
|
||||
o Fixed filename case mismatch between filenames and #includes for Unix and other platform.
|
||||
o Renamed and modifed gmNetwork to NetClient and NetServer as they are not gm files.
|
||||
o Modified GMD project to show gmDebugger in gm file set and use Win32 #defs for windows specific parts.
|
||||
o Removed memory display from snake.gm
|
||||
o Added sample script descriptions file.
|
||||
o Removed GM_MAX() and GM_MIN() macros and replaced with template versions in gmUtil
|
||||
o Added gmLog2ge function as faster version of gmHighestBit, and changed gmArraySimple and gmArrayComplex to suit.
|
||||
o Fixed buffer overrun bug with gmVariable::AsString function.
|
||||
o Removed all warning level 4 warnings bar the warnings generated from flex and bison files.
|
||||
o Added GM_VERSION #define to gmMachine.h. first version @ "1.1". added gmVersion script binding.
|
||||
o Changed some #ifdef DEBUG to #ifdef GM_DEBUG_BUILD
|
||||
|
||||
09/01/04
|
||||
o Added constant folding for ints and floats
|
||||
o Added +=, -=, *=, /= etc. Some more optimisation is required on this to avoid reavaluation of complex l-value.
|
||||
|
||||
10/01/04
|
||||
o Added threadKillAll() binding to allow termination of all threads.
|
||||
o Added threadIds() binding to allow access to all threads.
|
||||
o Modified BomberRun.gm to allow exit from main menu.
|
||||
|
||||
16/01/04
|
||||
o Added Minimal code to use GM example.
|
||||
|
||||
18/01/04
|
||||
o Fixed bug in string operator not. Incorrectly returned true instead of false.
|
||||
|
||||
23/01/04
|
||||
o Modified copyright message to be 'GameMonkey Script' as that is the full name.
|
||||
|
||||
25/01/04
|
||||
o Added GameObject example code.
|
||||
|
||||
07/02/04
|
||||
o Remove GM_ASSERT from macro GM_CHECK_FLOAT_PARAM. Was probably left in from some previous debugging.
|
||||
|
||||
20/02/04
|
||||
o gmMachine::Get/SetDesiredByteMemoryUsage() removed and replaced with Get/SetDesiredByteMemoryUsageHard() and Get/SetDesiredByteMemoryUsageSoft().
|
||||
o Changed behaviour of gmMachine::SetDesiredByteMemoryUsage***(). No longer sets auto-memory to false, also does not have auto-memory parameter.
|
||||
o New incremental garbage collection logic to govern the inc GC system.
|
||||
o New GC stats functions: TODO
|
||||
o Set gmConfig.h to use incremental GC by default. Was using atomic GC.
|
||||
o Renamed machine bound function threadIds() to threadAllIds() to make it more different from threadId() and more consistent with other naming.
|
||||
|
||||
21/02/04
|
||||
o Added paranoid check code to Incremental GC.
|
||||
o Fixed serious bug in gmTable with IncGC that caused deleted 'values' to go through write barrier.
|
||||
o Added Sleep(0) to WIN32 build of GME to improve windows performance which otherwise suffers from unyielding threads.
|
||||
|
||||
26/02/04
|
||||
o Changed strtol to strtoul in gmParser.y to convert hex and binary numbers to unsigned ints causing them to not be truncated. They will appear as full signed ints rather than be clipped to 0-2b range.
|
||||
|
||||
03/03/04
|
||||
o Fixed bug in gmCodeGen.cpp The array container could resize causing pointers to become invalid. List is now used to hold function pointers.
|
||||
|
||||
06/03/04
|
||||
o Generate code target to big/little endian regardless of source platform.
|
||||
|
||||
09/03/04
|
||||
o Modified gmMachine::CollectGarbage() AND binding sysCollectGarbage() to take a optional parameter to perform force full collection.
|
||||
o Improved VM GC logic so garbage collector will always free memory when it has the chance after a collect cycle.
|
||||
o Added sysSetDesiredMemoryUsageAuto() binding to enable auto adjust of memory limits (used to be flag in old set memory limit functions).
|
||||
|
||||
11/03/04
|
||||
o Fixed excess memory bug in gmCodeGen.cpp as result of previous bug fix.
|
||||
o Improved VM GC logic so garbage collector auto sizes soft and hard limits better, but realize logic is still poor as long as only soft limit is exceeded.
|
||||
|
||||
01/04/04
|
||||
o Fixed gmRandomFloat to use RAND_MAX instead of 65535. Effects some platforms as these were not equivalent.
|
||||
|
||||
21/04/04
|
||||
o Improved gmThread::Param(), returns reference to gmVariable instead of instance.
|
||||
|
||||
18/06/04
|
||||
o Removed reseting of error log from gmMachine and gmThread. Allowed memory limit on log. Updated examples.
|
||||
|
||||
23/08/04
|
||||
o Fixed gmMachine::Init(). Global table not initialized after ResetAndFreeMemory().
|
||||
|
||||
10/10/04
|
||||
o Fixed bugs in gmCodeGen.cpp. Removed remaining array resize bugs.
|
||||
|
||||
24/10/04
|
||||
o Remove gmArrayComplex and replaced with gmArraySimple to reduce code as it was not necessary.
|
||||
o Removed 'no effect' variables that had been there to remove warning 'unreferenced parameter'.
|
||||
o Wrapped new with GM_NEW and GM_PLACEMENT_NEW for simpler replacement eg. debug new.
|
||||
o Changed multi char constatns 'abcd' to macro version for better compiler compatability.
|
||||
o Aligned memory for allocator in gmLibHooks.cpp for PS2. Added alignment size #define.
|
||||
o Moved #include "assert.h" from gm into gm_Configp.h for better compatability.
|
||||
o Other minor changes for compiler compatability or reduction of compiler warnings.
|
||||
|
||||
25/10/04
|
||||
o Changed behaviour of Block() to yeild if blocked on 'null'.
|
||||
o Made functions in gmThread const.
|
||||
o Added memory presizing functions to allocators and gmMachine.
|
||||
|
||||
27/10/04
|
||||
o Changed malloc,free,realloc to new,delete equivalents in bison.simple and flex.skl files.
|
||||
o Aligned memory addresses in gmCodeTree, gmLog.
|
||||
o Added ToDoList.txt to the \docs folder. Removed readme.txt from src\gm folder.
|
||||
|
||||
02/11/04
|
||||
o Added new line to end of files to remove warnings on some compilers.
|
||||
o Fixed some non-commented comments on preprocessor commands to remove warnings on some compilers.
|
||||
o Changed some includes to later standard: cassert, new, to remove warnings on some compilers.
|
||||
o Moved non gm header includes into gmConfig.h with comments. External includes now only in gmConfig.h and gmConfig_p.h.
|
||||
o Renamed platform\win32 to win32msvc. Added platform\win32gcc. Adjusted paths in projects to reflect this change.
|
||||
|
||||
11/11/04
|
||||
o Fixed commented #define in gmParser.y.
|
||||
o Fixed bug in gmCallScript.h. If C called C bound function via script, thread was not exited correctly.
|
||||
o Added gmCallScript::BeginFunction() Generic function call, and AddParamTable().
|
||||
|
||||
07/12/04
|
||||
o Removed platform specific #defines and checks from gmConfig.h
|
||||
|
||||
10/12/04
|
||||
o Added #include <errno.h> to flex.skl for Linux compile.
|
||||
o Fixed bug in GML. Code used obsolete non 32bit byte code.
|
||||
|
||||
11/12/04
|
||||
o Added gmCall. gmCallScript is now deprecated.
|
||||
|
||||
27/12/04
|
||||
o Replaced GC related 'workLeftToDo' variable names with 'workLeftToGo' so a simple 'todo' search won't find these.
|
||||
o Implemented file.Seek() as it was not implemented.
|
||||
|
||||
01/01/05
|
||||
o gmfrontend.bat strips CR chars from flex and bison script files before generating compiler files. This should help Linux & Unix builds.
|
||||
|
||||
24/01/05
|
||||
o Fixed more compiler warnings.
|
||||
|
||||
30/01/05
|
||||
o Added newline to end of gmDebugger.cpp. Will have to watch for this in future to keep gcc happy.
|
||||
|
||||
03/02/05
|
||||
o Added (experimental) new table creation syntax. eg. t = {1,2,3,}; same as t = table(1,2,3); for simplified config files.
|
||||
|
||||
04/02/05
|
||||
o Added explicit gmVariable() constructors for gmTableObject and gmFunctionObject completing the default set.
|
||||
|
||||
10/02/05
|
||||
o Modified gmCall to improve access to return variables.
|
||||
|
||||
21/03/05
|
||||
o Added ability for gmMachine to handle cpp owned gmObjects and handle GC for them.
|
||||
o Changed gmMachine::Presize() (and related functions) to take named params instead of int array for clarity and future expansion.
|
||||
o Modified gmMemFixedSet to have more smaller memory sizes.
|
||||
|
||||
09/04/05
|
||||
o Removed Windows specific hack from gmDebugger.cpp and moved it to the example Windows debugger files.
|
||||
o Added gmCall::AddParam() for generic gmVariable params.
|
||||
|
||||
14/04/05
|
||||
o Added syntax highlighting to debugger. (Actually added back old files I found, that were meant for an IDE never finished.)
|
||||
o Added line numbering to debugger. A quick hack, but maybe useful.
|
||||
|
||||
16/04/05
|
||||
o Changed gmObject derived class constructors to be non-public. This forces creation by gmMachine.
|
||||
o Minor change to gmListDouble to make some compiler happy.
|
||||
|
||||
21/04/05
|
||||
o Minor changes for compatability with MSVS 2005. gmMathLib, gmStringLib, platform header.
|
||||
|
||||
04/05/05
|
||||
o Added GM_FLOAT_OR_INT_PARAM and GM_CHECK_FLOAT_OR_INT_PARAM to gmThread.h to help with numerical parameters.
|
||||
|
||||
09/05/05
|
||||
o Changed some #defines in gmThread.h gmIncGC.cpp/.h prefixed with GM_ to reduce potential conflict with other code.
|
||||
|
||||
10/05/05
|
||||
o Fixed/Changed friend declaration used by gmTableObject and gmFunctionObject non-public constructors, to work with MSVC 6, 2003, 2005. Handling of friend template functions was different and broken.
|
||||
o Added optional UserBreak callback to allow external code to break thread execution. Eg. to exit endless loop when user pressed CTRL-BREAK.
|
||||
|
||||
05/08/05
|
||||
o Fixed bug in GameObject example, String operator= not always used.
|
||||
|
||||
29/09/05
|
||||
o Fixed bug in gmTableObject. GC WriteBarrier was not being called with setting values to null. Old values, reassigned elsewhere could be lost.
|
||||
|
||||
30/10/05
|
||||
o Changed location of thread create event so 'this' should be available for query.
|
||||
|
||||
14/11/05
|
||||
o Removed gmShutdownVector3Lib() from Vector3 binds example.
|
||||
|
||||
11/01/06
|
||||
o Fixed bug in gmMachine::RemoveCPPOwnedGMObject(). Was not calling WriteBarrier.
|
||||
o Added gmMachine::IsCPPOwnedGMObject().
|
||||
o Added gmMachine::GetTypeTable() (as per DrEvil's mod).
|
||||
o Modified gmMachine::RegisterLib(), Allow append/modify existing table (as per Oli's mod).
|
||||
o Removed gmCallScript. (Had been depricated for a long time.)
|
||||
o Added experimental gmGCRoot to \binds. Current implementation uses STL, but allows custom containers.
|
||||
|
||||
21/01/06
|
||||
o Changed gmMachine, all non-public members are now protected instead of private to allow derived access.
|
||||
o Changed project and workspaces from MSVC 6 to MSVC 8 (2005)
|
||||
To compile the Windows based examples & utils, you will need at least 'MS Visual C++ 2005 Express' and the 'Platform SDK', both freely downloadable from Microsoft.
|
||||
To run the Windows examples you will need VC8 compatible runtimes installed, The '.Net Framework 2.0' redistributables or 'Windows Update' will provide these.
|
||||
(Left VC6 projects and workspaces in for now. Debugger still relies on MFC which is not supported by 2005 Express edition.)
|
||||
|
||||
08/03/06
|
||||
o Changed doString and (system.)DoFile bindings to take optional 'this' as parameter
|
||||
o Added gmThread Param() with default gmVariable param.
|
||||
|
||||
10/03/06
|
||||
o Removed CrimsonEdit syntax highlighting for binary numbers as it did not behave as expected.
|
||||
|
||||
15/03/06
|
||||
o Changed GC to start in 'off' state.
|
||||
|
||||
02/04/06
|
||||
o Fixed gmMachine anomaly where, when a new thread is created just before yielding, and the yield occured in the last running thread, the new thread was skipped until the next execution cycle.
|
||||
|
||||
05/04/06
|
||||
o Fixed bug where local (shared) strings waiting for finalization were reused. Made them revive properly.
|
||||
o Removed old write barrier from gmTable, leaving new correct code only.
|
||||
|
||||
16/04/06
|
||||
o Changed AsString() default for reference types to display pointer in hex instead of decimal.
|
||||
|
||||
17/04/06
|
||||
o Added more info on binding functions to GameMonkeyScriptReference.pdf
|
||||
|
||||
22/04/06
|
||||
o Changed gmThread so that final stack frame including 'this' is still valid when MC_THREAD_DESTROY occurs.
|
||||
|
||||
20/05/06
|
||||
o Added config #define GMMACHINE_REMOVECOMPILER to allow compiler to be removed from gmMachine. Saves some KBs as well as disables compilation.
|
||||
|
||||
03/06/06
|
||||
o Added optional divide by zero check in operators to create GM exception rather than OS exception, as per DrEvil's suggestion.
|
||||
|
||||
19/06/06
|
||||
o Changed gmMachine::AddCPPOwnedGMObject() and RemoveCPPOwnedGMObject() to ignore NULL ptrs rather than ASSERT on them, for caller convenience.
|
||||
|
||||
24/07/06
|
||||
o Added two gmMachine function registration wrappers to provide alternate interface.
|
||||
o Added %u to bound function 'format' to support unsigned ints.
|
||||
|
||||
01/08/06
|
||||
o Added gmVariable::IsNull().
|
||||
|
||||
13/09/06
|
||||
o Added a bunch of accessor functions to gmVariable and gmTable for convenience.
|
||||
|
||||
15/09/06
|
||||
o Added #define-able nullify in gmVariable ctor. Disabled by default.
|
||||
|
||||
26/09/06
|
||||
o Added #define _USE_32BIT_TIME_T so system binding 32 bit time functions work with MSVC 8 runtimes.
|
||||
|
||||
18/10/06
|
||||
o Changed new gmVariable helpers to be const.
|
||||
|
||||
26/10/06
|
||||
o Added user data to function registering. This can be useful when binding C/C++ functions, particularly when registering MANY script functions to ONE C function that redirects to MANY C/C++ member functions.
|
||||
|
||||
05/12/06
|
||||
o Fixed setdot misspelled as getdot in gmOperators.cpp and gmMachineLib.cpp.
|
||||
|
||||
04/01/07
|
||||
o Modified gmKillThread() to behave as its comment describes.
|
||||
|
||||
17/01/07
|
||||
o Fixed bug where GC was not correctly handling persistent strings. Thanks Kaz.
|
||||
|
||||
20/01/07
|
||||
o Improved GC stats to reduce false warnings about bad GC config.
|
||||
|
||||
21/01/07
|
||||
o Added operators implemented from script. Thanks Seth 'Nightmare'.
|
||||
o Added another GC stats related function.
|
||||
o Rearranged pragmas and includes in config headers so order could influence subsequent files.
|
||||
o Added 'typename' to some template declarations in gmGCRootUtil.
|
||||
|
||||
21/02/07
|
||||
o Fixed atan2 binding. Thanks 'blackstormy'.
|
||||
|
||||
20/03/07
|
||||
o Modified GC auto calibrate to allow no shrink.
|
||||
o Moved GC related settings to gmConfig.h.
|
||||
o Added gmMachine::GetAutoMemoryUsage().
|
||||
o Changed GME initial memory setting.
|
||||
o Added example Vector3 non operator Add for efficiency comparison.
|
||||
|
||||
03/05/07
|
||||
o Added Experimental user type op_bool test for conditions. Thanks Kaz.
|
||||
o Modified killed threads. Callback from killed state before recycle/delete.
|
||||
o Added const to more functions in gmThread.
|
||||
|
||||
16/06/07
|
||||
o Fix bug relating to thread stack resize gmThread::Touch(). Thanks DrEvil.
|
||||
o Build .EXEs with VC2005 SP1, static linked VC runtimes except for Debugger which uses MFC.
|
||||
Get vcredist_x86.exe from http://www.microsoft.com/downloads/details.aspx?familyid=200B2FD9-AE1A-4A14-984D-389C36F85647&displaylang=en or search for "Microsoft Visual C++ 2005 SP1 Redistributable Package" if needed.
|
||||
|
||||
22/10/07
|
||||
o Added gmMachine::BindLibToFunction() Just a call through to existing gmLibHooks.
|
||||
o Changed gmGCRoot<>::Set() to handle null.
|
||||
o Removed a_filename from gmMachine::ExecuteFunction() since it wasn't used and could confuse users.
|
||||
|
||||
11/11/07
|
||||
o Modified all GM_*_PARAM() and added overloads for all gmThread:Param*(), to check if invalid type was present.
|
||||
o Modified threadKill() to ignore threads with invalid Id instead of killing current thread.
|
||||
|
||||
BIN
vscript/languages/gm/doc/GameMonkeyFAQ.pdf
Normal file
BIN
vscript/languages/gm/doc/GameMonkeyFAQ.pdf
Normal file
Binary file not shown.
BIN
vscript/languages/gm/doc/GameMonkeyGarbageCollection.pdf
Normal file
BIN
vscript/languages/gm/doc/GameMonkeyGarbageCollection.pdf
Normal file
Binary file not shown.
BIN
vscript/languages/gm/doc/GameMonkeyLicense.pdf
Normal file
BIN
vscript/languages/gm/doc/GameMonkeyLicense.pdf
Normal file
Binary file not shown.
BIN
vscript/languages/gm/doc/GameMonkeyScriptReference.pdf
Normal file
BIN
vscript/languages/gm/doc/GameMonkeyScriptReference.pdf
Normal file
Binary file not shown.
27
vscript/languages/gm/doc/ToDoList.txt
Normal file
27
vscript/languages/gm/doc/ToDoList.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
-----------------------------------------------------------
|
||||
GameMonkey Script TODO list
|
||||
|
||||
In no particular order unless otherwise specified. No time
|
||||
frame for changes or features, so effectively a wish list.
|
||||
-----------------------------------------------------------
|
||||
|
||||
o Fix empty / commented out script from producing parse error. Work around: Add a ';' semicolon to end of source, or ignore known empty scripts.
|
||||
o Change overide 'this' syntax from this:func() to func<this>() to support member chains eg. a.b.c<t>()
|
||||
o Put ++, -- operators back, but don't let them be used in conditions for consistency.
|
||||
|
||||
|
||||
o Make string hash table resize for efficiency in string intensive applications.
|
||||
o Make compiler thread safe. Probably by moving to Lemon parser and Flex++.
|
||||
o Make user type Ids sharable between gmMachine instances so identical registration order is not required.
|
||||
o Make 64bit compatible.
|
||||
o Add exception handling like try / catch blocks.
|
||||
o Possibly move code from gmThread into gmMachine.
|
||||
o Possibly optimize byte code by moving to register stack machine.
|
||||
o Possibly allow extra (eg. variable number of) parameters to be passes as a table to script functions.
|
||||
o Optimize thread code to efficiently handle enormous numbers of threads.
|
||||
o Expose more to the debugger.
|
||||
o Eventually remove classic garbage collector, leaving only incremental collector.
|
||||
o Option for unicode strings.
|
||||
o Binding to allow user type creation and operator functions from within script.
|
||||
o Possibly store the 'color' bit and 'persistent' bit flags for GCObjects in the lower, unused part of the list pointer.
|
||||
o Possibly change the double linked list to a single XOR encoded link. This CPU for memory trade may not be worthwhile.
|
||||
308
vscript/languages/gm/doc/gmdoc.html
Normal file
308
vscript/languages/gm/doc/gmdoc.html
Normal file
@@ -0,0 +1,308 @@
|
||||
<HTML><HEAD><TITLE>GM Documentation</TITLE></HEAD><BODY><BR><HR>
|
||||
<H1>gm</H1>
|
||||
<HR><BR>
|
||||
<HR>
|
||||
<a name="gm::array"><H3>array</H3>
|
||||
</a><B><EM>Brief:</B></EM> array will create a fixed size array object <BR><B><EM>Param:</B></EM> int size optional (0) <BR><B><EM>Return:</B></EM> array <BR><BR><HR>
|
||||
<H1>array</H1>
|
||||
<HR><BR>
|
||||
<HR>
|
||||
<a name="array::Size"><H3>Size</H3>
|
||||
</a><B><EM>Brief:</B></EM> Size will return the current size of the fixed array <BR><B><EM>Return:</B></EM> int array size <BR><HR>
|
||||
<a name="array::Resize"><H3>Resize</H3>
|
||||
</a><B><EM>Brief:</B></EM> Resize will resize the array to a new size <BR><B><EM>Param:</B></EM> int size optional (0) <BR><B><EM>Return:</B></EM> null <BR><HR>
|
||||
<a name="array::Shift"><H3>Shift</H3>
|
||||
</a><B><EM>Brief:</B></EM> Shift will shift slide the array elements by a delta, nulls are shifted in <BR><B><EM>Param:</B></EM> int delta <BR><B><EM>Return:</B></EM> null <BR><HR>
|
||||
<a name="array::Move"><H3>Move</H3>
|
||||
</a><B><EM>Brief:</B></EM> Move will perform a non destructive move on the array <BR><B><EM>Param:</B></EM> int dst <BR><B><EM>Param:</B></EM> int src <BR><B><EM>Param:</B></EM> int size <BR><B><EM>Return:</B></EM> null <BR><BR><HR>
|
||||
<H1>math</H1>
|
||||
<HR><BR>
|
||||
<HR>
|
||||
<a name="math::abs"><H3>abs</H3>
|
||||
</a><B><EM>Brief:</B></EM> abs will return the absolute value of the passed int \ float <BR><B><EM>Param:</B></EM> int\float <BR><B><EM>Return:</B></EM> int\float abs(param) <BR><HR>
|
||||
<a name="math::sqrt"><H3>sqrt</H3>
|
||||
</a><B><EM>Brief:</B></EM> sqrt will return the square root of the passed int \ float <BR><B><EM>Param:</B></EM> int\float <BR><B><EM>Return:</B></EM> int\float sqrt(param) <BR><HR>
|
||||
<a name="math::sqrt"><H3>sqrt</H3>
|
||||
</a><B><EM>Brief:</B></EM> sqrt will return the square root of the passed int \ float <BR><B><EM>Param:</B></EM> int\float A <BR><B><EM>Param:</B></EM> int\float B <BR><B><EM>Return:</B></EM> int\float A to the power of B <BR><HR>
|
||||
<a name="math::floor"><H3>floor</H3>
|
||||
</a><B><EM>Brief:</B></EM> floor <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float floor(A) <BR><HR>
|
||||
<a name="math::ceil"><H3>ceil</H3>
|
||||
</a><B><EM>Brief:</B></EM> ceil <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float ceil(A) <BR><HR>
|
||||
<a name="math::round"><H3>round</H3>
|
||||
</a><B><EM>Brief:</B></EM> round <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float round(A) <BR><HR>
|
||||
<a name="math::degtorad"><H3>degtorad</H3>
|
||||
</a><B><EM>Brief:</B></EM> degtorad will convert degrees to radians <BR><B><EM>Param:</B></EM> float\int deg <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::radtodeg"><H3>radtodeg</H3>
|
||||
</a><B><EM>Brief:</B></EM> radtodeg will convert radians to degrees <BR><B><EM>Param:</B></EM> float\int rad <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::cos"><H3>cos</H3>
|
||||
</a><B><EM>Brief:</B></EM> cos will return the radian cosine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::sin"><H3>sin</H3>
|
||||
</a><B><EM>Brief:</B></EM> sin will return the radian sine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::tan"><H3>tan</H3>
|
||||
</a><B><EM>Brief:</B></EM> tan will return the radian tan (sin/cos) <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::acos"><H3>acos</H3>
|
||||
</a><B><EM>Brief:</B></EM> acos will return the radian arc cosine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::asin"><H3>asin</H3>
|
||||
</a><B><EM>Brief:</B></EM> asin will return the radian arc sine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::atan"><H3>atan</H3>
|
||||
</a><B><EM>Brief:</B></EM> atan will return the radian arc tangent <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::atan"><H3>atan</H3>
|
||||
</a><B><EM>Brief:</B></EM> atan will return the radian arc tangent of x / y <BR><B><EM>Param:</B></EM> float x <BR><B><EM>Param:</B></EM> float y <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::log"><H3>log</H3>
|
||||
</a><B><EM>Brief:</B></EM> log will return the natural logarithm of 1 parameter, or (base, value) the logarithm to base <BR><B><EM>Param:</B></EM> float natural \ base <BR><B><EM>Param:</B></EM> float value (optional) <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::min"><H3>min</H3>
|
||||
</a><B><EM>Brief:</B></EM> min will return the min of the 2 passed values <BR><B><EM>Param:</B></EM> float\int A <BR><B><EM>Param:</B></EM> float\int B <BR><B><EM>Return:</B></EM> float \ int min(A, B) <BR><HR>
|
||||
<a name="math::max"><H3>max</H3>
|
||||
</a><B><EM>Brief:</B></EM> max will return the max of the 2 passed values <BR><B><EM>Param:</B></EM> float\int A <BR><B><EM>Param:</B></EM> float\int B <BR><B><EM>Return:</B></EM> float \ int max(A, B) <BR><HR>
|
||||
<a name="math::clamp"><H3>clamp</H3>
|
||||
</a><B><EM>Brief:</B></EM> clamp will return the clamed value. clamp(min, val, max) <BR><B><EM>Param:</B></EM> float\int MIN <BR><B><EM>Param:</B></EM> float\int VALUE <BR><B><EM>Param:</B></EM> float\int MAX <BR><B><EM>Return:</B></EM> float\int value clamped to min, max <BR><HR>
|
||||
<a name="math::randint"><H3>randint</H3>
|
||||
</a><B><EM>Brief:</B></EM> randint will return a random int from lower inclusive to upper. <BR><B><EM>Param:</B></EM> int lower inclusive <BR><B><EM>Param:</B></EM> int upper <BR><B><EM>Return:</B></EM> int <BR><HR>
|
||||
<a name="math::randfloat"><H3>randfloat</H3>
|
||||
</a><B><EM>Brief:</B></EM> randfloat will return a random float from lower inclusive to upper. <BR><B><EM>Param:</B></EM> float lower inclusive <BR><B><EM>Param:</B></EM> float upper <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::randseed"><H3>randseed</H3>
|
||||
</a><B><EM>Brief:</B></EM> randseed will seed the random number generator <BR><B><EM>Param:</B></EM> int seed <BR><BR><HR>
|
||||
<H1>string</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> string operations often store a copy of the string on the stack, so keep string sizes reasonable <BR><HR>
|
||||
<a name="string::IsEmpty"><H3>IsEmpty</H3>
|
||||
</a><B><EM>Brief:</B></EM> IsEmpty will test to see if the string is 0 length <BR><B><EM>Return:</B></EM> non-zero if the string is empty <BR><HR>
|
||||
<a name="string::Length"><H3>Length</H3>
|
||||
</a><B><EM>Brief:</B></EM> Length will return the length of the string not including the null terminating character <BR><B><EM>Return:</B></EM> int length <BR><HR>
|
||||
<a name="string::Left"><H3>Left</H3>
|
||||
</a><B><EM>Brief:</B></EM> Left will return the left count charaters of the string <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Right"><H3>Right</H3>
|
||||
</a><B><EM>Brief:</B></EM> Right will return the right count charaters of the string <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::RightAt"><H3>RightAt</H3>
|
||||
</a><B><EM>Brief:</B></EM> RightAt will return the charaters right of and including the given index <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Mid"><H3>Mid</H3>
|
||||
</a><B><EM>Brief:</B></EM> Mid will return count characters from the start index <BR><B><EM>Param:</B></EM> int startIndex <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Compare"><H3>Compare</H3>
|
||||
</a><B><EM>Brief:</B></EM> Compare will perform a string compare <BR><B><EM>Param:</B></EM> string to compare <BR><B><EM>Return:</B></EM> -1 if the this < compare string, 0 if the strings are equal, 1 otherwise <BR><HR>
|
||||
<a name="string::CompareNoCase"><H3>CompareNoCase</H3>
|
||||
</a><B><EM>Brief:</B></EM> CompareNoCase will perform a string compare (case insensitive) <BR><B><EM>Param:</B></EM> string to compare <BR><B><EM>Return:</B></EM> -1 if the this < compare string, 0 if the strings are equal, 1 otherwise <BR><HR>
|
||||
<a name="string::Int"><H3>Int</H3>
|
||||
</a><B><EM>Brief:</B></EM> Int will return the int value of the string <BR><B><EM>Return:</B></EM> int value <BR><HR>
|
||||
<a name="string::Float"><H3>Float</H3>
|
||||
</a><B><EM>Brief:</B></EM> Float will return the float value of the string <BR><B><EM>Return:</B></EM> float value <BR><HR>
|
||||
<a name="string::String"><H3>String</H3>
|
||||
</a><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Upper"><H3>Upper</H3>
|
||||
</a><B><EM>Brief:</B></EM> Upper will return the string as uppercase <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Lower"><H3>Lower</H3>
|
||||
</a><B><EM>Brief:</B></EM> Lower will return the string as lowercase <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::SpanIncluding"><H3>SpanIncluding</H3>
|
||||
</a><B><EM>Brief:</B></EM> SpanIncluding will return this string while characters are within the passed string <BR><B><EM>Param:</B></EM> string charset <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::SpanExcluding"><H3>SpanExcluding</H3>
|
||||
</a><B><EM>Brief:</B></EM> SpanExcluding will return this string while characters are not within the passed string <BR><B><EM>Param:</B></EM> string charset <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::AppendPath"><H3>AppendPath</H3>
|
||||
</a><B><EM>Brief:</B></EM> AppendPath will append a path make sure one '\' is maintained <BR><B><EM>Param:</B></EM> string path to append <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::ReplaceCharsInSet"><H3>ReplaceCharsInSet</H3>
|
||||
</a><B><EM>Brief:</B></EM> ReplaceCharsInSet will replace all chars in this that are within the charset with the given int char <BR><B><EM>Param:</B></EM> int char to replace with <BR><B><EM>Param:</B></EM> string charset of chars to replace <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Find"><H3>Find</H3>
|
||||
</a><B><EM>Brief:</B></EM> Find will find the first occurance of the passed string within this string <BR><B><EM>Param:</B></EM> string search string <BR><B><EM>Param:</B></EM> int start index optional (0) <BR><B><EM>Return:</B></EM> int index of first occurance, or -1 if the string was not found <BR><HR>
|
||||
<a name="string::Reverse"><H3>Reverse</H3>
|
||||
</a><B><EM>Brief:</B></EM> Reverse characters of a string <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::ReverseFind"><H3>ReverseFind</H3>
|
||||
</a><B><EM>Brief:</B></EM> ReverseFind will find the first occurance of the passed string within this string starting from the right <BR><B><EM>Param:</B></EM> string search string <BR><B><EM>Return:</B></EM> int index of first occurance, or -1 if the string was not found <BR><HR>
|
||||
<a name="string::GetAt"><H3>GetAt</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetAt will return the char at the given index <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Return:</B></EM> int char, or null if index was out of range <BR><HR>
|
||||
<a name="string::SetAt"><H3>SetAt</H3>
|
||||
</a><B><EM>Brief:</B></EM> SetAt will return the string with the character set at the given position <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Param:</B></EM> int char <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::TrimLeft"><H3>TrimLeft</H3>
|
||||
</a><B><EM>Brief:</B></EM> TrimLeft will return the string with the chars from the passed char set trimmed from the left <BR><B><EM>Param:</B></EM> string charset optional (" \r\n\v\t") <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::TrimRight"><H3>TrimRight</H3>
|
||||
</a><B><EM>Brief:</B></EM> TrimRight will return the string with the chars from the passed char set trimmed from the right <BR><B><EM>Param:</B></EM> string charset optional (" \r\n\v\t") <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::GetFilenameNoExt"><H3>GetFilenameNoExt</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetFilenameNoExt will return the filename part of a path string <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::GetFilename"><H3>GetFilename</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetFilename will return the filename part of a path string incl. extension <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::GetExtension"><H3>GetExtension</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetExtension will return the file extension <BR><B><EM>Param:</B></EM> int inclDot optional (0) 1 will include '.', 0 won't <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::SetExtension"><H3>SetExtension</H3>
|
||||
</a><B><EM>Brief:</B></EM> SetExtension returns a string with the extension change to the given one. <BR><B><EM>Param:</B></EM> string ext optional (null) the new extension, with or without the dot. null to remove extension. <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::GetPath"><H3>GetPath</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetPath will return the file path from a path string <BR><B><EM>Param:</B></EM> int inclSlash optional (0) will include a '\' on the end of the path <BR><B><EM>Return:</B></EM> string <BR><BR><HR>
|
||||
<H1>system</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> system functions are bound in a "system" table. <BR><HR>
|
||||
<a name="system::Exec"><H3>Exec</H3>
|
||||
</a><B><EM>Brief:</B></EM> Exec will execute a system command <BR><B><EM>Param:</B></EM> string params will be concatinated together with a single space to form the final system command string <BR><B><EM>Return:</B></EM> integer value returned from system exec call, -1 on error <BR><HR>
|
||||
<a name="system::DoFile"><H3>DoFile</H3>
|
||||
</a><B><EM>Brief:</B></EM> DoFile will execute the gm script in the named file <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int optional (1) as 1 will execute string before returning, 0 will execute later. <BR><B><EM>Param:</B></EM> ref optional (null) set 'this' <BR><B><EM>Return:</B></EM> thread id of new thread created to execute file <BR><HR>
|
||||
<a name="system::File"><H3>File</H3>
|
||||
</a><B><EM>Brief:</B></EM> File will create a file object <BR><B><EM>Return:</B></EM> file object <BR><HR>
|
||||
<a name="system::FileExists"><H3>FileExists</H3>
|
||||
</a><B><EM>Brief:</B></EM> FileExists will test to see if a file exists <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Return:</B></EM> 1 if the file exists, otherwise 0 <BR><HR>
|
||||
<a name="system::FileFindFirst"><H3>FileFindFirst</H3>
|
||||
</a><B><EM>Brief:</B></EM> FileFindFirst will start a file search will test to see if a file exists <BR><B><EM>Param:</B></EM> string filesearch (may contain wildcards, eg, `c:\temp\*.txt`) <BR><B><EM>Return:</B></EM> fileFind object. fileFind object has .filename and .size member <BR><B><EM>See Also:</B></EM> fileFind <BR><HR>
|
||||
<a name="system::FileFindNext"><H3>FileFindNext</H3>
|
||||
</a><B><EM>Brief:</B></EM> FileFindNext will get the next file matching the file find <BR><B><EM>Param:</B></EM> fileFind object returned by FileFindFirst call or FileFindNext call <BR><B><EM>Return:</B></EM> fileFind object <BR><B><EM>See Also:</B></EM> fileFind <BR><HR>
|
||||
<a name="system::FileInfo"><H3>FileInfo</H3>
|
||||
</a><B><EM>Brief:</B></EM> FileInfo will return a file info object that has readonly members for .creationDate <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Return:</B></EM> fileInfo object, fileInfo object has a .creationTime, .accessedTime, .modifiedTime and a .size <BR><HR>
|
||||
<a name="system::CreateFolder"><H3>CreateFolder</H3>
|
||||
</a><B><EM>Brief:</B></EM> CreateFolder will create a file path if it does not already exist <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Return:</B></EM> int 0 on failure, 1 on successful create, 2 if folder already exists <BR><HR>
|
||||
<a name="system::DeleteFolder"><H3>DeleteFolder</H3>
|
||||
</a><B><EM>Brief:</B></EM> DeleteFolder will remove a file path <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Param:</B></EM> int remove subfiles and folders optional (0) <BR><B><EM>Return:</B></EM> int 1 on success, 0 con failure <BR><HR>
|
||||
<a name="system::Time"><H3>Time</H3>
|
||||
</a><B><EM>Brief:</B></EM> Time will return a unix style time_t as an int <BR><B><EM>Return:</B></EM> the current time <BR><HR>
|
||||
<a name="system::FormatTime"><H3>FormatTime</H3>
|
||||
</a><B><EM>Brief:</B></EM> FormatTime will take a int (time_t) value and format according to the passed format string. <BR><B><EM>Param:</B></EM> int time (-1) is a (time_t) to be converted to a string, passing -1 gets current time <BR><B><EM>Param:</B></EM> string format ("%A %d %B %Y, %I:%M:%S %p") is the format string to use.<BR> %a : Abbreviated weekday name<BR> %A : Full weekday name<BR> %b : Abbreviated month name<BR> %B : Full month name<BR> %c : Date and time representation appropriate for locale<BR> %d : Day of month as decimal number (01 <20> 31)<BR> %H : Hour in 24-hour format (00 <20> 23)<BR> %I : Hour in 12-hour format (01 <20> 12)<BR> %j : Day of year as decimal number (001 <20> 366)<BR> %m : Month as decimal number (01 <20> 12)<BR> %M : Minute as decimal number (00 <20> 59)<BR> %p : Current locale<6C>s A.M./P.M. indicator for 12-hour clock<BR> %S : Second as decimal number (00 <20> 59)<BR> %U : Week of year as decimal number, with Sunday as first day of week (00 <20> 53)<BR> %w : Weekday as decimal number (0 <20> 6; Sunday is 0)<BR> %W : Week of year as decimal number, with Monday as first day of week (00 <20> 53)<BR> %x : Date representation for current locale<BR> %X : Time representation for current locale<BR> %y : Year without century, as decimal number (00 <20> 99)<BR> %Y : Year with century, as decimal number<BR> %z, %Z : Time-zone name or abbreviation; no characters if time zone is unknown<BR> %% : Percent sign<BR> <BR><B><EM>Return:</B></EM> the time as a string. <BR><BR><HR>
|
||||
<H1>fileFind</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> fileFind object has a "filename" and "size" member <BR><HR>
|
||||
<a name="fileFind::GetAttribute"><H3>GetAttribute</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetAttribute will test a file attribute. <BR><B><EM>Param:</B></EM> int char attribute 'r' readonly, 'a' archive, 's' system, 'h' hidden, 'c' compressed, 'd' directory <BR><B><EM>Return:</B></EM> 1 if the attribute is set, 0 otherwise <BR><BR><HR>
|
||||
<H1>file</H1>
|
||||
<HR><BR>
|
||||
<HR>
|
||||
<a name="file::Open"><H3>Open</H3>
|
||||
</a><B><EM>Brief:</B></EM> Open will open a file in binary mode <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int readonly optional (1) <BR><B><EM>Return:</B></EM> 1 if the open was successful, 0 otherwise <BR><HR>
|
||||
<a name="file::OpenText"><H3>OpenText</H3>
|
||||
</a><B><EM>Brief:</B></EM> OpenText will open a file in text mode <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int readonly optional (1) <BR><B><EM>Return:</B></EM> 1 if the open was successful, 0 otherwise <BR><HR>
|
||||
<a name="file::Close"><H3>Close</H3>
|
||||
</a><B><EM>Brief:</B></EM> Close will close a file <BR><HR>
|
||||
<a name="file::IsOpen"><H3>IsOpen</H3>
|
||||
</a><B><EM>Brief:</B></EM> IsOpen will test to see if a file is open <BR><B><EM>Return:</B></EM> 1 if the file is open, 0 otherwise <BR><HR>
|
||||
<a name="file::Seek"><H3>Seek</H3>
|
||||
</a><B><EM>Brief:</B></EM> Move to position within file <BR><B><EM>Param:</B></EM> int offset positional offset relative to origin <BR><B><EM>Param:</B></EM> int origin eg. myFile.SEEK_CUR, myFile.SEEK_END, myFile.SEEK_SET for current, end, start origins. <BR><B><EM>Return:</B></EM> int 1 if operation succeeded, 0 if failed. <BR><HR>
|
||||
<a name="file::Tell"><H3>Tell</H3>
|
||||
</a><B><EM>Brief:</B></EM> Tell will return the current cursor position of the file <BR><B><EM>Return:</B></EM> int the current cursor position, -1 on error <BR><HR>
|
||||
<a name="file::ReadLine"><H3>ReadLine</H3>
|
||||
</a><B><EM>Brief:</B></EM> ReadLine will read a line of text from the file. <BR><B><EM>Param:</B></EM> int keep optional (0) as 1 will keep the "\n" char on the line, otherwise it is removed <BR><B><EM>Return:</B></EM> string, or null on eof <BR><HR>
|
||||
<a name="file::ReadChar"><H3>ReadChar</H3>
|
||||
</a><B><EM>Brief:</B></EM> ReadChar will read a char from the file <BR><B><EM>Return:</B></EM> int char or null on eof <BR><HR>
|
||||
<a name="file::WriteString"><H3>WriteString</H3>
|
||||
</a><B><EM>Brief:</B></EM> WriteString will write a string to the file <BR><B><EM>Param:</B></EM> string to write to file <BR><B><EM>Return:</B></EM> 1 on success, null on error <BR><HR>
|
||||
<a name="file::WriteChar"><H3>WriteChar</H3>
|
||||
</a><B><EM>Brief:</B></EM> WriteChar will write a char to the file <BR><B><EM>Param:</B></EM> int char to write to file <BR><B><EM>Return:</B></EM> 1 on success, null on error <BR><BR><HR>
|
||||
<H1>Vector3</H1>
|
||||
<HR><BR>
|
||||
<HR>
|
||||
<a name="Vector3::Vector3"><H3>Vector3</H3>
|
||||
</a><B><EM>Brief:</B></EM> Create a Vector3 object <BR><B><EM>Param:</B></EM> float x or [0] optional (0) <BR><B><EM>Param:</B></EM> float y or [1] optional (0) <BR><B><EM>Param:</B></EM> float z or [2] optional (0) <BR><BR><HR>
|
||||
<H1>Vector3</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> Vector3 math class <BR><HR>
|
||||
<a name="Vector3::DominantAxis"><H3>DominantAxis</H3>
|
||||
</a><B><EM>Brief:</B></EM> Find the index of the largest vector component. <BR><B><EM>This:</B></EM> Vector to evaluate. <BR><B><EM>Return:</B></EM> int Index of largest component. <BR><HR>
|
||||
<a name="Vector3::Dot"><H3>Dot</H3>
|
||||
</a><B><EM>Brief:</B></EM> Calculate the Dot (or Inner) Product of two vectors. <BR><B><EM>This:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><B><EM>Return:</B></EM> float result. <BR><HR>
|
||||
<a name="Vector3::Length"><H3>Length</H3>
|
||||
</a><B><EM>Brief:</B></EM> Length will return the length of the vector. <BR><B><EM>Return:</B></EM> float Dot product result that is cosine of the angle between the two vectors. <BR><HR>
|
||||
<a name="Vector3::Cross"><H3>Cross</H3>
|
||||
</a><B><EM>Brief:</B></EM> Calculate the Cross (or Outer) Product of two vectors. <BR><B><EM>This:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><B><EM>Return:</B></EM> Vector3 Cross product resultant vector that is perpendicular to the two input vectors and length sine of the angle between them. <BR><HR>
|
||||
<a name="Vector3::Normalize"><H3>Normalize</H3>
|
||||
</a><B><EM>Brief:</B></EM> Return a unit length copy of this vector. <BR><B><EM>This:</B></EM> Vector to be copied. <BR><B><EM>Return:</B></EM> Vector3 Unit length copy of this vector. <BR><HR>
|
||||
<a name="Vector3::LengthSquared"><H3>LengthSquared</H3>
|
||||
</a><B><EM>Brief:</B></EM> Return the squared length of the vector. <BR><B><EM>Return:</B></EM> float Squared length of the vector. <BR><HR>
|
||||
<a name="Vector3::ProjectFrom"><H3>ProjectFrom</H3>
|
||||
</a><B><EM>Brief:</B></EM> Project a direction from a point. <BR><B><EM>This:</B></EM> Vector3 Direction. <BR><B><EM>Param:</B></EM> Vector3 Start point; <BR><B><EM>Param:</B></EM> float Distance or time. <BR><B><EM>Return:</B></EM> Vector3 Projected result. <BR><HR>
|
||||
<a name="Vector3::Clone"><H3>Clone</H3>
|
||||
</a><B><EM>Brief:</B></EM> Return a copy of this vector. <BR><B><EM>Return:</B></EM> A copy of this vector. <BR><HR>
|
||||
<a name="Vector3::Set"><H3>Set</H3>
|
||||
</a><B><EM>Brief:</B></EM> Set vector from other vector or 3 components. <BR><HR>
|
||||
<a name="Vector3::LerpToPoint"><H3>LerpToPoint</H3>
|
||||
</a><B><EM>Brief:</B></EM> Linear interpolate between two 'point' vectors. <BR><B><EM>This:</B></EM> Vector3 From vector. <BR><B><EM>Param:</B></EM> Vector3 To vector. <BR><B><EM>Param:</B></EM> float Fraction or time between 0 and 1. <BR><B><EM>Return:</B></EM> Vector3 Resulting inbetween vector. <BR><HR>
|
||||
<a name="Vector3::SlerpToVector"><H3>SlerpToVector</H3>
|
||||
</a><B><EM>Brief:</B></EM> Spherical linear interpolate between two vectors. <BR><B><EM>This:</B></EM> Vector3 From vector. <BR><B><EM>Param:</B></EM> Vector3 To vector. <BR><B><EM>Param:</B></EM> float Fraction or time between 0 and 1. <BR><B><EM>Return:</B></EM> Vector3 Resulting inbetween vector. <BR><HR>
|
||||
<a name="Vector3::RotateAxisAngle"><H3>RotateAxisAngle</H3>
|
||||
</a><B><EM>Brief:</B></EM> Rotate around Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> Vector3 Unit length axis of rotation. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
|
||||
<a name="Vector3::RotateX"><H3>RotateX</H3>
|
||||
</a><B><EM>Brief:</B></EM> Rotate around X Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
|
||||
<a name="Vector3::RotateX"><H3>RotateX</H3>
|
||||
</a><B><EM>Brief:</B></EM> Rotate around Y Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
|
||||
<a name="Vector3::RotateZ"><H3>RotateZ</H3>
|
||||
</a><B><EM>Brief:</B></EM> Rotate around Z Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
|
||||
<a name="Vector3::SetAdd"><H3>SetAdd</H3>
|
||||
</a><B><EM>Brief:</B></EM> Add two vectors, store result in this. Demonstrate relative efficiency compared to operator version. <BR><B><EM>This:</B></EM> Vector3 Result vector. <BR><B><EM>Param:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><HR>
|
||||
<a name="Vector3::Add"><H3>Add</H3>
|
||||
</a><B><EM>Brief:</B></EM> Add vector to this. Demonstrate relative efficiency compared to operator version. <BR><B><EM>This:</B></EM> Vector3 Result vector. <BR><B><EM>Param:</B></EM> Vector3 vector to add. <BR><BR><HR>
|
||||
<H1>gm</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> functions in the gm lib are all global scope <BR><HR>
|
||||
<a name="gm::debug"><H3>debug</H3>
|
||||
</a><B><EM>Brief:</B></EM> debug will cause a the debugger to break at this point while running. <BR><BR><HR>
|
||||
<H1>gm</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> functions in the gm lib are all global scope <BR><HR>
|
||||
<a name="gm::gmVersion"><H3>gmVersion</H3>
|
||||
</a><B><EM>Brief:</B></EM> gmVersion will return the gmMachine version string. version string is major type . minor type as a string and was added at version 1.1 <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="gm::typeId"><H3>typeId</H3>
|
||||
</a><B><EM>Brief:</B></EM> typeId will return the type id of the passed var <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> integer type <BR><HR>
|
||||
<a name="gm::typeName"><H3>typeName</H3>
|
||||
</a><B><EM>Brief:</B></EM> typeName will return the type name of the passed var <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="gm::typeRegisterOperator"><H3>typeRegisterOperator</H3>
|
||||
</a><B><EM>Brief:</B></EM> typeRegisterOperator will register an operator for a type <BR><B><EM>Param:</B></EM> int typeid <BR><B><EM>Param:</B></EM> string operator name is one of "getdot", "setdot", "getind", "setind", "add", "sub", "mul", "div", "mod", "inc", "dec", "bitor", "bitxor", "bitand", "shiftleft", "shiftright", "bitinv", "lt", "gt", "lte", "gte", "eq", "neq", "neg", "pos", "not" <BR><B><EM>Param:</B></EM> function <BR><B><EM>Return:</B></EM> 1 on success, otherwise 0 <BR><HR>
|
||||
<a name="gm::typeRegisterVariable"><H3>typeRegisterVariable</H3>
|
||||
</a><B><EM>Brief:</B></EM> typeRegisterVariable will register a variable with a type such that (type).varname will return the variable <BR><B><EM>Param:</B></EM> int typeid <BR><B><EM>Param:</B></EM> string var name <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> 1 on success, otherwise 0 <BR><HR>
|
||||
<a name="gm::sysCollectGarbage"><H3>sysCollectGarbage</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysCollectGarbage will run the garbage collector iff the current mem used is over the desired mem used <BR><B><EM>Param:</B></EM> forceFullCollect (false) Optionally perform full garbage collection immediately if garbage collection is not disabled. <BR><B><EM>Return:</B></EM> 1 if the gc was run, 0 otherwise <BR><HR>
|
||||
<a name="gm::sysGetMemoryUsage"><H3>sysGetMemoryUsage</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetMemoryUsage will return the current memory used in bytes <BR><B><EM>Return:</B></EM> int memory usage <BR><HR>
|
||||
<a name="gm::sysSetDesiredMemoryUsageHard"><H3>sysSetDesiredMemoryUsageHard</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageHard will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run. <BR><B><EM>Param:</B></EM> int desired mem usage in bytes <BR><HR>
|
||||
<a name="gm::sysSetDesiredMemoryUsageSoft"><H3>sysSetDesiredMemoryUsageSoft</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageSoft will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run. <BR><B><EM>Param:</B></EM> int desired mem usage in bytes <BR><HR>
|
||||
<a name="gm::sysGetDesiredMemoryUsageHard"><H3>sysGetDesiredMemoryUsageHard</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetDesiredMemoryUsageHard will get the desired memory useage in bytes. Note that this value is used to start garbage collection, it is not a strict limit. <BR><B><EM>Return:</B></EM> int Desired memory usage in bytes. <BR><HR>
|
||||
<a name="gm::sysGetDesiredMemoryUsageSoft"><H3>sysGetDesiredMemoryUsageSoft</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetDesiredMemoryUsageSoft will get the desired memory useage in bytes. Note that this value is used to start garbage collection, it is not a strict limit. <BR><B><EM>Return:</B></EM> int Desired memory usage in bytes. <BR><HR>
|
||||
<a name="gm::sysSetDesiredMemoryUsageAuto"><H3>sysSetDesiredMemoryUsageAuto</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageAuto will enable auto adjustment of the memory limit(s) for subsequent garbage collections. <BR><B><EM>Param:</B></EM> int enable or disable flag <BR><HR>
|
||||
<a name="gm::sysGetStatsGCNumFullCollects"><H3>sysGetStatsGCNumFullCollects</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumFullCollects Return the number of times full garbage collection has occured. <BR><B><EM>Return:</B></EM> int Number of times full collect has occured. <BR><HR>
|
||||
<a name="gm::sysGetStatsGCNumIncCollects"><H3>sysGetStatsGCNumIncCollects</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumIncCollects Return the number of times incremental garbage collection has occured. This number may increase in twos as the GC has multiple phases which appear as restarts. <BR><B><EM>Return:</B></EM> int Number of times incremental collect has occured. <BR><HR>
|
||||
<a name="gm::sysGetStatsGCNumWarnings"><H3>sysGetStatsGCNumWarnings</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumWarnings Return the number of warnings because the GC or VM thought the GC was poorly configured. If this number is large and growing rapidly, the GC soft and hard limits need to be configured better. Do not be concerned if this number grows slowly. <BR><B><EM>Return:</B></EM> int Number of warnings garbage collect has generated. <BR><HR>
|
||||
<a name="gm::sysIsGCRunning"><H3>sysIsGCRunning</H3>
|
||||
</a><B><EM>Brief:</B></EM> Returns true if GC is running a cycle. <BR><HR>
|
||||
<a name="gm::sysTime"><H3>sysTime</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysTime will return the machine time in milli seconds <BR><B><EM>Return:</B></EM> int <BR><HR>
|
||||
<a name="gm::doString"><H3>doString</H3>
|
||||
</a><B><EM>Brief:</B></EM> doString will execute the passed gm script <BR><B><EM>Param:</B></EM> string script <BR><B><EM>Param:</B></EM> int optional (1) set as true and the string will execute before returning to this thread <BR><B><EM>Param:</B></EM> ref optional (null) set 'this' <BR><B><EM>Return:</B></EM> int thread id of thread created for string execution <BR><HR>
|
||||
<a name="gm::globals"><H3>globals</H3>
|
||||
</a><B><EM>Brief:</B></EM> globals will return the globals table <BR><B><EM>Return:</B></EM> table containing all global variables <BR><HR>
|
||||
<a name="gm::threadTime"><H3>threadTime</H3>
|
||||
</a><B><EM>Brief:</B></EM> threadTime will return the thread execution time in milliseconds <BR><B><EM>Return:</B></EM> int <BR><HR>
|
||||
<a name="gm::threadId"><H3>threadId</H3>
|
||||
</a><B><EM>Brief:</B></EM> threadId will return the thread id of the current executing script <BR><B><EM>Return:</B></EM> int <BR><HR>
|
||||
<a name="gm::threadAllIds"><H3>threadAllIds</H3>
|
||||
</a><B><EM>Brief:</B></EM> threadIds returns a table of thread Ids <BR><B><EM>Return:</B></EM> table of thread Ids <BR><HR>
|
||||
<a name="gm::threadKill"><H3>threadKill</H3>
|
||||
</a><B><EM>Brief:</B></EM> threadKill will kill the thread with the given id <BR><B><EM>Param:</B></EM> int threadId optional (0) will kill this thread <BR><HR>
|
||||
<a name="gm::threadKillAll"><H3>threadKillAll</H3>
|
||||
</a><B><EM>Brief:</B></EM> threadKillAll will kill all the threads except the current one <BR><B><EM>Param:</B></EM> bool optional (false) will kill this thread if true <BR><HR>
|
||||
<a name="gm::thread"><H3>thread</H3>
|
||||
</a><B><EM>Brief:</B></EM> thread will start a new thread <BR><B><EM>Param:</B></EM> function entry point of the thread <BR><B><EM>Param:</B></EM> ... parameters to pass to the entry function <BR><B><EM>Return:</B></EM> int threadid <BR><HR>
|
||||
<a name="gm::yield"><H3>yield</H3>
|
||||
</a><B><EM>Brief:</B></EM> yield will hand execution control to the next thread <BR><HR>
|
||||
<a name="gm::exit"><H3>exit</H3>
|
||||
</a><B><EM>Brief:</B></EM> exit will kill this thread <BR><HR>
|
||||
<a name="gm::assert"><H3>assert</H3>
|
||||
</a><B><EM>Brief:</B></EM> assert <BR><B><EM>Param:</B></EM> int expression if true, will do nothing, if false, will cause an exception <BR><HR>
|
||||
<a name="gm::sleep"><H3>sleep</H3>
|
||||
</a><B><EM>Brief:</B></EM> sleep will sleep this thread for the given number of seconds <BR><B><EM>Param:</B></EM> int\float seconds <BR><HR>
|
||||
<a name="gm::signal"><H3>signal</H3>
|
||||
</a><B><EM>Brief:</B></EM> signal will signal the given variable, this will unblock dest threads that are blocked on the same variable. <BR><B><EM>Param:</B></EM> var <BR><B><EM>Param:</B></EM> int destThreadId optional (0) 0 will signal all threads <BR><HR>
|
||||
<a name="gm::block"><H3>block</H3>
|
||||
</a><B><EM>Brief:</B></EM> block will block on all passed vars, execution will halt until another thread signals one of the block variables. Will yield on null and return null. <BR><B><EM>Param:</B></EM> ... vars <BR><B><EM>Return:</B></EM> the unblocking var <BR><HR>
|
||||
<a name="gm::stateSet"><H3>stateSet</H3>
|
||||
</a><B><EM>Brief:</B></EM> stateSet will collapse the stack to nothing, and push the passed functions. <BR><B><EM>Param:</B></EM> function new state function to execute <BR><B><EM>Param:</B></EM> ... params for new state function <BR><HR>
|
||||
<a name="gm::stateSetOnThread"><H3>stateSetOnThread</H3>
|
||||
</a><B><EM>Brief:</B></EM> stateSetOnThread will collapse the stack of the given thread id to nothing, and push the passed functions. <BR><B><EM>Param:</B></EM> int thread id <BR><B><EM>Param:</B></EM> function new state function to execute <BR><B><EM>Param:</B></EM> ... params for new state function <BR><HR>
|
||||
<a name="gm::stateGet"><H3>stateGet</H3>
|
||||
</a><B><EM>Brief:</B></EM> stateGet will return the function on the bottom of this threads execution stack iff it was pushed using stateSet <BR><B><EM>Param:</B></EM> a_threadId Optional Id of thread to get state on. \reutrn function \ null <BR><HR>
|
||||
<a name="gm::stateGetLast"><H3>stateGetLast</H3>
|
||||
</a><B><EM>Brief:</B></EM> stateGetLast will return the last state function of this thread <BR><B><EM>Param:</B></EM> a_threadId Optional Id of thread to get last state on. \reutrn function \ null <BR><HR>
|
||||
<a name="gm::stateSetExitFunction"><H3>stateSetExitFunction</H3>
|
||||
</a><B><EM>Brief:</B></EM> stateSetExitFunction will set an exit function for this state, that will be called with no parameters if this thread switches state <BR><B><EM>Param:</B></EM> function <BR><HR>
|
||||
<a name="gm::tableCount"><H3>tableCount</H3>
|
||||
</a><B><EM>Brief:</B></EM> tableCount will return the number of elements in a table object <BR><B><EM>Param:</B></EM> table <BR><B><EM>Return:</B></EM> int <BR><HR>
|
||||
<a name="gm::tableDuplicate"><H3>tableDuplicate</H3>
|
||||
</a><B><EM>Brief:</B></EM> tableDuplicate will duplicate the passed table object <BR><B><EM>Param:</B></EM> table <BR><B><EM>Return:</B></EM> table <BR><HR>
|
||||
<a name="gm::print"><H3>print</H3>
|
||||
</a><B><EM>Brief:</B></EM> print will print the given vars to the print handler. passed strings are concatinated together with a seperating space. <BR><B><EM>Param:</B></EM> ... strings <BR><HR>
|
||||
<a name="gm::format"><H3>format</H3>
|
||||
</a><B><EM>Brief:</B></EM> format (like sprintf, but returns a string) %d, %s, %f, %c, %b, %x, %e <BR><B><EM>Param:</B></EM> string <BR></BODY></HTML>
|
||||
1689
vscript/languages/gm/scripts/Bomber.gm
Normal file
1689
vscript/languages/gm/scripts/Bomber.gm
Normal file
File diff suppressed because it is too large
Load Diff
691
vscript/languages/gm/scripts/BomberRun.gm
Normal file
691
vscript/languages/gm/scripts/BomberRun.gm
Normal file
@@ -0,0 +1,691 @@
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// BomberRun by George Allan
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
global score = 0;
|
||||
global height = 23;
|
||||
global gframe = 0;
|
||||
global detec = table();
|
||||
global killStars = 0;
|
||||
global playerx = 0;
|
||||
global playery = 0;
|
||||
global fireCnt = 0;
|
||||
global fireMax = 3;
|
||||
global clearExplo = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
global sync = function() {
|
||||
time = TICK();
|
||||
wait = (1./30.) - time;
|
||||
if(wait > 0) {
|
||||
sleep(wait);
|
||||
}
|
||||
TICK();
|
||||
global gframe = gframe + 1;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
global star = function() {
|
||||
|
||||
global detec;
|
||||
global killStars;
|
||||
|
||||
frame = 0;
|
||||
while (1) {
|
||||
|
||||
x = randint(1,79);
|
||||
y = randint(2,height);
|
||||
|
||||
// On top of a building ?
|
||||
if (y < detec[x]-1) {
|
||||
|
||||
// Print Star for a while
|
||||
t = randint(50,100);
|
||||
for (i=0;i<t;i=i+1) {
|
||||
// Choose a twinkle color
|
||||
if (randint(0,2) == 0) {
|
||||
CATTRIB(CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.B_BLUE);
|
||||
}
|
||||
else {
|
||||
CATTRIB(CA.F_RED|CA.F_GREEN | CA.B_BLUE);
|
||||
}
|
||||
|
||||
// Pop !
|
||||
if (i>t-2) {
|
||||
XYTEXT(x,y,",");
|
||||
}
|
||||
else {
|
||||
XYTEXT(x,y,".");
|
||||
}
|
||||
|
||||
// Sync to the master frame
|
||||
while (frame == gframe) {
|
||||
yield();
|
||||
if (killStars) {
|
||||
exit();
|
||||
}
|
||||
}
|
||||
frame = gframe;
|
||||
}
|
||||
|
||||
// Clear Star
|
||||
CATTRIB(CA.F_BLUE | CA.B_BLUE);
|
||||
XYTEXT(x,y," ");
|
||||
}
|
||||
yield();
|
||||
if (killStars) {
|
||||
exit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
global particle = function(x, y) {
|
||||
|
||||
global detec;
|
||||
global clearExplo;
|
||||
|
||||
pcoltab = table(CA.F_RED|CA.F_INTENSITY, CA.F_RED|CA.F_GREEN|CA.F_INTENSITY, CA.F_RED|CA.F_GREEN|CA.F_BLUE|CA.F_INTENSITY);
|
||||
pfrtab = table(".", "+", "*");
|
||||
|
||||
xmom = randfloat(-1.0,1.0);
|
||||
// ymom = randfloat(-0.2,-2.0);
|
||||
ymom = randfloat(-2.0,-0.2);
|
||||
|
||||
xpos = x.Float();
|
||||
ypos = y.Float();
|
||||
|
||||
frame = -1;
|
||||
|
||||
time = randint(10,40);
|
||||
from = time;
|
||||
|
||||
frint = randint(0,3);
|
||||
|
||||
while (1) {
|
||||
|
||||
if (y < detec[x]) {
|
||||
CATTRIB(CA.F_BLUE | CA.B_BLUE);
|
||||
XYTEXT(x,y," ");
|
||||
}
|
||||
xpos = xpos + xmom;
|
||||
ypos = ypos + ymom;
|
||||
ymom = ymom + 0.15f;
|
||||
x = xpos.Int();
|
||||
y = ypos.Int();
|
||||
|
||||
// On Screen
|
||||
if ( y >= height || y < 0 || x < 0 || x > 79) {
|
||||
exit();
|
||||
}
|
||||
|
||||
// Out of Time ?
|
||||
time = time - 1;
|
||||
if (time < 0 || clearExplo) {
|
||||
exit();
|
||||
}
|
||||
|
||||
if (y < detec[x]) {
|
||||
fr = (time*3) / from;
|
||||
CATTRIB(pcoltab[fr] | CA.B_BLUE);
|
||||
XYTEXT(x,y,pfrtab[frint]);
|
||||
}
|
||||
|
||||
// Sync to the master frame
|
||||
while (frame == gframe) {
|
||||
yield();
|
||||
}
|
||||
frame = gframe;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
global explode = function(x, y) {
|
||||
|
||||
global particle;
|
||||
|
||||
for (i=0;i<10;i=i+1) {
|
||||
thread(particle, x, y);
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
global bullet = function() {
|
||||
|
||||
global detec;
|
||||
global playerx;
|
||||
global playery;
|
||||
global score;
|
||||
global gframe;
|
||||
global fireCnt;
|
||||
global score;
|
||||
global explode;
|
||||
|
||||
firex = playerx;
|
||||
firey = playery+1;
|
||||
fireHTG = 4;
|
||||
frame = gframe;
|
||||
|
||||
score = score - 10;
|
||||
|
||||
while (1) {
|
||||
|
||||
// Detection
|
||||
if (firey > height-1 || fireHTG <= 0) {
|
||||
fireCnt = fireCnt - 1;
|
||||
exit();
|
||||
}
|
||||
else {
|
||||
// If okay - display
|
||||
CATTRIB(CA.F_RED|CA.F_GREEN|CA.F_BLUE|CA.F_INTENSITY | CA.B_BLUE);
|
||||
XYTEXT(firex,firey,"\31");
|
||||
}
|
||||
|
||||
// Sync to the master frame
|
||||
while (frame == gframe) {
|
||||
yield();
|
||||
}
|
||||
frame = gframe;
|
||||
|
||||
// Clear Bullet
|
||||
CATTRIB(CA.F_BLUE | CA.B_BLUE);
|
||||
XYTEXT(firex,firey," ");
|
||||
|
||||
firey = firey + 1;
|
||||
first = 0;
|
||||
|
||||
// Detection
|
||||
if (detec[firex] < firey) {
|
||||
score = score + 10;
|
||||
detec[firex] = firey;
|
||||
fireHTG = fireHTG - 1;
|
||||
// Boom !
|
||||
explode(firex,firey);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
game = function() {
|
||||
|
||||
global detec;
|
||||
global killStars;
|
||||
global star;
|
||||
global bullet;
|
||||
global playerx;
|
||||
global playery;
|
||||
global fireCnt;
|
||||
global fireMax;
|
||||
global score;
|
||||
global explode;
|
||||
|
||||
killStars = 0;
|
||||
allgone = 0;
|
||||
|
||||
for (i=0;i<30;i=i+1) {
|
||||
thread(star);
|
||||
}
|
||||
|
||||
CATTRIB(CA.F_BLUE | CA.B_BLUE);
|
||||
CLS();
|
||||
|
||||
coltab = table(CA.B_GREEN|CA.B_RED, CA.B_BLUE|CA.B_RED, CA.B_BLUE|CA.B_GREEN, CA.B_GREEN);
|
||||
coltabfore = table(CA.F_GREEN|CA.F_RED, CA.F_BLUE|CA.F_RED, CA.F_BLUE|CA.F_GREEN, CA.F_GREEN);
|
||||
btypes = table("\254", "\58", "\124");
|
||||
ttypes = table("\191", "\194", "\218");
|
||||
|
||||
// Clear Collision
|
||||
for (i=0;i<=150;i=i+1) {
|
||||
detec[i] = height;
|
||||
}
|
||||
|
||||
// Buildings
|
||||
for (x=0;x<80;x=x+1) {
|
||||
|
||||
// Calc Height
|
||||
rand = 10;
|
||||
if (x < 30) {
|
||||
rand = x / 3;
|
||||
}
|
||||
if (x > 50) {
|
||||
rand = (80-x) / 3;
|
||||
}
|
||||
if (x < 10) {
|
||||
rand = 0;
|
||||
}
|
||||
if (x > 70) {
|
||||
rand = 0;
|
||||
}
|
||||
rand = randint(0,rand);
|
||||
if (rand > 1) {
|
||||
|
||||
// Detection
|
||||
detec[x] = height-rand;
|
||||
|
||||
// Random Building Color
|
||||
col = randint(0,4);
|
||||
|
||||
// Random Top
|
||||
if (randint(0,10) < 2) {
|
||||
CATTRIB(coltabfore[col] | CA.B_BLUE);
|
||||
ttype = randint(0,3);
|
||||
XYTEXT(x,height-rand-1,ttypes[ttype]);
|
||||
}
|
||||
|
||||
// The Main Building
|
||||
btype = randint(0,3);
|
||||
CATTRIB(coltab[col] | CA.F_BLACK);
|
||||
for (y=height-rand;y<height;y=y+1) {
|
||||
if (randint(0,5) < 2) {
|
||||
CATTRIB(coltab[col] | CA.F_RED | CA.F_GREEN | CA.F_INTENSITY);
|
||||
XYTEXT(x,y,btypes[btype]);
|
||||
CATTRIB(coltab[col] | CA.F_BLACK);
|
||||
}
|
||||
else {
|
||||
XYTEXT(x,y,btypes[btype]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CATTRIB(CA.F_GREEN | CA.B_BLACK);
|
||||
for (x=0;x<80;x=x+1) {
|
||||
XYTEXT(x,height,"\178");
|
||||
}
|
||||
|
||||
playerx = 2;
|
||||
playery = 3;
|
||||
anim = 0;
|
||||
playermove = 0;
|
||||
fireOkay = 0;
|
||||
landingtimer = 0;
|
||||
|
||||
while(!ISPRESSED('Q')) {
|
||||
|
||||
// Flattened already ?
|
||||
allgone = 1;
|
||||
for (i=0;i<80;i=i+1) {
|
||||
if (detec[i] != height) {
|
||||
allgone = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Add Loads of stars
|
||||
if (ISPRESSED('S')) {
|
||||
for (i=0;i<30;i=i+1) {
|
||||
thread(star);
|
||||
}
|
||||
}
|
||||
|
||||
// Score
|
||||
CATTRIB(CA.F_BLUE | CA.F_GREEN | CA.F_INTENSITY | CA.B_BLUE);
|
||||
XY(35,1);
|
||||
print("Score ", score);
|
||||
|
||||
// Show Memeory Usage
|
||||
if (ISPRESSED('M')) {
|
||||
XY(1,2);
|
||||
print("Memory ", sysGetMemoryUsage());
|
||||
}
|
||||
|
||||
if (ISPRESSED(' ')) {
|
||||
if (fireCnt < fireMax && (fireOkay || fireMax == 50)) {
|
||||
thread(bullet);
|
||||
fireCnt = fireCnt + 1;
|
||||
}
|
||||
fireOkay = 0;
|
||||
}
|
||||
else {
|
||||
fireOkay = 1;
|
||||
}
|
||||
|
||||
// Clear Player
|
||||
CATTRIB(CA.F_BLUE | CA.B_BLUE);
|
||||
XYTEXT(playerx-2,playery," ");
|
||||
|
||||
// Auto Pilot landing
|
||||
if (allgone && playery < height-1) {
|
||||
landingtimer = landingtimer + 1;
|
||||
if (landingtimer > 10) {
|
||||
landingtimer = 0;
|
||||
playery = playery + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Move and reposition if reach the side of the screen
|
||||
playerx = playerx+1;
|
||||
if (playerx > 79) {
|
||||
playerx = 2;
|
||||
playery = playery + 1;
|
||||
}
|
||||
|
||||
// Not too low
|
||||
if (playery > height-1) {
|
||||
playery = height-1;
|
||||
}
|
||||
|
||||
// Print at new position
|
||||
CATTRIB(CA.F_RED|CA.F_GREEN|CA.F_BLUE|CA.F_INTENSITY | CA.B_BLUE);
|
||||
XYTEXT(playerx-2,playery,"\200");
|
||||
XYTEXT(playerx-1,playery,"\205");
|
||||
XYTEXT(playerx,playery,"\254");
|
||||
|
||||
// Landed !!!
|
||||
if (playerx == 60 && playery == height-1) {
|
||||
for (i=0;i<60;i=i+1) {
|
||||
sync();
|
||||
}
|
||||
// Completion bonus - based on fireMax
|
||||
if (score != 50) {
|
||||
score = score + (6-fireMax) * 1000;
|
||||
}
|
||||
niceone();
|
||||
return;
|
||||
}
|
||||
|
||||
// Hit a building (ignores arieals)
|
||||
if (detec[playerx+1] <= playery) {
|
||||
sync();
|
||||
|
||||
y0 = playery;
|
||||
y1 = playery;
|
||||
y2 = playery;
|
||||
x = playerx;
|
||||
|
||||
// Dramatic Pause !
|
||||
for (i=0;i<20;i=i+1) {
|
||||
XYTEXT(x-2,y0,"\200");
|
||||
XYTEXT(x-1,y1,"\205");
|
||||
XYTEXT(x-0,y2,"\254");
|
||||
sync();
|
||||
}
|
||||
|
||||
done = 0;
|
||||
while (!done) {
|
||||
done = 1;
|
||||
|
||||
// Clear
|
||||
CATTRIB(CA.F_BLUE | CA.B_BLUE);
|
||||
XYTEXT(x-2,y0," ");
|
||||
XYTEXT(x-1,y1," ");
|
||||
XYTEXT(x-0,y2," ");
|
||||
|
||||
// Move
|
||||
if (y0 < detec[x-2]-1) {
|
||||
y0 = y0 + 1;
|
||||
done = 0;
|
||||
}
|
||||
if (y1 < detec[x-1]-1) {
|
||||
y1 = y1 + 1;
|
||||
done = 0;
|
||||
}
|
||||
if (y2 < detec[x-0]-1) {
|
||||
y2 = y2 + 1;
|
||||
done = 0;
|
||||
}
|
||||
|
||||
// Print
|
||||
CATTRIB(CA.F_RED|CA.F_GREEN|CA.F_BLUE|CA.F_INTENSITY | CA.B_BLUE);
|
||||
XYTEXT(x-2,y0,"\200");
|
||||
XYTEXT(x-1,y1,"\205");
|
||||
XYTEXT(x-0,y2,"\254");
|
||||
sync();
|
||||
}
|
||||
|
||||
for (i=0;i<60;i=i+1) {
|
||||
XYTEXT(x-2,y0,"\200");
|
||||
XYTEXT(x-1,y1,"\205");
|
||||
XYTEXT(x-0,y2,"\254");
|
||||
sync();
|
||||
}
|
||||
gameover();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (anim == 1) {
|
||||
XYTEXT(playerx+1,playery,"\217");
|
||||
anim = 0;
|
||||
}
|
||||
else {
|
||||
XYTEXT(playerx+1,playery,"\191");
|
||||
anim = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Next Frame
|
||||
sync();
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
titlescreen = function() {
|
||||
|
||||
global killStars = 1;
|
||||
global fireMax = 0;
|
||||
global score = 0;
|
||||
global detec;
|
||||
global clearExplo = 0;
|
||||
|
||||
for (i=0;i<81;i=i+1) {
|
||||
detec[i] = height;
|
||||
}
|
||||
|
||||
CATTRIB(0);
|
||||
CLS();
|
||||
|
||||
CATTRIB(CA.F_RED);
|
||||
CURSOR(0,0);
|
||||
|
||||
maxtab = 12;
|
||||
tabindex = 0;
|
||||
tctab = table( CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY,
|
||||
CA.F_GREEN | CA.F_INTENSITY | CA.F_INTENSITY,
|
||||
CA.F_BLUE,
|
||||
CA.F_GREEN | CA.F_INTENSITY,
|
||||
CA.F_RED|CA.F_GREEN | CA.F_INTENSITY,
|
||||
CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY,
|
||||
CA.F_BLUE | CA.F_INTENSITY,
|
||||
CA.F_BLUE | CA.F_INTENSITY,
|
||||
CA.F_RED|CA.F_BLUE|CA.F_GREEN,
|
||||
CA.F_BLUE,
|
||||
CA.F_BLUE | CA.F_INTENSITY,
|
||||
CA.F_RED|CA.F_GREEN | CA.F_INTENSITY );
|
||||
|
||||
maxtab2 = 8;
|
||||
tabindex2 = 0;
|
||||
tctab2 = table( CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY,
|
||||
CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY,
|
||||
CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY,
|
||||
CA.F_RED|CA.F_GREEN | CA.F_INTENSITY,
|
||||
CA.F_RED | CA.F_INTENSITY,
|
||||
CA.F_RED,
|
||||
CA.F_RED | CA.F_INTENSITY,
|
||||
CA.F_RED|CA.F_GREEN | CA.F_INTENSITY );
|
||||
|
||||
|
||||
// Flush Keyboard !?
|
||||
ISPRESSED(' ');
|
||||
ISPRESSED(' ');
|
||||
ISPRESSED(' ');
|
||||
|
||||
while(!fireMax) {
|
||||
|
||||
// Start in which mode
|
||||
if (ISPRESSED('1')) { fireMax = 1; }
|
||||
if (ISPRESSED('2')) { fireMax = 2; }
|
||||
if (ISPRESSED('3')) { fireMax = 3; }
|
||||
if (ISPRESSED('4')) { fireMax = 4; }
|
||||
if (ISPRESSED('5')) { fireMax = 5; }
|
||||
if (ISPRESSED(' ')) { fireMax = 3; }
|
||||
if (ISPRESSED('C')) { fireMax = 50; }
|
||||
if (ISPRESSED(27)) { threadKillAll(true); } // Kill all threads when ESC is pressed
|
||||
|
||||
// Title Color
|
||||
tabindex = tabindex + 1;
|
||||
if (tabindex >= maxtab) {
|
||||
tabindex = 0;
|
||||
}
|
||||
CATTRIB(tctab[tabindex]);
|
||||
|
||||
CLS();
|
||||
XYTEXT(4,2,` ________ ______ ________ `);
|
||||
XYTEXT(4,3,` ___ __ )____________ ______ /________________ __ \___ ________ `);
|
||||
XYTEXT(4,4,` __ __ | __ \_ __ ``__ \_ __ \ _ \_ ___/_ /_/ / / / /_ __ \ `);
|
||||
XYTEXT(4,5,` _ /_/ // /_/ / / / / / / /_/ / __/ / _ _, _// /_/ /_ / / / `);
|
||||
XYTEXT(4,6,` /_____/ \____//_/ /_/ /_//_.___/\___//_/ /_/ |_| \__,_/ /_/ /_/ `);
|
||||
|
||||
// Text Color
|
||||
tabindex2 = tabindex2 + 1;
|
||||
if (tabindex2 >= maxtab2) {
|
||||
tabindex2 = 0;
|
||||
}
|
||||
CATTRIB(tctab2[tabindex2]);
|
||||
|
||||
XYTEXT(12,8, " Written in GameMonkey by Happy ");
|
||||
|
||||
XYTEXT(12,11, " Flatten the city by dropping bombs (spacebar)");
|
||||
XYTEXT(12,12, " Destory all buildings to land saftley");
|
||||
XYTEXT(12,13, " Score 10 points for each builing piece destoryed");
|
||||
XYTEXT(12,14, " Loose 10 points for each bomb dropped");
|
||||
XYTEXT(12,15, " Landing bonus is based on difficulty setting");
|
||||
|
||||
XYTEXT(12,18, " Press 'Space' to play at standard level");
|
||||
XYTEXT(12,19, " Press 1-5 to choose difficulty (number of bombs)");
|
||||
XYTEXT(12,20, "Press 'C' to play the cheat version (hold the spacebar!)");
|
||||
XYTEXT(12,21, " Press 'Q' to to quit during play");
|
||||
XYTEXT(12,22, " Press ESC to exit game now");
|
||||
|
||||
x = randint(2,78);
|
||||
y = randint(2,height-2);
|
||||
explode(x,y);
|
||||
|
||||
sync();
|
||||
}
|
||||
|
||||
clearExplo = 1;
|
||||
sync();
|
||||
sync();
|
||||
clearExplo = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
global niceone = function() {
|
||||
|
||||
global killStars = 0;
|
||||
global detec;
|
||||
global score;
|
||||
global clearExplo = 0;
|
||||
global fireMax;
|
||||
|
||||
for (i=0;i<500;i=i+1) {
|
||||
thread(star);
|
||||
}
|
||||
|
||||
for (i=0;i<200;i=i+1) {
|
||||
|
||||
CATTRIB(CA.F_RED|CA.F_GREEN|CA.F_BLUE|CA.F_INTENSITY|CA.B_BLUE);
|
||||
CLS();
|
||||
|
||||
XYTEXT(1,7,` .__ __. __ ______ _______ ______ .__ __. _______ __ __ __ `);
|
||||
XYTEXT(1,8,` | \ | || | / | ____| / __ \ | \ | || ____| | || || | `);
|
||||
XYTEXT(1,9,` | \| || || ,----' |__ | | | || \| || |__ | || || | `);
|
||||
XYTEXT(1,10,` | . `` || || | | __| | | | || . `` || __| | || || | `);
|
||||
XYTEXT(1,11,` | |\ || || ``----. |____ | ``--' || |\ || |____ |__||__||__| `);
|
||||
XYTEXT(1,12,` |__| \__||__| \______|_______| \______/ |__| \__||_______| (__)(__)(__) `);
|
||||
XY(1,15);
|
||||
if (score < 0) {
|
||||
print(" You score is to crap too mention !");
|
||||
}
|
||||
else if (score < 1500) {
|
||||
print(" Your score is a average", score);
|
||||
}
|
||||
else if (score < 3000) {
|
||||
print(" Your score is a respectable", score);
|
||||
}
|
||||
else {
|
||||
print(" Your score is an awesome", score);
|
||||
}
|
||||
if (fireMax == 50) {
|
||||
XY(1,17);
|
||||
print(" --- Now try again without cheating ;) ---");
|
||||
}
|
||||
|
||||
x = randint(2,78);
|
||||
y = randint(2,height-2);
|
||||
explode(x,y);
|
||||
|
||||
sync();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
global gameover = function() {
|
||||
|
||||
global killStars = 1;
|
||||
global detec;
|
||||
global score;
|
||||
global clearExplo = 1;
|
||||
|
||||
CATTRIB(CA.F_RED|CA.F_INTENSITY);
|
||||
for (i=height+15;i>-15;i=i-1) {
|
||||
CLS();
|
||||
y = i;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` _______ ___ .___ ___. _______ `); }
|
||||
y = y + 1;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` / _____| / \ | \/ || ____| `); }
|
||||
y = y + 1;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` | | __ / ^ \ | \ / || |__ `); }
|
||||
y = y + 1;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` | | |_ | / /_\ \ | |\/| || __| `); }
|
||||
y = y + 1;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` | |__| | / _____ \ | | | || |____ `); }
|
||||
y = y + 1;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` \______|/__/ _\__\|__|__|__||_______| `); }
|
||||
y = y + 1;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` / __ \ \ \ / /| ____| _ \ `); }
|
||||
y = y + 1;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` | | | | \ \/ / | |__ | |_) | `); }
|
||||
y = y + 1;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` | | | | \ / | __| | / `); }
|
||||
y = y + 1;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` | ``--' | \ / | |____| |\ \----. `); }
|
||||
y = y + 1;
|
||||
if (y >= 0 && y <= height) { XYTEXT(8,y,` \______/ \__/ |_______| _| ``._____| `); }
|
||||
y = y + 1;
|
||||
sync();
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// and finaly - the main loop !
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
while (1) {
|
||||
titlescreen();
|
||||
game();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
33
vscript/languages/gm/scripts/ScriptDescriptions.txt
Normal file
33
vscript/languages/gm/scripts/ScriptDescriptions.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
Descriptions of some of the example scripts
|
||||
|
||||
-----------------------------------------------------------
|
||||
|
||||
Snake.gm
|
||||
Bomber.gm
|
||||
BomberRun.gm
|
||||
MineSweeper.gm
|
||||
|
||||
Were written in a few hours by programmers testing an early
|
||||
version of GameMonkey. They were given the script
|
||||
reference, a copy of gme.exe and a syntax highlighter file.
|
||||
|
||||
-----------------------------------------------------------
|
||||
|
||||
gmDoc.gm (and related files)
|
||||
|
||||
Is a utility to scan through .cpp files, find comments
|
||||
relating to GM script bindings and generate a .html file
|
||||
documenting those functions.
|
||||
|
||||
-----------------------------------------------------------
|
||||
|
||||
benchmark.gm
|
||||
|
||||
Is a set of benchmarks used by Doug Bagley at
|
||||
http://www.bagley.org/~doug/shootout/
|
||||
to compare the performance of several languages.
|
||||
( Note that simply executing these scripts with
|
||||
different languages does not provide a true indication
|
||||
of comparative performance. )
|
||||
|
||||
-----------------------------------------------------------
|
||||
230
vscript/languages/gm/scripts/benchmarks.gm
Normal file
230
vscript/languages/gm/scripts/benchmarks.gm
Normal file
@@ -0,0 +1,230 @@
|
||||
// Set of benchmarks
|
||||
|
||||
sysSetDesiredMemoryUsageHard(16 * 1024, 1);
|
||||
sysSetDesiredMemoryUsageSoft(sysGetDesiredMemoryUsageHard());
|
||||
|
||||
//
|
||||
//
|
||||
// ACKERMAN
|
||||
//
|
||||
//
|
||||
|
||||
print("*** ACKERMAN ***");
|
||||
|
||||
global Ack = function(M, N)
|
||||
{
|
||||
if (M == 0)
|
||||
{
|
||||
N=N+1;
|
||||
return(N);
|
||||
}
|
||||
|
||||
if (N == 0)
|
||||
{
|
||||
M=M-1;
|
||||
return(Ack(M, 1));
|
||||
}
|
||||
N=N-1;
|
||||
return (Ack(M-1, Ack(M, N)));
|
||||
};
|
||||
|
||||
NUM = 8;
|
||||
TICK(); //GD Used to be clock() what was that matt?
|
||||
print(Ack(3,NUM));
|
||||
print("time = ", TICK());
|
||||
|
||||
//
|
||||
//
|
||||
// FIB
|
||||
//
|
||||
//
|
||||
|
||||
print("*** FIB ***");
|
||||
|
||||
global fib = function(n)
|
||||
{
|
||||
if (n < 2) { return(1); }
|
||||
return fib(n-2) + fib(n-1);
|
||||
};
|
||||
|
||||
N = 32;
|
||||
TICK();
|
||||
print(fib(N));
|
||||
print("time = ", TICK());
|
||||
|
||||
//
|
||||
//
|
||||
// MATRIX
|
||||
//
|
||||
//
|
||||
|
||||
print("*** MATRIX ***");
|
||||
|
||||
local n = 300;
|
||||
local size = 30;
|
||||
|
||||
mkmatrix = function(rows, cols)
|
||||
{
|
||||
count = 1;
|
||||
mx = table();
|
||||
|
||||
for(i=0; i < rows; i=i+1)
|
||||
{
|
||||
row = table();
|
||||
for(j = 0; j < cols; j=j+1)
|
||||
{
|
||||
row[j] = count;
|
||||
count=count+1;
|
||||
}
|
||||
mx[i] = row;
|
||||
}
|
||||
return mx;
|
||||
};
|
||||
|
||||
mmult = function(rows, cols, m1, m2)
|
||||
{
|
||||
m3 = table();
|
||||
|
||||
for(i = 0; i < rows; i=i+1)
|
||||
{
|
||||
m3[i] = table();
|
||||
m1_i = m1[i];
|
||||
for(j = 0; j < cols; j=j+1)
|
||||
{
|
||||
rowj = 0;
|
||||
for(k = 0; k < cols; k=k+1)
|
||||
{
|
||||
rowj = rowj + m1_i[k] * m2[k][j];
|
||||
}
|
||||
m3[i][j] = rowj;
|
||||
}
|
||||
}
|
||||
|
||||
return m3;
|
||||
};
|
||||
|
||||
TICK();
|
||||
m1 = mkmatrix(size, size);
|
||||
m2 = mkmatrix(size, size);
|
||||
|
||||
for(i = 0; i < n; i=i+1)
|
||||
{
|
||||
mm = mmult(size, size, m1, m2);
|
||||
}
|
||||
|
||||
t = TICK();
|
||||
|
||||
print(mm[0][0], mm[2][3], mm[3][2], mm[4][4]);
|
||||
print("time = ", t);
|
||||
|
||||
//
|
||||
//
|
||||
// HASH
|
||||
//
|
||||
//
|
||||
|
||||
print("*** HASH ***");
|
||||
|
||||
local n = 80000;
|
||||
TICK();
|
||||
|
||||
X=table();
|
||||
for(i=1; i <= n; i=i+1)
|
||||
{
|
||||
//print(format("%x", i), i);
|
||||
X[format("%x", i)] = i;
|
||||
}
|
||||
|
||||
c = 0;
|
||||
|
||||
for(i=n; i>=1; i=i-1)
|
||||
{
|
||||
if(X[i+""])
|
||||
{
|
||||
c=c+1;
|
||||
}
|
||||
}
|
||||
|
||||
print(c);
|
||||
print("time = ", TICK());
|
||||
|
||||
//
|
||||
//
|
||||
// HEAPSORT
|
||||
//
|
||||
//
|
||||
|
||||
print("*** HEAPSORT ***");
|
||||
|
||||
global IM = 139968.;
|
||||
global IA = 3877.;
|
||||
global IC = 29573.;
|
||||
global LAST = 42.;
|
||||
|
||||
gen_random = function(max)
|
||||
{
|
||||
global LAST = (LAST * IA + IC) % IM;
|
||||
return ((max * LAST) / IM);
|
||||
};
|
||||
|
||||
heapsort = function(n, ra)
|
||||
{
|
||||
l = n/2 + 1;
|
||||
ir = n;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if(l > 1)
|
||||
{
|
||||
l=l-1;
|
||||
rra = ra[l];
|
||||
}
|
||||
else
|
||||
{
|
||||
rra = ra[ir];
|
||||
ra[ir] = ra[1];
|
||||
ir=ir-1;
|
||||
if(ir == 1)
|
||||
{
|
||||
ra[1] = rra;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
i = l;
|
||||
j = l * 2;
|
||||
while(j <= ir)
|
||||
{
|
||||
if(j < ir and ra[j] < ra[j+1])
|
||||
{
|
||||
j=j+1;
|
||||
}
|
||||
if(rra < ra[j])
|
||||
{
|
||||
ra[i] = ra[j];
|
||||
i = j;
|
||||
j = j + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
j = ir + 1;
|
||||
}
|
||||
}
|
||||
|
||||
ra[i] = rra;
|
||||
}
|
||||
};
|
||||
|
||||
TICK();
|
||||
|
||||
local ary = table();
|
||||
local N = 80000;
|
||||
|
||||
for(i = 0; i < N; i=i+1)
|
||||
{
|
||||
ary[i] = gen_random(1.0);
|
||||
}
|
||||
|
||||
heapsort(N, ary);
|
||||
print(ary[N-1]);
|
||||
print("time = ", TICK());
|
||||
9
vscript/languages/gm/scripts/gmDoc/gmdoc.cmd
Normal file
9
vscript/languages/gm/scripts/gmDoc/gmdoc.cmd
Normal file
@@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
|
||||
rem Update GM Script function documentation
|
||||
|
||||
..\..\bin\gme.exe gmdoc.gm gmdoc.txt gmdoc.html
|
||||
|
||||
copy gmdoc.html ..\..\doc
|
||||
|
||||
rem Done.
|
||||
333
vscript/languages/gm/scripts/gmDoc/gmdoc.gm
Normal file
333
vscript/languages/gm/scripts/gmDoc/gmdoc.gm
Normal file
@@ -0,0 +1,333 @@
|
||||
//debug();
|
||||
|
||||
//
|
||||
// This program generates html documentation for script functions.
|
||||
// It merely converts comments with a certain syntax into documentation.
|
||||
// Example:
|
||||
//
|
||||
// /*gm
|
||||
// \lib Library Name
|
||||
// */
|
||||
//
|
||||
// /*gm
|
||||
// \function Function name
|
||||
// \brief Brief description of function
|
||||
// \param Parameter description, you should describe type information
|
||||
// \return Return value description
|
||||
// */
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// \function CreateGMDocumenter
|
||||
//
|
||||
global CreateGMDocumenter = function()
|
||||
{
|
||||
documenter = table
|
||||
(
|
||||
// \function ExtractGmCommentStrings
|
||||
// \param fp is source file handle
|
||||
// \param commentHandler is a function taking 2 params, comment string and context
|
||||
// \param context is passed to comment handler
|
||||
ExtractCommentStrings = function(fp, commentHandler, context)
|
||||
{
|
||||
openGmComment = "/*gm";
|
||||
openGmCommentLen = openGmComment.Length();
|
||||
closeGmComment = "*/";
|
||||
closeGmCommentLen = closeGmComment.Length();
|
||||
|
||||
line = fp.ReadLine();
|
||||
while(line)
|
||||
{
|
||||
pos = line.Find(openGmComment);
|
||||
while(pos >= 0)
|
||||
{
|
||||
pos = pos + openGmCommentLen;
|
||||
line = line.Right(line.Length() - pos);
|
||||
comment = "";
|
||||
|
||||
// eat up lines untill end of comment
|
||||
pos = line.Find(closeGmComment);
|
||||
while(pos < 0)
|
||||
{
|
||||
comment = comment + line;
|
||||
line = fp.ReadLine();
|
||||
|
||||
if(line == null)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = line.Find(closeGmComment);
|
||||
}
|
||||
}
|
||||
|
||||
if(line)
|
||||
{
|
||||
comment = comment + line.Left(pos);
|
||||
line = line.Right(line.Length() - (pos + closeGmCommentLen));
|
||||
pos = line.Find(openGmComment);
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = -1;
|
||||
}
|
||||
|
||||
if(comment.Length() > 0)
|
||||
{
|
||||
// process comment string
|
||||
comment = comment.ReplaceCharsInSet(' ', "\r\n\v");
|
||||
commentHandler(comment, context);
|
||||
}
|
||||
}
|
||||
line = fp.ReadLine();
|
||||
}
|
||||
},
|
||||
|
||||
// \function CommentHandler
|
||||
// \param comment is an incoming comment string
|
||||
// \param context is a table with a m_sections table where each m_section
|
||||
// is a function taking the section string and the context
|
||||
// context also has a BeginComment() call
|
||||
CommentHandler = function(comment, context)
|
||||
{
|
||||
if(comment and comment.Length())
|
||||
{
|
||||
commentSet = table();
|
||||
|
||||
foreach(section and sectionHandler in context.m_sections)
|
||||
{
|
||||
search = comment;
|
||||
sectionStart = search.Find(section);
|
||||
sectionLength = section.Length();
|
||||
offset = 0;
|
||||
|
||||
while(sectionStart >= 0)
|
||||
{
|
||||
commentSet[offset + sectionStart] = section;
|
||||
offset = offset + sectionLength;
|
||||
search = search.Right(search.Length() - sectionLength);
|
||||
sectionStart = search.Find(section);
|
||||
}
|
||||
}
|
||||
|
||||
// commentSet is now a table containing the start positions of each comment bit.
|
||||
length = comment.Length();
|
||||
local j;
|
||||
|
||||
for(i = 0; i < length; i = i + 1)
|
||||
{
|
||||
if(commentSet[i])
|
||||
{
|
||||
// find the next comment...
|
||||
for(j = i + 1; j < length; j = j + 1)
|
||||
{
|
||||
if(commentSet[j] or j == (length - 1))
|
||||
{
|
||||
section = commentSet[i];
|
||||
sectionLength = section.Length();
|
||||
first = i + sectionLength;
|
||||
count = (j - i) - sectionLength;
|
||||
subComment = comment.Mid(first, count);
|
||||
context.m_sections[section](subComment, context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
m_files = table(),
|
||||
|
||||
// \function AddFile will add a file to be documented
|
||||
AddFile = function(filename)
|
||||
{
|
||||
.m_files[tableCount(.m_files)] = filename;
|
||||
},
|
||||
|
||||
// \function CreateDocumentation will create documentation for all added files
|
||||
// \param path is the output path for the resulting .html docco
|
||||
CreateDocumentation = function(filename)
|
||||
{
|
||||
context = table();
|
||||
context.m_sections = table();
|
||||
context.m_htmlOut = system.File();
|
||||
context.m_xmlOut = system.File();
|
||||
context.m_lib = "";
|
||||
|
||||
context.m_sections[`\lib`] = function(comment, context)
|
||||
{
|
||||
comment = comment.TrimLeft().TrimRight();
|
||||
// trim parenthesis
|
||||
|
||||
context.Write("<BR><HR>\n");
|
||||
context.Heading(1, comment);
|
||||
context.Write("<HR><BR>\n");
|
||||
context.m_lib = comment;
|
||||
};
|
||||
context.m_sections[`\function`] = function(comment, context)
|
||||
{
|
||||
comment = comment.TrimLeft().TrimRight();
|
||||
// trim parenthesis
|
||||
|
||||
context.Write("<HR>\n");
|
||||
context.Write(format(`<a name="%s::%s">`,context.m_lib,comment));
|
||||
context.Heading(3, comment);
|
||||
context.Write(`</a>`);
|
||||
context.XMLWrite(format(`<function name="%s::%s"/>`, context.m_lib, comment));
|
||||
};
|
||||
context.m_sections[`\brief`] = function(comment, context)
|
||||
{
|
||||
context.Write(format("<B><EM>Brief:</B></EM>%s<BR>", comment));
|
||||
};
|
||||
context.m_sections[`\param`] = function(comment, context)
|
||||
{
|
||||
context.Write(format("<B><EM>Param:</B></EM>%s<BR>", comment));
|
||||
};
|
||||
context.m_sections[`\return`] = function(comment, context)
|
||||
{
|
||||
context.Write(format("<B><EM>Return:</B></EM>%s<BR>", comment));
|
||||
};
|
||||
context.m_sections[`\sa`] = function(comment, context)
|
||||
{
|
||||
context.Write(format("<B><EM>See Also:</B></EM>%s<BR>", comment));
|
||||
};
|
||||
context.Heading = function(number, string)
|
||||
{
|
||||
.m_htmlOut.WriteString(format("<H%d>%s</H%d>\n", number, string, number));
|
||||
};
|
||||
context.m_sections[`\this`] = function(comment, context)
|
||||
{
|
||||
context.Write(format("<B><EM>This:</B></EM>%s<BR>", comment));
|
||||
};
|
||||
context.Paragraph = function(string)
|
||||
{
|
||||
.m_htmlOut.WriteString(format("<P>%s</P>\n", string));
|
||||
};
|
||||
context.Write = function(string)
|
||||
{
|
||||
.m_htmlOut.WriteString(string);
|
||||
};
|
||||
context.XMLWrite = function(string)
|
||||
{
|
||||
if(.m_xmlOut)
|
||||
{
|
||||
.m_xmlOut.WriteString(string);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
xmlFilename = filename.SetExtension("xml");
|
||||
if(!context.m_xmlOut.Open(xmlFilename, 0))
|
||||
{
|
||||
print("** ERROR: Failed to open XML output file '",xmlFilename,"' ! **");
|
||||
context.m_xmlOut = null;
|
||||
}
|
||||
|
||||
context.XMLWrite(`<?xml version="1.0" encoding="utf-8"?>`);
|
||||
context.XMLWrite("<functions>");
|
||||
|
||||
//
|
||||
if(context.m_htmlOut.Open(filename, 0))
|
||||
{
|
||||
// write html head
|
||||
context.Write("<HTML><HEAD><TITLE>GM Documentation</TITLE></HEAD><BODY>");
|
||||
foreach(file in .m_files)
|
||||
{
|
||||
fp = system.File();
|
||||
if(fp.Open(file))
|
||||
{
|
||||
print(`Documenting`, file, `...`);
|
||||
|
||||
.ExtractCommentStrings(fp,.CommentHandler,context);
|
||||
|
||||
fp.Close();
|
||||
}
|
||||
}
|
||||
|
||||
context.XMLWrite("</functions>");
|
||||
if(context.m_xmlOut)
|
||||
|
||||
{
|
||||
context.m_xmlOut.Close();
|
||||
}
|
||||
|
||||
context.Write("</BODY></HTML>");
|
||||
context.m_htmlOut.Close();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return documenter;
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
* Entry Point
|
||||
*
|
||||
*/
|
||||
|
||||
print("Starting GMDoc...");
|
||||
|
||||
inFile = arg[0]; // directory/file listings of 'to be documented' files
|
||||
outFile = arg[1]; // ouput help files
|
||||
|
||||
documenter = CreateGMDocumenter();
|
||||
|
||||
dirsFile = system.File();
|
||||
|
||||
if(!dirsFile.Open(inFile, 1))
|
||||
{
|
||||
print("** Failed to open input file! **");
|
||||
}
|
||||
else
|
||||
{
|
||||
nextLine = dirsFile.ReadLine();
|
||||
|
||||
while(nextLine)
|
||||
{
|
||||
nextLine = nextLine.TrimRight(); // carriage return syndrome
|
||||
|
||||
path = nextLine;
|
||||
filename = nextLine.GetFilename();
|
||||
pos = filename.Find(".cpp",filename.Length() - 5);
|
||||
|
||||
if(pos >= 0) // doc file
|
||||
{
|
||||
if(system.FileExists(path))
|
||||
{
|
||||
documenter.AddFile(path);
|
||||
}
|
||||
}
|
||||
else // doc directory
|
||||
{
|
||||
handle = system.FileFindFirst(path ^ `\*.*`);
|
||||
while(handle)
|
||||
{
|
||||
extension = handle.filename.GetExtension().Lower();
|
||||
print(extension);
|
||||
if(extension == `cpp` or extension == `gm`)
|
||||
{
|
||||
if(system.FileExists(path ^ handle.filename))
|
||||
{
|
||||
documenter.AddFile(path ^ handle.filename);
|
||||
}
|
||||
}
|
||||
handle = system.FileFindNext(handle);
|
||||
}
|
||||
}
|
||||
|
||||
nextLine = dirsFile.ReadLine(); // read next line
|
||||
}
|
||||
}
|
||||
|
||||
dirsFile.Close();
|
||||
|
||||
documenter.CreateDocumentation(outFile);
|
||||
|
||||
print(`Done.`);
|
||||
|
||||
|
||||
|
||||
308
vscript/languages/gm/scripts/gmDoc/gmdoc.html
Normal file
308
vscript/languages/gm/scripts/gmDoc/gmdoc.html
Normal file
@@ -0,0 +1,308 @@
|
||||
<HTML><HEAD><TITLE>GM Documentation</TITLE></HEAD><BODY><BR><HR>
|
||||
<H1>gm</H1>
|
||||
<HR><BR>
|
||||
<HR>
|
||||
<a name="gm::array"><H3>array</H3>
|
||||
</a><B><EM>Brief:</B></EM> array will create a fixed size array object <BR><B><EM>Param:</B></EM> int size optional (0) <BR><B><EM>Return:</B></EM> array <BR><BR><HR>
|
||||
<H1>array</H1>
|
||||
<HR><BR>
|
||||
<HR>
|
||||
<a name="array::Size"><H3>Size</H3>
|
||||
</a><B><EM>Brief:</B></EM> Size will return the current size of the fixed array <BR><B><EM>Return:</B></EM> int array size <BR><HR>
|
||||
<a name="array::Resize"><H3>Resize</H3>
|
||||
</a><B><EM>Brief:</B></EM> Resize will resize the array to a new size <BR><B><EM>Param:</B></EM> int size optional (0) <BR><B><EM>Return:</B></EM> null <BR><HR>
|
||||
<a name="array::Shift"><H3>Shift</H3>
|
||||
</a><B><EM>Brief:</B></EM> Shift will shift slide the array elements by a delta, nulls are shifted in <BR><B><EM>Param:</B></EM> int delta <BR><B><EM>Return:</B></EM> null <BR><HR>
|
||||
<a name="array::Move"><H3>Move</H3>
|
||||
</a><B><EM>Brief:</B></EM> Move will perform a non destructive move on the array <BR><B><EM>Param:</B></EM> int dst <BR><B><EM>Param:</B></EM> int src <BR><B><EM>Param:</B></EM> int size <BR><B><EM>Return:</B></EM> null <BR><BR><HR>
|
||||
<H1>math</H1>
|
||||
<HR><BR>
|
||||
<HR>
|
||||
<a name="math::abs"><H3>abs</H3>
|
||||
</a><B><EM>Brief:</B></EM> abs will return the absolute value of the passed int \ float <BR><B><EM>Param:</B></EM> int\float <BR><B><EM>Return:</B></EM> int\float abs(param) <BR><HR>
|
||||
<a name="math::sqrt"><H3>sqrt</H3>
|
||||
</a><B><EM>Brief:</B></EM> sqrt will return the square root of the passed int \ float <BR><B><EM>Param:</B></EM> int\float <BR><B><EM>Return:</B></EM> int\float sqrt(param) <BR><HR>
|
||||
<a name="math::sqrt"><H3>sqrt</H3>
|
||||
</a><B><EM>Brief:</B></EM> sqrt will return the square root of the passed int \ float <BR><B><EM>Param:</B></EM> int\float A <BR><B><EM>Param:</B></EM> int\float B <BR><B><EM>Return:</B></EM> int\float A to the power of B <BR><HR>
|
||||
<a name="math::floor"><H3>floor</H3>
|
||||
</a><B><EM>Brief:</B></EM> floor <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float floor(A) <BR><HR>
|
||||
<a name="math::ceil"><H3>ceil</H3>
|
||||
</a><B><EM>Brief:</B></EM> ceil <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float ceil(A) <BR><HR>
|
||||
<a name="math::round"><H3>round</H3>
|
||||
</a><B><EM>Brief:</B></EM> round <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float round(A) <BR><HR>
|
||||
<a name="math::degtorad"><H3>degtorad</H3>
|
||||
</a><B><EM>Brief:</B></EM> degtorad will convert degrees to radians <BR><B><EM>Param:</B></EM> float\int deg <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::radtodeg"><H3>radtodeg</H3>
|
||||
</a><B><EM>Brief:</B></EM> radtodeg will convert radians to degrees <BR><B><EM>Param:</B></EM> float\int rad <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::cos"><H3>cos</H3>
|
||||
</a><B><EM>Brief:</B></EM> cos will return the radian cosine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::sin"><H3>sin</H3>
|
||||
</a><B><EM>Brief:</B></EM> sin will return the radian sine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::tan"><H3>tan</H3>
|
||||
</a><B><EM>Brief:</B></EM> tan will return the radian tan (sin/cos) <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::acos"><H3>acos</H3>
|
||||
</a><B><EM>Brief:</B></EM> acos will return the radian arc cosine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::asin"><H3>asin</H3>
|
||||
</a><B><EM>Brief:</B></EM> asin will return the radian arc sine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::atan"><H3>atan</H3>
|
||||
</a><B><EM>Brief:</B></EM> atan will return the radian arc tangent <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::atan"><H3>atan</H3>
|
||||
</a><B><EM>Brief:</B></EM> atan will return the radian arc tangent of x / y <BR><B><EM>Param:</B></EM> float x <BR><B><EM>Param:</B></EM> float y <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::log"><H3>log</H3>
|
||||
</a><B><EM>Brief:</B></EM> log will return the natural logarithm of 1 parameter, or (base, value) the logarithm to base <BR><B><EM>Param:</B></EM> float natural \ base <BR><B><EM>Param:</B></EM> float value (optional) <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::min"><H3>min</H3>
|
||||
</a><B><EM>Brief:</B></EM> min will return the min of the 2 passed values <BR><B><EM>Param:</B></EM> float\int A <BR><B><EM>Param:</B></EM> float\int B <BR><B><EM>Return:</B></EM> float \ int min(A, B) <BR><HR>
|
||||
<a name="math::max"><H3>max</H3>
|
||||
</a><B><EM>Brief:</B></EM> max will return the max of the 2 passed values <BR><B><EM>Param:</B></EM> float\int A <BR><B><EM>Param:</B></EM> float\int B <BR><B><EM>Return:</B></EM> float \ int max(A, B) <BR><HR>
|
||||
<a name="math::clamp"><H3>clamp</H3>
|
||||
</a><B><EM>Brief:</B></EM> clamp will return the clamed value. clamp(min, val, max) <BR><B><EM>Param:</B></EM> float\int MIN <BR><B><EM>Param:</B></EM> float\int VALUE <BR><B><EM>Param:</B></EM> float\int MAX <BR><B><EM>Return:</B></EM> float\int value clamped to min, max <BR><HR>
|
||||
<a name="math::randint"><H3>randint</H3>
|
||||
</a><B><EM>Brief:</B></EM> randint will return a random int from lower inclusive to upper. <BR><B><EM>Param:</B></EM> int lower inclusive <BR><B><EM>Param:</B></EM> int upper <BR><B><EM>Return:</B></EM> int <BR><HR>
|
||||
<a name="math::randfloat"><H3>randfloat</H3>
|
||||
</a><B><EM>Brief:</B></EM> randfloat will return a random float from lower inclusive to upper. <BR><B><EM>Param:</B></EM> float lower inclusive <BR><B><EM>Param:</B></EM> float upper <BR><B><EM>Return:</B></EM> float <BR><HR>
|
||||
<a name="math::randseed"><H3>randseed</H3>
|
||||
</a><B><EM>Brief:</B></EM> randseed will seed the random number generator <BR><B><EM>Param:</B></EM> int seed <BR><BR><HR>
|
||||
<H1>string</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> string operations often store a copy of the string on the stack, so keep string sizes reasonable <BR><HR>
|
||||
<a name="string::IsEmpty"><H3>IsEmpty</H3>
|
||||
</a><B><EM>Brief:</B></EM> IsEmpty will test to see if the string is 0 length <BR><B><EM>Return:</B></EM> non-zero if the string is empty <BR><HR>
|
||||
<a name="string::Length"><H3>Length</H3>
|
||||
</a><B><EM>Brief:</B></EM> Length will return the length of the string not including the null terminating character <BR><B><EM>Return:</B></EM> int length <BR><HR>
|
||||
<a name="string::Left"><H3>Left</H3>
|
||||
</a><B><EM>Brief:</B></EM> Left will return the left count charaters of the string <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Right"><H3>Right</H3>
|
||||
</a><B><EM>Brief:</B></EM> Right will return the right count charaters of the string <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::RightAt"><H3>RightAt</H3>
|
||||
</a><B><EM>Brief:</B></EM> RightAt will return the charaters right of and including the given index <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Mid"><H3>Mid</H3>
|
||||
</a><B><EM>Brief:</B></EM> Mid will return count characters from the start index <BR><B><EM>Param:</B></EM> int startIndex <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Compare"><H3>Compare</H3>
|
||||
</a><B><EM>Brief:</B></EM> Compare will perform a string compare <BR><B><EM>Param:</B></EM> string to compare <BR><B><EM>Return:</B></EM> -1 if the this < compare string, 0 if the strings are equal, 1 otherwise <BR><HR>
|
||||
<a name="string::CompareNoCase"><H3>CompareNoCase</H3>
|
||||
</a><B><EM>Brief:</B></EM> CompareNoCase will perform a string compare (case insensitive) <BR><B><EM>Param:</B></EM> string to compare <BR><B><EM>Return:</B></EM> -1 if the this < compare string, 0 if the strings are equal, 1 otherwise <BR><HR>
|
||||
<a name="string::Int"><H3>Int</H3>
|
||||
</a><B><EM>Brief:</B></EM> Int will return the int value of the string <BR><B><EM>Return:</B></EM> int value <BR><HR>
|
||||
<a name="string::Float"><H3>Float</H3>
|
||||
</a><B><EM>Brief:</B></EM> Float will return the float value of the string <BR><B><EM>Return:</B></EM> float value <BR><HR>
|
||||
<a name="string::String"><H3>String</H3>
|
||||
</a><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Upper"><H3>Upper</H3>
|
||||
</a><B><EM>Brief:</B></EM> Upper will return the string as uppercase <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Lower"><H3>Lower</H3>
|
||||
</a><B><EM>Brief:</B></EM> Lower will return the string as lowercase <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::SpanIncluding"><H3>SpanIncluding</H3>
|
||||
</a><B><EM>Brief:</B></EM> SpanIncluding will return this string while characters are within the passed string <BR><B><EM>Param:</B></EM> string charset <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::SpanExcluding"><H3>SpanExcluding</H3>
|
||||
</a><B><EM>Brief:</B></EM> SpanExcluding will return this string while characters are not within the passed string <BR><B><EM>Param:</B></EM> string charset <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::AppendPath"><H3>AppendPath</H3>
|
||||
</a><B><EM>Brief:</B></EM> AppendPath will append a path make sure one '\' is maintained <BR><B><EM>Param:</B></EM> string path to append <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::ReplaceCharsInSet"><H3>ReplaceCharsInSet</H3>
|
||||
</a><B><EM>Brief:</B></EM> ReplaceCharsInSet will replace all chars in this that are within the charset with the given int char <BR><B><EM>Param:</B></EM> int char to replace with <BR><B><EM>Param:</B></EM> string charset of chars to replace <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::Find"><H3>Find</H3>
|
||||
</a><B><EM>Brief:</B></EM> Find will find the first occurance of the passed string within this string <BR><B><EM>Param:</B></EM> string search string <BR><B><EM>Param:</B></EM> int start index optional (0) <BR><B><EM>Return:</B></EM> int index of first occurance, or -1 if the string was not found <BR><HR>
|
||||
<a name="string::Reverse"><H3>Reverse</H3>
|
||||
</a><B><EM>Brief:</B></EM> Reverse characters of a string <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::ReverseFind"><H3>ReverseFind</H3>
|
||||
</a><B><EM>Brief:</B></EM> ReverseFind will find the first occurance of the passed string within this string starting from the right <BR><B><EM>Param:</B></EM> string search string <BR><B><EM>Return:</B></EM> int index of first occurance, or -1 if the string was not found <BR><HR>
|
||||
<a name="string::GetAt"><H3>GetAt</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetAt will return the char at the given index <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Return:</B></EM> int char, or null if index was out of range <BR><HR>
|
||||
<a name="string::SetAt"><H3>SetAt</H3>
|
||||
</a><B><EM>Brief:</B></EM> SetAt will return the string with the character set at the given position <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Param:</B></EM> int char <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::TrimLeft"><H3>TrimLeft</H3>
|
||||
</a><B><EM>Brief:</B></EM> TrimLeft will return the string with the chars from the passed char set trimmed from the left <BR><B><EM>Param:</B></EM> string charset optional (" \r\n\v\t") <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::TrimRight"><H3>TrimRight</H3>
|
||||
</a><B><EM>Brief:</B></EM> TrimRight will return the string with the chars from the passed char set trimmed from the right <BR><B><EM>Param:</B></EM> string charset optional (" \r\n\v\t") <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::GetFilenameNoExt"><H3>GetFilenameNoExt</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetFilenameNoExt will return the filename part of a path string <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::GetFilename"><H3>GetFilename</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetFilename will return the filename part of a path string incl. extension <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::GetExtension"><H3>GetExtension</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetExtension will return the file extension <BR><B><EM>Param:</B></EM> int inclDot optional (0) 1 will include '.', 0 won't <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::SetExtension"><H3>SetExtension</H3>
|
||||
</a><B><EM>Brief:</B></EM> SetExtension returns a string with the extension change to the given one. <BR><B><EM>Param:</B></EM> string ext optional (null) the new extension, with or without the dot. null to remove extension. <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="string::GetPath"><H3>GetPath</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetPath will return the file path from a path string <BR><B><EM>Param:</B></EM> int inclSlash optional (0) will include a '\' on the end of the path <BR><B><EM>Return:</B></EM> string <BR><BR><HR>
|
||||
<H1>system</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> system functions are bound in a "system" table. <BR><HR>
|
||||
<a name="system::Exec"><H3>Exec</H3>
|
||||
</a><B><EM>Brief:</B></EM> Exec will execute a system command <BR><B><EM>Param:</B></EM> string params will be concatinated together with a single space to form the final system command string <BR><B><EM>Return:</B></EM> integer value returned from system exec call, -1 on error <BR><HR>
|
||||
<a name="system::DoFile"><H3>DoFile</H3>
|
||||
</a><B><EM>Brief:</B></EM> DoFile will execute the gm script in the named file <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int optional (1) as 1 will execute string before returning, 0 will execute later. <BR><B><EM>Param:</B></EM> ref optional (null) set 'this' <BR><B><EM>Return:</B></EM> thread id of new thread created to execute file <BR><HR>
|
||||
<a name="system::File"><H3>File</H3>
|
||||
</a><B><EM>Brief:</B></EM> File will create a file object <BR><B><EM>Return:</B></EM> file object <BR><HR>
|
||||
<a name="system::FileExists"><H3>FileExists</H3>
|
||||
</a><B><EM>Brief:</B></EM> FileExists will test to see if a file exists <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Return:</B></EM> 1 if the file exists, otherwise 0 <BR><HR>
|
||||
<a name="system::FileFindFirst"><H3>FileFindFirst</H3>
|
||||
</a><B><EM>Brief:</B></EM> FileFindFirst will start a file search will test to see if a file exists <BR><B><EM>Param:</B></EM> string filesearch (may contain wildcards, eg, `c:\temp\*.txt`) <BR><B><EM>Return:</B></EM> fileFind object. fileFind object has .filename and .size member <BR><B><EM>See Also:</B></EM> fileFind <BR><HR>
|
||||
<a name="system::FileFindNext"><H3>FileFindNext</H3>
|
||||
</a><B><EM>Brief:</B></EM> FileFindNext will get the next file matching the file find <BR><B><EM>Param:</B></EM> fileFind object returned by FileFindFirst call or FileFindNext call <BR><B><EM>Return:</B></EM> fileFind object <BR><B><EM>See Also:</B></EM> fileFind <BR><HR>
|
||||
<a name="system::FileInfo"><H3>FileInfo</H3>
|
||||
</a><B><EM>Brief:</B></EM> FileInfo will return a file info object that has readonly members for .creationDate <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Return:</B></EM> fileInfo object, fileInfo object has a .creationTime, .accessedTime, .modifiedTime and a .size <BR><HR>
|
||||
<a name="system::CreateFolder"><H3>CreateFolder</H3>
|
||||
</a><B><EM>Brief:</B></EM> CreateFolder will create a file path if it does not already exist <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Return:</B></EM> int 0 on failure, 1 on successful create, 2 if folder already exists <BR><HR>
|
||||
<a name="system::DeleteFolder"><H3>DeleteFolder</H3>
|
||||
</a><B><EM>Brief:</B></EM> DeleteFolder will remove a file path <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Param:</B></EM> int remove subfiles and folders optional (0) <BR><B><EM>Return:</B></EM> int 1 on success, 0 con failure <BR><HR>
|
||||
<a name="system::Time"><H3>Time</H3>
|
||||
</a><B><EM>Brief:</B></EM> Time will return a unix style time_t as an int <BR><B><EM>Return:</B></EM> the current time <BR><HR>
|
||||
<a name="system::FormatTime"><H3>FormatTime</H3>
|
||||
</a><B><EM>Brief:</B></EM> FormatTime will take a int (time_t) value and format according to the passed format string. <BR><B><EM>Param:</B></EM> int time (-1) is a (time_t) to be converted to a string, passing -1 gets current time <BR><B><EM>Param:</B></EM> string format ("%A %d %B %Y, %I:%M:%S %p") is the format string to use.<BR> %a : Abbreviated weekday name<BR> %A : Full weekday name<BR> %b : Abbreviated month name<BR> %B : Full month name<BR> %c : Date and time representation appropriate for locale<BR> %d : Day of month as decimal number (01 <20> 31)<BR> %H : Hour in 24-hour format (00 <20> 23)<BR> %I : Hour in 12-hour format (01 <20> 12)<BR> %j : Day of year as decimal number (001 <20> 366)<BR> %m : Month as decimal number (01 <20> 12)<BR> %M : Minute as decimal number (00 <20> 59)<BR> %p : Current locale<6C>s A.M./P.M. indicator for 12-hour clock<BR> %S : Second as decimal number (00 <20> 59)<BR> %U : Week of year as decimal number, with Sunday as first day of week (00 <20> 53)<BR> %w : Weekday as decimal number (0 <20> 6; Sunday is 0)<BR> %W : Week of year as decimal number, with Monday as first day of week (00 <20> 53)<BR> %x : Date representation for current locale<BR> %X : Time representation for current locale<BR> %y : Year without century, as decimal number (00 <20> 99)<BR> %Y : Year with century, as decimal number<BR> %z, %Z : Time-zone name or abbreviation; no characters if time zone is unknown<BR> %% : Percent sign<BR> <BR><B><EM>Return:</B></EM> the time as a string. <BR><BR><HR>
|
||||
<H1>fileFind</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> fileFind object has a "filename" and "size" member <BR><HR>
|
||||
<a name="fileFind::GetAttribute"><H3>GetAttribute</H3>
|
||||
</a><B><EM>Brief:</B></EM> GetAttribute will test a file attribute. <BR><B><EM>Param:</B></EM> int char attribute 'r' readonly, 'a' archive, 's' system, 'h' hidden, 'c' compressed, 'd' directory <BR><B><EM>Return:</B></EM> 1 if the attribute is set, 0 otherwise <BR><BR><HR>
|
||||
<H1>file</H1>
|
||||
<HR><BR>
|
||||
<HR>
|
||||
<a name="file::Open"><H3>Open</H3>
|
||||
</a><B><EM>Brief:</B></EM> Open will open a file in binary mode <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int readonly optional (1) <BR><B><EM>Return:</B></EM> 1 if the open was successful, 0 otherwise <BR><HR>
|
||||
<a name="file::OpenText"><H3>OpenText</H3>
|
||||
</a><B><EM>Brief:</B></EM> OpenText will open a file in text mode <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int readonly optional (1) <BR><B><EM>Return:</B></EM> 1 if the open was successful, 0 otherwise <BR><HR>
|
||||
<a name="file::Close"><H3>Close</H3>
|
||||
</a><B><EM>Brief:</B></EM> Close will close a file <BR><HR>
|
||||
<a name="file::IsOpen"><H3>IsOpen</H3>
|
||||
</a><B><EM>Brief:</B></EM> IsOpen will test to see if a file is open <BR><B><EM>Return:</B></EM> 1 if the file is open, 0 otherwise <BR><HR>
|
||||
<a name="file::Seek"><H3>Seek</H3>
|
||||
</a><B><EM>Brief:</B></EM> Move to position within file <BR><B><EM>Param:</B></EM> int offset positional offset relative to origin <BR><B><EM>Param:</B></EM> int origin eg. myFile.SEEK_CUR, myFile.SEEK_END, myFile.SEEK_SET for current, end, start origins. <BR><B><EM>Return:</B></EM> int 1 if operation succeeded, 0 if failed. <BR><HR>
|
||||
<a name="file::Tell"><H3>Tell</H3>
|
||||
</a><B><EM>Brief:</B></EM> Tell will return the current cursor position of the file <BR><B><EM>Return:</B></EM> int the current cursor position, -1 on error <BR><HR>
|
||||
<a name="file::ReadLine"><H3>ReadLine</H3>
|
||||
</a><B><EM>Brief:</B></EM> ReadLine will read a line of text from the file. <BR><B><EM>Param:</B></EM> int keep optional (0) as 1 will keep the "\n" char on the line, otherwise it is removed <BR><B><EM>Return:</B></EM> string, or null on eof <BR><HR>
|
||||
<a name="file::ReadChar"><H3>ReadChar</H3>
|
||||
</a><B><EM>Brief:</B></EM> ReadChar will read a char from the file <BR><B><EM>Return:</B></EM> int char or null on eof <BR><HR>
|
||||
<a name="file::WriteString"><H3>WriteString</H3>
|
||||
</a><B><EM>Brief:</B></EM> WriteString will write a string to the file <BR><B><EM>Param:</B></EM> string to write to file <BR><B><EM>Return:</B></EM> 1 on success, null on error <BR><HR>
|
||||
<a name="file::WriteChar"><H3>WriteChar</H3>
|
||||
</a><B><EM>Brief:</B></EM> WriteChar will write a char to the file <BR><B><EM>Param:</B></EM> int char to write to file <BR><B><EM>Return:</B></EM> 1 on success, null on error <BR><BR><HR>
|
||||
<H1>Vector3</H1>
|
||||
<HR><BR>
|
||||
<HR>
|
||||
<a name="Vector3::Vector3"><H3>Vector3</H3>
|
||||
</a><B><EM>Brief:</B></EM> Create a Vector3 object <BR><B><EM>Param:</B></EM> float x or [0] optional (0) <BR><B><EM>Param:</B></EM> float y or [1] optional (0) <BR><B><EM>Param:</B></EM> float z or [2] optional (0) <BR><BR><HR>
|
||||
<H1>Vector3</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> Vector3 math class <BR><HR>
|
||||
<a name="Vector3::DominantAxis"><H3>DominantAxis</H3>
|
||||
</a><B><EM>Brief:</B></EM> Find the index of the largest vector component. <BR><B><EM>This:</B></EM> Vector to evaluate. <BR><B><EM>Return:</B></EM> int Index of largest component. <BR><HR>
|
||||
<a name="Vector3::Dot"><H3>Dot</H3>
|
||||
</a><B><EM>Brief:</B></EM> Calculate the Dot (or Inner) Product of two vectors. <BR><B><EM>This:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><B><EM>Return:</B></EM> float result. <BR><HR>
|
||||
<a name="Vector3::Length"><H3>Length</H3>
|
||||
</a><B><EM>Brief:</B></EM> Length will return the length of the vector. <BR><B><EM>Return:</B></EM> float Dot product result that is cosine of the angle between the two vectors. <BR><HR>
|
||||
<a name="Vector3::Cross"><H3>Cross</H3>
|
||||
</a><B><EM>Brief:</B></EM> Calculate the Cross (or Outer) Product of two vectors. <BR><B><EM>This:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><B><EM>Return:</B></EM> Vector3 Cross product resultant vector that is perpendicular to the two input vectors and length sine of the angle between them. <BR><HR>
|
||||
<a name="Vector3::Normalize"><H3>Normalize</H3>
|
||||
</a><B><EM>Brief:</B></EM> Return a unit length copy of this vector. <BR><B><EM>This:</B></EM> Vector to be copied. <BR><B><EM>Return:</B></EM> Vector3 Unit length copy of this vector. <BR><HR>
|
||||
<a name="Vector3::LengthSquared"><H3>LengthSquared</H3>
|
||||
</a><B><EM>Brief:</B></EM> Return the squared length of the vector. <BR><B><EM>Return:</B></EM> float Squared length of the vector. <BR><HR>
|
||||
<a name="Vector3::ProjectFrom"><H3>ProjectFrom</H3>
|
||||
</a><B><EM>Brief:</B></EM> Project a direction from a point. <BR><B><EM>This:</B></EM> Vector3 Direction. <BR><B><EM>Param:</B></EM> Vector3 Start point; <BR><B><EM>Param:</B></EM> float Distance or time. <BR><B><EM>Return:</B></EM> Vector3 Projected result. <BR><HR>
|
||||
<a name="Vector3::Clone"><H3>Clone</H3>
|
||||
</a><B><EM>Brief:</B></EM> Return a copy of this vector. <BR><B><EM>Return:</B></EM> A copy of this vector. <BR><HR>
|
||||
<a name="Vector3::Set"><H3>Set</H3>
|
||||
</a><B><EM>Brief:</B></EM> Set vector from other vector or 3 components. <BR><HR>
|
||||
<a name="Vector3::LerpToPoint"><H3>LerpToPoint</H3>
|
||||
</a><B><EM>Brief:</B></EM> Linear interpolate between two 'point' vectors. <BR><B><EM>This:</B></EM> Vector3 From vector. <BR><B><EM>Param:</B></EM> Vector3 To vector. <BR><B><EM>Param:</B></EM> float Fraction or time between 0 and 1. <BR><B><EM>Return:</B></EM> Vector3 Resulting inbetween vector. <BR><HR>
|
||||
<a name="Vector3::SlerpToVector"><H3>SlerpToVector</H3>
|
||||
</a><B><EM>Brief:</B></EM> Spherical linear interpolate between two vectors. <BR><B><EM>This:</B></EM> Vector3 From vector. <BR><B><EM>Param:</B></EM> Vector3 To vector. <BR><B><EM>Param:</B></EM> float Fraction or time between 0 and 1. <BR><B><EM>Return:</B></EM> Vector3 Resulting inbetween vector. <BR><HR>
|
||||
<a name="Vector3::RotateAxisAngle"><H3>RotateAxisAngle</H3>
|
||||
</a><B><EM>Brief:</B></EM> Rotate around Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> Vector3 Unit length axis of rotation. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
|
||||
<a name="Vector3::RotateX"><H3>RotateX</H3>
|
||||
</a><B><EM>Brief:</B></EM> Rotate around X Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
|
||||
<a name="Vector3::RotateX"><H3>RotateX</H3>
|
||||
</a><B><EM>Brief:</B></EM> Rotate around Y Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
|
||||
<a name="Vector3::RotateZ"><H3>RotateZ</H3>
|
||||
</a><B><EM>Brief:</B></EM> Rotate around Z Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
|
||||
<a name="Vector3::SetAdd"><H3>SetAdd</H3>
|
||||
</a><B><EM>Brief:</B></EM> Add two vectors, store result in this. Demonstrate relative efficiency compared to operator version. <BR><B><EM>This:</B></EM> Vector3 Result vector. <BR><B><EM>Param:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><HR>
|
||||
<a name="Vector3::Add"><H3>Add</H3>
|
||||
</a><B><EM>Brief:</B></EM> Add vector to this. Demonstrate relative efficiency compared to operator version. <BR><B><EM>This:</B></EM> Vector3 Result vector. <BR><B><EM>Param:</B></EM> Vector3 vector to add. <BR><BR><HR>
|
||||
<H1>gm</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> functions in the gm lib are all global scope <BR><HR>
|
||||
<a name="gm::debug"><H3>debug</H3>
|
||||
</a><B><EM>Brief:</B></EM> debug will cause a the debugger to break at this point while running. <BR><BR><HR>
|
||||
<H1>gm</H1>
|
||||
<HR><BR>
|
||||
<B><EM>Brief:</B></EM> functions in the gm lib are all global scope <BR><HR>
|
||||
<a name="gm::gmVersion"><H3>gmVersion</H3>
|
||||
</a><B><EM>Brief:</B></EM> gmVersion will return the gmMachine version string. version string is major type . minor type as a string and was added at version 1.1 <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="gm::typeId"><H3>typeId</H3>
|
||||
</a><B><EM>Brief:</B></EM> typeId will return the type id of the passed var <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> integer type <BR><HR>
|
||||
<a name="gm::typeName"><H3>typeName</H3>
|
||||
</a><B><EM>Brief:</B></EM> typeName will return the type name of the passed var <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> string <BR><HR>
|
||||
<a name="gm::typeRegisterOperator"><H3>typeRegisterOperator</H3>
|
||||
</a><B><EM>Brief:</B></EM> typeRegisterOperator will register an operator for a type <BR><B><EM>Param:</B></EM> int typeid <BR><B><EM>Param:</B></EM> string operator name is one of "getdot", "setdot", "getind", "setind", "add", "sub", "mul", "div", "mod", "inc", "dec", "bitor", "bitxor", "bitand", "shiftleft", "shiftright", "bitinv", "lt", "gt", "lte", "gte", "eq", "neq", "neg", "pos", "not" <BR><B><EM>Param:</B></EM> function <BR><B><EM>Return:</B></EM> 1 on success, otherwise 0 <BR><HR>
|
||||
<a name="gm::typeRegisterVariable"><H3>typeRegisterVariable</H3>
|
||||
</a><B><EM>Brief:</B></EM> typeRegisterVariable will register a variable with a type such that (type).varname will return the variable <BR><B><EM>Param:</B></EM> int typeid <BR><B><EM>Param:</B></EM> string var name <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> 1 on success, otherwise 0 <BR><HR>
|
||||
<a name="gm::sysCollectGarbage"><H3>sysCollectGarbage</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysCollectGarbage will run the garbage collector iff the current mem used is over the desired mem used <BR><B><EM>Param:</B></EM> forceFullCollect (false) Optionally perform full garbage collection immediately if garbage collection is not disabled. <BR><B><EM>Return:</B></EM> 1 if the gc was run, 0 otherwise <BR><HR>
|
||||
<a name="gm::sysGetMemoryUsage"><H3>sysGetMemoryUsage</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetMemoryUsage will return the current memory used in bytes <BR><B><EM>Return:</B></EM> int memory usage <BR><HR>
|
||||
<a name="gm::sysSetDesiredMemoryUsageHard"><H3>sysSetDesiredMemoryUsageHard</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageHard will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run. <BR><B><EM>Param:</B></EM> int desired mem usage in bytes <BR><HR>
|
||||
<a name="gm::sysSetDesiredMemoryUsageSoft"><H3>sysSetDesiredMemoryUsageSoft</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageSoft will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run. <BR><B><EM>Param:</B></EM> int desired mem usage in bytes <BR><HR>
|
||||
<a name="gm::sysGetDesiredMemoryUsageHard"><H3>sysGetDesiredMemoryUsageHard</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetDesiredMemoryUsageHard will get the desired memory useage in bytes. Note that this value is used to start garbage collection, it is not a strict limit. <BR><B><EM>Return:</B></EM> int Desired memory usage in bytes. <BR><HR>
|
||||
<a name="gm::sysGetDesiredMemoryUsageSoft"><H3>sysGetDesiredMemoryUsageSoft</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetDesiredMemoryUsageSoft will get the desired memory useage in bytes. Note that this value is used to start garbage collection, it is not a strict limit. <BR><B><EM>Return:</B></EM> int Desired memory usage in bytes. <BR><HR>
|
||||
<a name="gm::sysSetDesiredMemoryUsageAuto"><H3>sysSetDesiredMemoryUsageAuto</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageAuto will enable auto adjustment of the memory limit(s) for subsequent garbage collections. <BR><B><EM>Param:</B></EM> int enable or disable flag <BR><HR>
|
||||
<a name="gm::sysGetStatsGCNumFullCollects"><H3>sysGetStatsGCNumFullCollects</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumFullCollects Return the number of times full garbage collection has occured. <BR><B><EM>Return:</B></EM> int Number of times full collect has occured. <BR><HR>
|
||||
<a name="gm::sysGetStatsGCNumIncCollects"><H3>sysGetStatsGCNumIncCollects</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumIncCollects Return the number of times incremental garbage collection has occured. This number may increase in twos as the GC has multiple phases which appear as restarts. <BR><B><EM>Return:</B></EM> int Number of times incremental collect has occured. <BR><HR>
|
||||
<a name="gm::sysGetStatsGCNumWarnings"><H3>sysGetStatsGCNumWarnings</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumWarnings Return the number of warnings because the GC or VM thought the GC was poorly configured. If this number is large and growing rapidly, the GC soft and hard limits need to be configured better. Do not be concerned if this number grows slowly. <BR><B><EM>Return:</B></EM> int Number of warnings garbage collect has generated. <BR><HR>
|
||||
<a name="gm::sysIsGCRunning"><H3>sysIsGCRunning</H3>
|
||||
</a><B><EM>Brief:</B></EM> Returns true if GC is running a cycle. <BR><HR>
|
||||
<a name="gm::sysTime"><H3>sysTime</H3>
|
||||
</a><B><EM>Brief:</B></EM> sysTime will return the machine time in milli seconds <BR><B><EM>Return:</B></EM> int <BR><HR>
|
||||
<a name="gm::doString"><H3>doString</H3>
|
||||
</a><B><EM>Brief:</B></EM> doString will execute the passed gm script <BR><B><EM>Param:</B></EM> string script <BR><B><EM>Param:</B></EM> int optional (1) set as true and the string will execute before returning to this thread <BR><B><EM>Param:</B></EM> ref optional (null) set 'this' <BR><B><EM>Return:</B></EM> int thread id of thread created for string execution <BR><HR>
|
||||
<a name="gm::globals"><H3>globals</H3>
|
||||
</a><B><EM>Brief:</B></EM> globals will return the globals table <BR><B><EM>Return:</B></EM> table containing all global variables <BR><HR>
|
||||
<a name="gm::threadTime"><H3>threadTime</H3>
|
||||
</a><B><EM>Brief:</B></EM> threadTime will return the thread execution time in milliseconds <BR><B><EM>Return:</B></EM> int <BR><HR>
|
||||
<a name="gm::threadId"><H3>threadId</H3>
|
||||
</a><B><EM>Brief:</B></EM> threadId will return the thread id of the current executing script <BR><B><EM>Return:</B></EM> int <BR><HR>
|
||||
<a name="gm::threadAllIds"><H3>threadAllIds</H3>
|
||||
</a><B><EM>Brief:</B></EM> threadIds returns a table of thread Ids <BR><B><EM>Return:</B></EM> table of thread Ids <BR><HR>
|
||||
<a name="gm::threadKill"><H3>threadKill</H3>
|
||||
</a><B><EM>Brief:</B></EM> threadKill will kill the thread with the given id <BR><B><EM>Param:</B></EM> int threadId optional (0) will kill this thread <BR><HR>
|
||||
<a name="gm::threadKillAll"><H3>threadKillAll</H3>
|
||||
</a><B><EM>Brief:</B></EM> threadKillAll will kill all the threads except the current one <BR><B><EM>Param:</B></EM> bool optional (false) will kill this thread if true <BR><HR>
|
||||
<a name="gm::thread"><H3>thread</H3>
|
||||
</a><B><EM>Brief:</B></EM> thread will start a new thread <BR><B><EM>Param:</B></EM> function entry point of the thread <BR><B><EM>Param:</B></EM> ... parameters to pass to the entry function <BR><B><EM>Return:</B></EM> int threadid <BR><HR>
|
||||
<a name="gm::yield"><H3>yield</H3>
|
||||
</a><B><EM>Brief:</B></EM> yield will hand execution control to the next thread <BR><HR>
|
||||
<a name="gm::exit"><H3>exit</H3>
|
||||
</a><B><EM>Brief:</B></EM> exit will kill this thread <BR><HR>
|
||||
<a name="gm::assert"><H3>assert</H3>
|
||||
</a><B><EM>Brief:</B></EM> assert <BR><B><EM>Param:</B></EM> int expression if true, will do nothing, if false, will cause an exception <BR><HR>
|
||||
<a name="gm::sleep"><H3>sleep</H3>
|
||||
</a><B><EM>Brief:</B></EM> sleep will sleep this thread for the given number of seconds <BR><B><EM>Param:</B></EM> int\float seconds <BR><HR>
|
||||
<a name="gm::signal"><H3>signal</H3>
|
||||
</a><B><EM>Brief:</B></EM> signal will signal the given variable, this will unblock dest threads that are blocked on the same variable. <BR><B><EM>Param:</B></EM> var <BR><B><EM>Param:</B></EM> int destThreadId optional (0) 0 will signal all threads <BR><HR>
|
||||
<a name="gm::block"><H3>block</H3>
|
||||
</a><B><EM>Brief:</B></EM> block will block on all passed vars, execution will halt until another thread signals one of the block variables. Will yield on null and return null. <BR><B><EM>Param:</B></EM> ... vars <BR><B><EM>Return:</B></EM> the unblocking var <BR><HR>
|
||||
<a name="gm::stateSet"><H3>stateSet</H3>
|
||||
</a><B><EM>Brief:</B></EM> stateSet will collapse the stack to nothing, and push the passed functions. <BR><B><EM>Param:</B></EM> function new state function to execute <BR><B><EM>Param:</B></EM> ... params for new state function <BR><HR>
|
||||
<a name="gm::stateSetOnThread"><H3>stateSetOnThread</H3>
|
||||
</a><B><EM>Brief:</B></EM> stateSetOnThread will collapse the stack of the given thread id to nothing, and push the passed functions. <BR><B><EM>Param:</B></EM> int thread id <BR><B><EM>Param:</B></EM> function new state function to execute <BR><B><EM>Param:</B></EM> ... params for new state function <BR><HR>
|
||||
<a name="gm::stateGet"><H3>stateGet</H3>
|
||||
</a><B><EM>Brief:</B></EM> stateGet will return the function on the bottom of this threads execution stack iff it was pushed using stateSet <BR><B><EM>Param:</B></EM> a_threadId Optional Id of thread to get state on. \reutrn function \ null <BR><HR>
|
||||
<a name="gm::stateGetLast"><H3>stateGetLast</H3>
|
||||
</a><B><EM>Brief:</B></EM> stateGetLast will return the last state function of this thread <BR><B><EM>Param:</B></EM> a_threadId Optional Id of thread to get last state on. \reutrn function \ null <BR><HR>
|
||||
<a name="gm::stateSetExitFunction"><H3>stateSetExitFunction</H3>
|
||||
</a><B><EM>Brief:</B></EM> stateSetExitFunction will set an exit function for this state, that will be called with no parameters if this thread switches state <BR><B><EM>Param:</B></EM> function <BR><HR>
|
||||
<a name="gm::tableCount"><H3>tableCount</H3>
|
||||
</a><B><EM>Brief:</B></EM> tableCount will return the number of elements in a table object <BR><B><EM>Param:</B></EM> table <BR><B><EM>Return:</B></EM> int <BR><HR>
|
||||
<a name="gm::tableDuplicate"><H3>tableDuplicate</H3>
|
||||
</a><B><EM>Brief:</B></EM> tableDuplicate will duplicate the passed table object <BR><B><EM>Param:</B></EM> table <BR><B><EM>Return:</B></EM> table <BR><HR>
|
||||
<a name="gm::print"><H3>print</H3>
|
||||
</a><B><EM>Brief:</B></EM> print will print the given vars to the print handler. passed strings are concatinated together with a seperating space. <BR><B><EM>Param:</B></EM> ... strings <BR><HR>
|
||||
<a name="gm::format"><H3>format</H3>
|
||||
</a><B><EM>Brief:</B></EM> format (like sprintf, but returns a string) %d, %s, %f, %c, %b, %x, %e <BR><B><EM>Param:</B></EM> string <BR></BODY></HTML>
|
||||
2
vscript/languages/gm/scripts/gmDoc/gmdoc.txt
Normal file
2
vscript/languages/gm/scripts/gmDoc/gmdoc.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
..\..\src\binds
|
||||
..\..\src\gm
|
||||
1
vscript/languages/gm/scripts/gmDoc/gmdoc.xml
Normal file
1
vscript/languages/gm/scripts/gmDoc/gmdoc.xml
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><functions><function name="gm::array"/><function name="array::Size"/><function name="array::Resize"/><function name="array::Shift"/><function name="array::Move"/><function name="math::abs"/><function name="math::sqrt"/><function name="math::sqrt"/><function name="math::floor"/><function name="math::ceil"/><function name="math::round"/><function name="math::degtorad"/><function name="math::radtodeg"/><function name="math::cos"/><function name="math::sin"/><function name="math::tan"/><function name="math::acos"/><function name="math::asin"/><function name="math::atan"/><function name="math::atan"/><function name="math::log"/><function name="math::min"/><function name="math::max"/><function name="math::clamp"/><function name="math::randint"/><function name="math::randfloat"/><function name="math::randseed"/><function name="string::IsEmpty"/><function name="string::Length"/><function name="string::Left"/><function name="string::Right"/><function name="string::RightAt"/><function name="string::Mid"/><function name="string::Compare"/><function name="string::CompareNoCase"/><function name="string::Int"/><function name="string::Float"/><function name="string::String"/><function name="string::Upper"/><function name="string::Lower"/><function name="string::SpanIncluding"/><function name="string::SpanExcluding"/><function name="string::AppendPath"/><function name="string::ReplaceCharsInSet"/><function name="string::Find"/><function name="string::Reverse"/><function name="string::ReverseFind"/><function name="string::GetAt"/><function name="string::SetAt"/><function name="string::TrimLeft"/><function name="string::TrimRight"/><function name="string::GetFilenameNoExt"/><function name="string::GetFilename"/><function name="string::GetExtension"/><function name="string::SetExtension"/><function name="string::GetPath"/><function name="system::Exec"/><function name="system::DoFile"/><function name="system::File"/><function name="system::FileExists"/><function name="system::FileFindFirst"/><function name="system::FileFindNext"/><function name="system::FileInfo"/><function name="system::CreateFolder"/><function name="system::DeleteFolder"/><function name="system::Time"/><function name="system::FormatTime"/><function name="fileFind::GetAttribute"/><function name="file::Open"/><function name="file::OpenText"/><function name="file::Close"/><function name="file::IsOpen"/><function name="file::Seek"/><function name="file::Tell"/><function name="file::ReadLine"/><function name="file::ReadChar"/><function name="file::WriteString"/><function name="file::WriteChar"/><function name="Vector3::Vector3"/><function name="Vector3::DominantAxis"/><function name="Vector3::Dot"/><function name="Vector3::Length"/><function name="Vector3::Cross"/><function name="Vector3::Normalize"/><function name="Vector3::LengthSquared"/><function name="Vector3::ProjectFrom"/><function name="Vector3::Clone"/><function name="Vector3::Set"/><function name="Vector3::LerpToPoint"/><function name="Vector3::SlerpToVector"/><function name="Vector3::RotateAxisAngle"/><function name="Vector3::RotateX"/><function name="Vector3::RotateX"/><function name="Vector3::RotateZ"/><function name="Vector3::SetAdd"/><function name="Vector3::Add"/><function name="gm::debug"/><function name="gm::gmVersion"/><function name="gm::typeId"/><function name="gm::typeName"/><function name="gm::typeRegisterOperator"/><function name="gm::typeRegisterVariable"/><function name="gm::sysCollectGarbage"/><function name="gm::sysGetMemoryUsage"/><function name="gm::sysSetDesiredMemoryUsageHard"/><function name="gm::sysSetDesiredMemoryUsageSoft"/><function name="gm::sysGetDesiredMemoryUsageHard"/><function name="gm::sysGetDesiredMemoryUsageSoft"/><function name="gm::sysSetDesiredMemoryUsageAuto"/><function name="gm::sysGetStatsGCNumFullCollects"/><function name="gm::sysGetStatsGCNumIncCollects"/><function name="gm::sysGetStatsGCNumWarnings"/><function name="gm::sysIsGCRunning"/><function name="gm::sysTime"/><function name="gm::doString"/><function name="gm::globals"/><function name="gm::threadTime"/><function name="gm::threadId"/><function name="gm::threadAllIds"/><function name="gm::threadKill"/><function name="gm::threadKillAll"/><function name="gm::thread"/><function name="gm::yield"/><function name="gm::exit"/><function name="gm::assert"/><function name="gm::sleep"/><function name="gm::signal"/><function name="gm::block"/><function name="gm::stateSet"/><function name="gm::stateSetOnThread"/><function name="gm::stateGet"/><function name="gm::stateGetLast"/><function name="gm::stateSetExitFunction"/><function name="gm::tableCount"/><function name="gm::tableDuplicate"/><function name="gm::print"/><function name="gm::format"/></functions>
|
||||
608
vscript/languages/gm/scripts/minesweeper.gm
Normal file
608
vscript/languages/gm/scripts/minesweeper.gm
Normal file
@@ -0,0 +1,608 @@
|
||||
//
|
||||
// MineSweeper By Andriy Doroshchuk
|
||||
//
|
||||
|
||||
global cursorOverClosed = CA.F_RED | CA.F_INTENSITY | CA.B_BLACK | CA.TRAILING_BYTE;
|
||||
global cursorOverOpened = CA.F_RED | CA.F_INTENSITY | CA.B_BLUE | CA.TRAILING_BYTE;
|
||||
global frameColor = CA.F_RED | CA.F_GREEN | CA.F_BLUE | CA.B_BLACK | CA.TRAILING_BYTE;
|
||||
global closedColor = CA.F_BLUE | CA.B_BLACK | CA.TRAILING_BYTE;
|
||||
global openedColor = CA.F_BLUE | CA.B_BLUE | CA.TRAILING_BYTE;
|
||||
global numberColor = CA.F_BLUE | CA.F_GREEN| CA.F_INTENSITY | CA.B_BLUE | CA.TRAILING_BYTE;
|
||||
global markColor = CA.F_BLUE | CA.F_RED | CA.F_INTENSITY | CA.B_BLUE | CA.TRAILING_BYTE;
|
||||
global mineColor = CA.F_GREEN| CA.F_RED | CA.B_BLUE | CA.TRAILING_BYTE;
|
||||
global helpColor = CA.F_GREEN| CA.F_RED | CA.F_BLUE |CA.B_BLUE | CA.TRAILING_BYTE;
|
||||
global winColor = CA.F_GREEN| CA.F_INTENSITY | CA.B_BLACK | CA.TRAILING_BYTE;
|
||||
global looseColor = CA.F_RED | CA.F_INTENSITY | CA.B_BLACK | CA.TRAILING_BYTE;
|
||||
|
||||
// wait till key will be released
|
||||
global WaitForKey = function(key)
|
||||
{
|
||||
for (;ISPRESSED(key);){}
|
||||
};
|
||||
|
||||
// print debug message
|
||||
global DebugText = function(text)
|
||||
{
|
||||
CATTRIB(frameColor);
|
||||
XYTEXT(0, 0, text);
|
||||
};
|
||||
|
||||
|
||||
game = table(
|
||||
x = 15,
|
||||
y = 0,
|
||||
lx = 30,
|
||||
ly = 15,
|
||||
mineCurr = 0,
|
||||
mineMax = 10,
|
||||
currLevel = 2,
|
||||
cX = 0,
|
||||
cY = 0,
|
||||
|
||||
field = table(),
|
||||
|
||||
// index in the field
|
||||
Index = function(x, y)
|
||||
{
|
||||
return (y*.lx+x);
|
||||
},
|
||||
|
||||
// draw field frame
|
||||
DrawFrame = function()
|
||||
{
|
||||
i;
|
||||
|
||||
// set color
|
||||
CATTRIB(frameColor);
|
||||
|
||||
// borders
|
||||
for (i=.x+1; i<.x+.lx+1; i=i+1)
|
||||
{
|
||||
XYTEXT(i, .y, "-");
|
||||
XYTEXT(i, .y+.ly+1, "-");
|
||||
}
|
||||
for (i=.y+1; i<.y+.ly+1; i=i+1)
|
||||
{
|
||||
XYTEXT(.x, i, "|");
|
||||
XYTEXT(.x+.lx+1, i, "|");
|
||||
}
|
||||
|
||||
// corners
|
||||
XYTEXT(.x, .y, "\1");
|
||||
XYTEXT(.x+.lx+1, .y, "\1");
|
||||
XYTEXT(.x, .y+.ly+1, "\1");
|
||||
XYTEXT(.x+.lx+1, .y+.ly+1, "\1");
|
||||
|
||||
// count
|
||||
XYTEXT(.x+3, .y, "[" + .mineCurr.String() + ":" + .mineMax.String() + "]");
|
||||
},
|
||||
|
||||
// field cell drawing
|
||||
DrawCell = function(x, y, cursor)
|
||||
{
|
||||
index = .Index(x, y);
|
||||
// debug cell = .field[index].String();
|
||||
cell = " ";
|
||||
|
||||
// closed cell
|
||||
if (.field[index] < 10)
|
||||
{
|
||||
if (cursor)
|
||||
{
|
||||
CATTRIB(cursorOverClosed);
|
||||
cell = "W";
|
||||
}
|
||||
else
|
||||
{
|
||||
CATTRIB(closedColor);
|
||||
}
|
||||
}
|
||||
// opened cell
|
||||
else if (.field[index] < 20)
|
||||
{
|
||||
if (.field[index] == 10)
|
||||
{
|
||||
if (cursor)
|
||||
{
|
||||
CATTRIB(cursorOverOpened);
|
||||
cell = "W";
|
||||
}
|
||||
else
|
||||
{
|
||||
CATTRIB(openedColor);
|
||||
}
|
||||
}
|
||||
else if (.field[index] < 19)
|
||||
{
|
||||
if (cursor)
|
||||
{
|
||||
CATTRIB(cursorOverOpened);
|
||||
}
|
||||
else
|
||||
{
|
||||
CATTRIB(numberColor);
|
||||
}
|
||||
val = .field[index] % 10;
|
||||
cell = val.String();
|
||||
}
|
||||
else
|
||||
{
|
||||
CATTRIB(mineColor);
|
||||
cell = "@";
|
||||
}
|
||||
}
|
||||
// intended mine
|
||||
else
|
||||
{
|
||||
if (cursor)
|
||||
{
|
||||
CATTRIB(cursorOverOpened);
|
||||
}
|
||||
else
|
||||
{
|
||||
CATTRIB(markColor);
|
||||
}
|
||||
cell = "?";
|
||||
}
|
||||
|
||||
XYTEXT(.x+x+1, .y+y+1, cell);
|
||||
},
|
||||
|
||||
// field cell drawing
|
||||
DrawCurrCell = function(cursor)
|
||||
{
|
||||
.DrawCell(.cX, .cY, cursor);
|
||||
},
|
||||
|
||||
// field drawing
|
||||
DrawField = function()
|
||||
{
|
||||
i; j;
|
||||
|
||||
.DrawFrame();
|
||||
for (i=0; i<.lx; i=i+1)
|
||||
{
|
||||
for (j=0; j<.ly; j=j+1)
|
||||
{
|
||||
.DrawCell(i, j, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// help string
|
||||
HelpString = function(msg, line, mode)
|
||||
{
|
||||
if (mode == 1)
|
||||
{
|
||||
CATTRIB(CA.F_BLUE | CA.F_GREEN | CA.F_INTENSITY | CA.B_BLACK | CA.TRAILING_BYTE);
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
CATTRIB(CA.F_RED | CA.B_BLACK | CA.TRAILING_BYTE);
|
||||
}
|
||||
else
|
||||
{
|
||||
CATTRIB(CA.F_GREEN | CA.B_BLACK | CA.TRAILING_BYTE);
|
||||
}
|
||||
|
||||
|
||||
XYTEXT(0, 25-line, " ");
|
||||
XYTEXT((80-msg.Length())/2, 25-line, msg);
|
||||
},
|
||||
|
||||
// Initialise field
|
||||
InitField = function(level)
|
||||
{
|
||||
i; j;
|
||||
t1;t2;
|
||||
count; position; index;
|
||||
|
||||
// clear screen
|
||||
CATTRIB(frameColor);
|
||||
CLS();
|
||||
|
||||
// set current level
|
||||
.currLevel = level;
|
||||
|
||||
// field extents
|
||||
if (level == 1)
|
||||
{
|
||||
.lx = 10;
|
||||
.ly = 10;
|
||||
.mineMax = 8;
|
||||
}
|
||||
else if (level == 2)
|
||||
{
|
||||
.lx = 18;
|
||||
.ly = 15;
|
||||
.mineMax = 35;
|
||||
}
|
||||
else
|
||||
{
|
||||
.lx = 30;
|
||||
.ly = 15;
|
||||
.mineMax = 90;
|
||||
}
|
||||
.x = (80-.lx)/2;
|
||||
.mineCurr = 0;
|
||||
|
||||
// init field contents
|
||||
max = .lx*.ly;
|
||||
for (i=0; i<max; i=i+1)
|
||||
{
|
||||
.field[i] = 0;
|
||||
}
|
||||
|
||||
// put mines on the field
|
||||
for (count=1; count<=.mineMax; count=count+1)
|
||||
{
|
||||
position = randint(0, .lx*.ly+1-count);
|
||||
for (i=0; i<.lx; i=i+1)
|
||||
{
|
||||
for (j=0; j<.ly; j=j+1)
|
||||
{
|
||||
index = .Index(i, j);
|
||||
if (.field[index] == 0)
|
||||
{
|
||||
if (position == 0)
|
||||
{
|
||||
.field[index] = 9;
|
||||
i = .lx;
|
||||
j = .ly;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = position - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculate mines environment
|
||||
for (i=0; i<.lx; i=i+1)
|
||||
{
|
||||
for (j=0; j<.ly; j=j+1)
|
||||
{
|
||||
if (.field[.Index(i, j)] == 9)
|
||||
{
|
||||
for (t1=i-1; t1<i+2; t1=t1+1)
|
||||
{
|
||||
for (t2=j-1; t2<j+2; t2=t2+1)
|
||||
{
|
||||
if ((t1!=i || t2!=j)
|
||||
&& (t1>=0) && (t1<.lx)
|
||||
&& (t2>=0) && (t2<.ly))
|
||||
{
|
||||
index = .Index(t1, t2);
|
||||
if (.field[index] < 9)
|
||||
{
|
||||
.field[index] = .field[index] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// draw field frame
|
||||
.DrawFrame();
|
||||
|
||||
// set current cell
|
||||
.cX=.lx/2;
|
||||
.cY=.ly/2;
|
||||
.DrawField();
|
||||
.DrawCell(.cX, .cY, 1);
|
||||
|
||||
.HelpString("", 4, 0);
|
||||
.HelpString("F2 - beginner. F3 - intermediate. F4 - expert", 2, 0);
|
||||
.HelpString("Enter - open. Space - mark as a mine. Backspace - open around.", 1, 0);
|
||||
},
|
||||
|
||||
// restart current game
|
||||
RestartField = function()
|
||||
{
|
||||
.InitField(.currLevel);
|
||||
},
|
||||
|
||||
// restart game after finishing
|
||||
Restart = function(win)
|
||||
{
|
||||
.HelpString("", 2, 0);
|
||||
.HelpString("", 1, 0);
|
||||
|
||||
if (win)
|
||||
{
|
||||
.HelpString("Congratulations, you won! Press F1 to continue.", 4, 1);
|
||||
for (; !ISPRESSED(112);){}
|
||||
}
|
||||
else
|
||||
{
|
||||
.HelpString("Sorry, you are dead! Press F1 to continue.", 4, 2);
|
||||
.DrawField();
|
||||
}
|
||||
for (; !ISPRESSED(112);){}
|
||||
|
||||
.RestartField();
|
||||
},
|
||||
|
||||
// field opening when open an empty cell
|
||||
CheckCell = function(x, y)
|
||||
{
|
||||
i; j;
|
||||
index = .Index(x, y);
|
||||
|
||||
if (.field[index] == 0)
|
||||
{
|
||||
.field[index] = 10;
|
||||
|
||||
// check surrounding
|
||||
for (i=x-1; i<x+2; i=i+1)
|
||||
{
|
||||
for (j=y-1; j<y+2; j=j+1)
|
||||
{
|
||||
if ((i!=x || j!=y)
|
||||
&& (i>=0) && (i<.lx)
|
||||
&& (j>=0) && (j<.ly))
|
||||
{
|
||||
.CheckCell(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (.field[index] < 9)
|
||||
{
|
||||
.field[index] = .field[index] + 10;
|
||||
}
|
||||
}
|
||||
|
||||
.DrawCell(x, y, 0);
|
||||
},
|
||||
|
||||
// check win condition
|
||||
CheckWin = function()
|
||||
{
|
||||
i; j; index;
|
||||
count = 0;
|
||||
for (i=0; i<.lx; i=i+1)
|
||||
{
|
||||
for (j=0; j<.ly; j=j+1)
|
||||
{
|
||||
index = .Index(i, j);
|
||||
if (.field[index] < 10)
|
||||
{
|
||||
count = 0;
|
||||
i = .lx;
|
||||
j = .ly;
|
||||
}
|
||||
else if (.field[index] >= 20)
|
||||
{
|
||||
count = count + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count == .mineMax)
|
||||
{
|
||||
.DrawCell(.cX, .cY, 0);
|
||||
.Restart(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
.DrawCell(.cX, .cY, 1);
|
||||
}
|
||||
},
|
||||
|
||||
// open field cell
|
||||
OpenCell = function(x, y, check)
|
||||
{
|
||||
i; j;
|
||||
index = .Index(x, y);
|
||||
if (!check)
|
||||
{
|
||||
if (.field[index] >= 20)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check the field
|
||||
if (check && (.field[index] % 10) == 9)
|
||||
{
|
||||
for (i=0; i<.lx; i=i+1)
|
||||
{
|
||||
for (j=0; j<.ly; j=j+1)
|
||||
{
|
||||
index = .Index(i, j);
|
||||
.field[index] = (.field[index] % 10) + 10;
|
||||
}
|
||||
}
|
||||
|
||||
.Restart(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
.CheckCell(x, y);
|
||||
.CheckWin();
|
||||
}
|
||||
},
|
||||
|
||||
// open cells around open mine
|
||||
HelperCell = function()
|
||||
{
|
||||
i; j;
|
||||
index = .Index(.cX, .cY);
|
||||
|
||||
// perform the action only if the cell is already open
|
||||
if (.field[index]>=10 && .field[index]<20)
|
||||
{
|
||||
// calculate mines around this cell
|
||||
mines = 0;
|
||||
for (i=.cX-1; i<.cX+2; i=i+1)
|
||||
{
|
||||
for (j=.cY-1; j<.cY+2; j=j+1)
|
||||
{
|
||||
if ((i!=.cX || j!=.cY)
|
||||
&& (i>=0) && (i<.lx)
|
||||
&& (j>=0) && (j<.ly))
|
||||
{
|
||||
index = .Index(i, j);
|
||||
if (.field[index] >= 20)
|
||||
{
|
||||
mines = mines + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check that we can open
|
||||
index = .Index(.cX, .cY);
|
||||
if (mines == (.field[index] % 10))
|
||||
{
|
||||
for (i=.cX-1; i<.cX+2; i=i+1)
|
||||
{
|
||||
for (j=.cY-1; j<.cY+2; j=j+1)
|
||||
{
|
||||
if ((i!=.cX || j!=.cY)
|
||||
&& (i>=0) && (i<.lx)
|
||||
&& (j>=0) && (j<.ly))
|
||||
{
|
||||
.OpenCell(i, j, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// move cursor
|
||||
Move = function(dir)
|
||||
{
|
||||
.DrawCurrCell(0);
|
||||
if (dir == 1)
|
||||
{
|
||||
if (.cY > 0)
|
||||
{
|
||||
.cY = .cY - 1;
|
||||
}
|
||||
}
|
||||
else if (dir == 3)
|
||||
{
|
||||
if (.cY<.ly-1)
|
||||
{
|
||||
.cY = .cY + 1;
|
||||
}
|
||||
}
|
||||
else if (dir == 0)
|
||||
{
|
||||
if (.cX > 0)
|
||||
{
|
||||
.cX = .cX - 1;
|
||||
}
|
||||
}
|
||||
else if (dir == 2)
|
||||
{
|
||||
if (.cX<.lx-1)
|
||||
{
|
||||
.cX = .cX + 1;
|
||||
}
|
||||
}
|
||||
WaitForKey(37+dir);
|
||||
.DrawCurrCell(1);
|
||||
},
|
||||
|
||||
// open current cell
|
||||
Open = function()
|
||||
{
|
||||
WaitForKey(13);
|
||||
.OpenCell(.cX, .cY, 1);
|
||||
},
|
||||
|
||||
// mark current cell
|
||||
Mark = function()
|
||||
{
|
||||
WaitForKey(32);
|
||||
index = .Index(.cX, .cY);
|
||||
if (.field[index] < 10)
|
||||
{
|
||||
.field[index] = (.field[index] % 10) + 20;
|
||||
.mineCurr = .mineCurr + 1;
|
||||
.DrawFrame();
|
||||
.CheckWin();
|
||||
}
|
||||
else if (.field[index] >= 20)
|
||||
{
|
||||
.field[index] = .field[index] % 10;
|
||||
.mineCurr = .mineCurr -1;
|
||||
.DrawFrame();
|
||||
.DrawCurrCell(1);
|
||||
}
|
||||
},
|
||||
|
||||
// init the game
|
||||
Run = function()
|
||||
{
|
||||
CURSOR(0, 0);
|
||||
.RestartField();
|
||||
|
||||
for (;!ISPRESSED(27);)
|
||||
{
|
||||
if (ISPRESSED(37))
|
||||
{
|
||||
.Move(0);
|
||||
}
|
||||
else if (ISPRESSED(38))
|
||||
{
|
||||
.Move(1);
|
||||
}
|
||||
else if (ISPRESSED(39))
|
||||
{
|
||||
.Move(2);
|
||||
}
|
||||
else if (ISPRESSED(40))
|
||||
{
|
||||
.Move(3);
|
||||
}
|
||||
else if (ISPRESSED(32))
|
||||
{
|
||||
.Mark();
|
||||
}
|
||||
else if (ISPRESSED(13))
|
||||
{
|
||||
.Open();
|
||||
}
|
||||
else if (ISPRESSED(8))
|
||||
{
|
||||
.HelperCell();
|
||||
WaitForKey(8);
|
||||
}
|
||||
else if (ISPRESSED(113))
|
||||
{
|
||||
WaitForKey(113);
|
||||
.InitField(1);
|
||||
}
|
||||
else if (ISPRESSED(114))
|
||||
{
|
||||
WaitForKey(114);
|
||||
.InitField(2);
|
||||
}
|
||||
else if (ISPRESSED(115))
|
||||
{
|
||||
WaitForKey(115);
|
||||
.InitField(3);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugText(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
game.Run();
|
||||
|
||||
|
||||
696
vscript/languages/gm/scripts/snake.gm
Normal file
696
vscript/languages/gm/scripts/snake.gm
Normal file
@@ -0,0 +1,696 @@
|
||||
|
||||
/*
|
||||
|
||||
SNAKE
|
||||
|
||||
by matty riek.
|
||||
|
||||
*/
|
||||
|
||||
global showMemoryUsage = false;
|
||||
|
||||
CURSOR(0,0);
|
||||
|
||||
global g_sx = 80;
|
||||
global g_sy = 24;
|
||||
global g_border =
|
||||
|
||||
"\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205"
|
||||
"\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205"
|
||||
"\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205";
|
||||
|
||||
global LocFromXY = function(a_x, a_y) { return a_y * g_sx + a_x; };
|
||||
global XFromLoc = function(a_loc) { return a_loc % g_sx; };
|
||||
global YFromLoc = function(a_loc) { return a_loc / g_sx; };
|
||||
global background = CA.B_BLUE;
|
||||
|
||||
global fade0 = table( CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
|
||||
CA.F_GREEN | CA.F_INTENSITY | CA.F_INTENSITY | background,
|
||||
CA.F_BLUE | background,
|
||||
CA.F_GREEN | CA.F_INTENSITY | background,
|
||||
CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background,
|
||||
CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
|
||||
CA.F_BLUE | CA.F_INTENSITY | background,
|
||||
CA.F_BLUE | CA.F_INTENSITY | background,
|
||||
CA.F_RED|CA.F_BLUE|CA.F_GREEN | background,
|
||||
CA.F_BLUE | background,
|
||||
CA.F_BLUE | CA.F_INTENSITY | background,
|
||||
CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background );
|
||||
|
||||
global fade1 = table( CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
|
||||
CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
|
||||
CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
|
||||
CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background,
|
||||
CA.F_RED | CA.F_INTENSITY | background,
|
||||
CA.F_RED | background,
|
||||
CA.F_RED | CA.F_INTENSITY | background,
|
||||
CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background );
|
||||
|
||||
global fadeRed = table( CA.F_RED|CA.F_INTENSITY | background , CA.F_RED | background);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CreateGemType
|
||||
//
|
||||
|
||||
global CreateGemType = function(a_char, a_time, a_score, a_chance, a_length, a_cycle, a_pickup, a_mover)
|
||||
{
|
||||
gem = table(m_char = a_char, m_time = a_time * 1000, m_score = a_score, m_chance = a_chance, m_length = a_length, m_cycle = a_cycle, m_mover = a_mover);
|
||||
|
||||
gem.Pickup = function()
|
||||
{
|
||||
};
|
||||
|
||||
if(a_pickup)
|
||||
{
|
||||
gem.Pickup = a_pickup;
|
||||
}
|
||||
|
||||
gem.i = 0;
|
||||
gem.Draw = function(x, y)
|
||||
{
|
||||
.i = .i + 1;
|
||||
if(.i >= tableCount(.m_cycle))
|
||||
{
|
||||
.i = 0;
|
||||
}
|
||||
CATTRIB(.m_cycle[.i]);
|
||||
XYTEXT(x, y, .m_char);
|
||||
CATTRIB(background | CA.F_GREEN | CA.F_INTENSITY);
|
||||
};
|
||||
return gem;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DrawScreen
|
||||
//
|
||||
global DrawScreen = function()
|
||||
{
|
||||
//
|
||||
CATTRIB(background | CA.F_GREEN | CA.F_INTENSITY);
|
||||
|
||||
// clear screen
|
||||
CLS();
|
||||
|
||||
// draw border
|
||||
XYTEXT(1, 0, g_border);
|
||||
XYTEXT(1, g_sy - 1, g_border);
|
||||
|
||||
ey = g_sy - 1;
|
||||
for(i = 1; i < ey; i = i + 1)
|
||||
{
|
||||
XYTEXT(0, i, "\186");
|
||||
XYTEXT(g_sx - 1, i, "\186");
|
||||
}
|
||||
XYTEXT(0, 0, "\201");
|
||||
XYTEXT(g_sx - 1, 0, "\187");
|
||||
XYTEXT(0, ey, "\200");
|
||||
XYTEXT(g_sx - 1, ey, "\188");
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// EmitGems
|
||||
//
|
||||
EmitGems = function(a_game)
|
||||
{
|
||||
// add a gem
|
||||
for(;;)
|
||||
{
|
||||
// choose a random sleep time
|
||||
sleep( randfloat(
|
||||
a_game.m_levels[a_game.m_level][1] / a_game.m_gameSpeed,
|
||||
a_game.m_levels[a_game.m_level][2] / a_game.m_gameSpeed)
|
||||
);
|
||||
|
||||
// pick a random location
|
||||
x = randint(1, g_sx - 2);
|
||||
y = randint(1, g_sy - 2);
|
||||
loc = LocFromXY(x, y);
|
||||
|
||||
// test if the location is on a existing gem or snake
|
||||
if(a_game.m_gems[loc])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
onsnake = false;
|
||||
foreach(snake in a_game.m_snakes)
|
||||
{
|
||||
if(snake.IsAt(loc))
|
||||
{
|
||||
onsnake = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(onsnake) { continue; }
|
||||
|
||||
// choose a gem
|
||||
found = false;
|
||||
while(!found)
|
||||
{
|
||||
foreach(gem in a_game.m_gemTypes)
|
||||
{
|
||||
if(randint(0, 100) < gem.m_chance)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the gem
|
||||
gemInstance = table(m_gem = gem, m_expire = sysTime() + (gem.m_time / a_game.m_gameSpeed));
|
||||
a_game.m_gems[loc] = gemInstance;
|
||||
if(gem.m_mover)
|
||||
{
|
||||
gemInstance.m_dirX = randint(-1, 2);
|
||||
gemInstance.m_dirY = randint(-1, 2);
|
||||
}
|
||||
|
||||
// draw the gem
|
||||
gem.Draw(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// ReclaimGems
|
||||
//
|
||||
ReclaimGems = function(a_game)
|
||||
{
|
||||
// add a gem
|
||||
for(;;)
|
||||
{
|
||||
time = sysTime();
|
||||
|
||||
foreach(key and gem in a_game.m_gems)
|
||||
{
|
||||
if(time > gem.m_expire)
|
||||
{
|
||||
// remove from screen
|
||||
XYTEXT(XFromLoc(key), YFromLoc(key), " ");
|
||||
a_game.m_gems[key] = null;
|
||||
b = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yield();
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DrawGems
|
||||
//
|
||||
DrawGems = function(a_game)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
foreach(key and gem in a_game.m_gems)
|
||||
{
|
||||
x = XFromLoc(key);
|
||||
y = YFromLoc(key);
|
||||
gem.m_gem.Draw(x, y);
|
||||
}
|
||||
sleep(1 / 30.);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Move mover gems
|
||||
//
|
||||
MoveMovers = function(a_game)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
movers = table();
|
||||
foreach(key and gem in a_game.m_gems)
|
||||
{
|
||||
if(gem.m_gem.m_mover)
|
||||
{
|
||||
x = XFromLoc(key);
|
||||
y = YFromLoc(key);
|
||||
|
||||
XYTEXT(x, y, " ");
|
||||
|
||||
x = x + gem.m_dirX;
|
||||
y = y + gem.m_dirY;
|
||||
|
||||
if(x <= 1 or x >= g_sx - 2) { gem.m_dirX = -gem.m_dirX; }
|
||||
if(y <= 1 or y >= g_sy - 2) { gem.m_dirY = -gem.m_dirY; }
|
||||
gem.m_newLoc = LocFromXY(x, y);
|
||||
movers[key] = gem;
|
||||
}
|
||||
}
|
||||
|
||||
foreach(key and gem in movers)
|
||||
{
|
||||
a_game.m_gems[key] = null;
|
||||
a_game.m_gems[gem.m_newLoc] = gem;
|
||||
}
|
||||
sleep(1 / 4.);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Snake
|
||||
//
|
||||
Snake = function(a_id, a_name, a_score, a_scoreX, a_scoreY, a_headChar, a_tailChar, a_startX, a_startY, a_startDir, a_keys)
|
||||
{
|
||||
snake = table(
|
||||
m_id = a_id,
|
||||
m_grow = 2,
|
||||
m_credits = 3,
|
||||
m_startX = a_startX,
|
||||
m_startY = a_startY,
|
||||
m_startDir = a_startDir,
|
||||
m_name = a_name,
|
||||
m_score = a_score,
|
||||
m_scoreX = a_scoreX,
|
||||
m_scoreY = a_scoreY,
|
||||
m_body = array(50),
|
||||
m_length = 0,
|
||||
m_dir = a_startDir, // 0 right, 1 up, 2 left, 3 down
|
||||
m_head = table(
|
||||
m_headChar = a_headChar,
|
||||
m_tailChar = a_tailChar,
|
||||
m_x = a_startX,
|
||||
m_y = a_startY
|
||||
),
|
||||
m_keys = a_keys
|
||||
);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Die
|
||||
//
|
||||
snake.Die = function() // return true if snake has no credits
|
||||
{
|
||||
// flash a death thinggy
|
||||
|
||||
i = 10;
|
||||
while(i)
|
||||
{
|
||||
if(i & 1) {
|
||||
XYTEXT(.m_scoreX, .m_scoreY, format(" *** FATALITY *** ", .m_name));
|
||||
}
|
||||
else {
|
||||
XYTEXT(.m_scoreX, .m_scoreY, format(" ", .m_name));
|
||||
}
|
||||
sleep(0.2f);
|
||||
i = i - 1;
|
||||
}
|
||||
|
||||
.UpdateScore();
|
||||
|
||||
// clear the snake
|
||||
for(i = 0; i < .m_length; i = i + 1)
|
||||
{
|
||||
loc = .m_body[i];
|
||||
x = XFromLoc(loc);
|
||||
y = YFromLoc(loc);
|
||||
XYTEXT(x, y, " ");
|
||||
}
|
||||
XYTEXT(.m_head.m_x, .m_head.m_y, " ");
|
||||
|
||||
// update the credits
|
||||
.m_credits = .m_credits - 1;
|
||||
if(.m_credits)
|
||||
{
|
||||
.m_head.m_x = .m_startX;
|
||||
.m_head.m_y = .m_startY;
|
||||
.m_dir = .m_startDir;
|
||||
.m_grow = 2;
|
||||
.m_length = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// UpdateScore
|
||||
//
|
||||
snake.UpdateScore = function()
|
||||
{
|
||||
XYTEXT(.m_scoreX, .m_scoreY, format(" %d UP %s SCORE %d ", .m_credits, .m_name, .m_score));
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Draw
|
||||
//
|
||||
snake.Draw = function(a_oldX, a_oldY)
|
||||
{
|
||||
// clear the tail.
|
||||
if(.m_length > 0 and .grow == null)
|
||||
{
|
||||
loc = .m_body[.m_length - 1];
|
||||
y = YFromLoc(loc);
|
||||
x = XFromLoc(loc);
|
||||
XYTEXT(x, y, " ");
|
||||
}
|
||||
|
||||
// grow the snake
|
||||
if(.grow)
|
||||
{
|
||||
.m_length = .m_length + 1;
|
||||
if(.m_length >= .m_body.Size())
|
||||
{
|
||||
.m_body.Resize(.m_body.Size() * 2);
|
||||
}
|
||||
.grow = false;
|
||||
}
|
||||
|
||||
// move the body along.
|
||||
for(i = .m_length - 1; i > 0; i = i - 1)
|
||||
{
|
||||
.m_body[i] = .m_body[i - 1];
|
||||
}
|
||||
|
||||
// put the new tail piece in
|
||||
if(.m_length)
|
||||
{
|
||||
.m_body[0] = LocFromXY(a_oldX, a_oldY);
|
||||
XYTEXT(a_oldX, a_oldY, .m_head.m_tailChar);
|
||||
}
|
||||
else
|
||||
{
|
||||
XYTEXT(a_oldX, a_oldY, " ");
|
||||
}
|
||||
|
||||
// draw the new head
|
||||
XYTEXT(.m_head.m_x, .m_head.m_y, .m_head.m_headChar);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IsAt
|
||||
//
|
||||
snake.IsAt = function(a_loc)
|
||||
{
|
||||
for(i = 0; i < .m_length; i = i + 1)
|
||||
{
|
||||
if(a_loc == .m_body[i])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(a_loc == LocFromXY(.m_head.m_x, .m_head.m_y))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Update
|
||||
//
|
||||
snake.Update = function(a_game)
|
||||
{
|
||||
member UpdateScore;
|
||||
|
||||
UpdateScore();
|
||||
for(;;)
|
||||
{
|
||||
wait = 1.0f / (a_game.m_snakeMoveRate * a_game.m_gameSpeed);
|
||||
sleep(wait);
|
||||
|
||||
x = .m_head.m_x;
|
||||
y = .m_head.m_y;
|
||||
|
||||
if(.m_dir == 0) { x = x + 1; }
|
||||
else if(.m_dir == 1) { y = y - 1; }
|
||||
else if(.m_dir == 2) { x = x - 1; }
|
||||
else if(.m_dir == 3) { y = y + 1; }
|
||||
|
||||
// did we collect a gem?
|
||||
loc = LocFromXY(x, y);
|
||||
gem = a_game.m_gems[loc];
|
||||
if(gem)
|
||||
{
|
||||
// update score
|
||||
.m_score = .m_score + gem.m_gem.m_score;
|
||||
.m_grow = gem.m_gem.m_length;
|
||||
pickup = gem.m_gem.Pickup;
|
||||
this:pickup();
|
||||
UpdateScore();
|
||||
|
||||
// remove the gem
|
||||
a_game.m_gems[loc] = null;
|
||||
}
|
||||
|
||||
// did we run into a wall??
|
||||
dead = false;
|
||||
if(x <= 0 or x >= g_sx - 1 or y <= 0 or y >= g_sy - 1)
|
||||
{
|
||||
dead = true;
|
||||
}
|
||||
|
||||
// did we run into ourselves?
|
||||
if(!dead and .IsAt(loc))
|
||||
{
|
||||
dead = true;
|
||||
}
|
||||
|
||||
// update position
|
||||
oldX = .m_head.m_x;
|
||||
oldY = .m_head.m_y;
|
||||
.m_head.m_x = x;
|
||||
.m_head.m_y = y;
|
||||
|
||||
// did we run into another snake
|
||||
if(!dead)
|
||||
{
|
||||
foreach(snake in a_game.m_snakes)
|
||||
{
|
||||
if(snake != this and snake.IsAt(loc))
|
||||
{
|
||||
dead = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// did we die?
|
||||
if(dead)
|
||||
{
|
||||
.m_head.m_x = oldX;
|
||||
.m_head.m_y = oldY;
|
||||
die = .Die();
|
||||
.UpdateScore();
|
||||
if(die)
|
||||
{
|
||||
a_game.m_snakes[.m_id] = null;
|
||||
threadKill(.kht);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
.Draw(oldX, oldY);
|
||||
|
||||
if(.m_grow)
|
||||
{
|
||||
.grow = true;
|
||||
.m_grow = .m_grow - 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// KeyHandler
|
||||
//
|
||||
snake.KeyHandler = function(a_game)
|
||||
{
|
||||
wait = 1.0f / a_game.m_keyUpdateRate;
|
||||
for(;;)
|
||||
{
|
||||
for(i = 0; i < 4; i = i + 1)
|
||||
{
|
||||
if(ISPRESSED(.m_keys[i]))
|
||||
{
|
||||
.m_dir = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sleep(wait);
|
||||
}
|
||||
};
|
||||
|
||||
return snake;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Game
|
||||
//
|
||||
g_game = table(
|
||||
|
||||
// goodies
|
||||
m_gems = table(),
|
||||
|
||||
m_levels = table(
|
||||
|
||||
// level is speed, lower spawn time, upper spawn time, next level score
|
||||
table(1.0f, 10, 20, 500),
|
||||
table(2.0f, 8, 18, 1500),
|
||||
table(2.5f, 5, 16, 2700),
|
||||
table(3.0f, 4, 13, 3800),
|
||||
table(3.5f, 3, 10, 5000)
|
||||
),
|
||||
|
||||
m_gemTypes = table(
|
||||
|
||||
CreateGemType("\4", 22, 50, 40, 3, fade0), // standard 1
|
||||
CreateGemType("\5", 17, 100, 30, 4, fade0), // standard 2
|
||||
CreateGemType("\6", 15, 200, 20, 5, fade1), // standard 3
|
||||
CreateGemType("\3", 30, 20, 15, 0, fadeRed, function() { .m_credits = .m_credits + 1; }, true), // 1up
|
||||
CreateGemType("\15", 45, 0, 15, 50, fade1) // bomb
|
||||
),
|
||||
|
||||
// snakes
|
||||
m_snakes = table(
|
||||
Snake(0, "lefty", 0, 8, 23, "\1", "\176", 1, 12, 0, table('D', 'W', 'A', 'S')),
|
||||
Snake(1, "righty", 0, 50, 23, "\2", "\177", 78, 12, 2, table('L', 'I', 'J', 'K'))
|
||||
),
|
||||
|
||||
// game
|
||||
m_level = 0,
|
||||
m_over = false,
|
||||
m_totalScore = 0,
|
||||
|
||||
// timing
|
||||
m_speedup = 2.0f, // overall speed multiplier
|
||||
m_gameSpeed = 1.0f, // current game speed
|
||||
m_keyUpdateRate = 30.0f, // times per second
|
||||
m_snakeMoveRate = 4.0f // times per second
|
||||
);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Main
|
||||
//
|
||||
ScoreMonitor = function()
|
||||
{
|
||||
.m_gameSpeed = .m_speedup * .m_levels[.m_level][0];
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// update scores and levels.
|
||||
.m_totalScore = 0;
|
||||
foreach(snake in .m_snakes)
|
||||
{
|
||||
.m_totalScore = .m_totalScore + snake.m_score;
|
||||
}
|
||||
|
||||
// are they at the next level
|
||||
if(.m_totalScore >= .m_levels[.m_level][3])
|
||||
{
|
||||
// level up.... have we finished
|
||||
.m_level = .m_level + 1;
|
||||
if(.m_level >= tableCount(.m_levels))
|
||||
{
|
||||
// you win....
|
||||
XYTEXT(0,0,"YOUWIN");
|
||||
sleep(5);
|
||||
.gameover = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// next level
|
||||
.m_gameSpeed = .m_speedup * .m_levels[.m_level][0];
|
||||
}
|
||||
}
|
||||
|
||||
XYTEXT(8, 0, format("LEVEL %d, SCORE %d, NEXT LEVEL TARGET %d", .m_level, .m_totalScore, .m_levels[.m_level][3]));
|
||||
if(showMemoryUsage)
|
||||
{
|
||||
XYTEXT(8, 1, format("mem %d Desired H: %d S: %d F: %d I: %d W: %d",
|
||||
sysGetMemoryUsage(),
|
||||
sysGetDesiredMemoryUsageHard(),
|
||||
sysGetDesiredMemoryUsageSoft(),
|
||||
sysGetStatsGCNumFullCollects(),
|
||||
sysGetStatsGCNumIncCollects(),
|
||||
sysGetStatsGCNumWarnings() ));
|
||||
}
|
||||
sleep(0.1);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Title Screen
|
||||
//
|
||||
|
||||
// todo
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Main
|
||||
//
|
||||
|
||||
DrawScreen();
|
||||
|
||||
// start the snakes
|
||||
foreach(snake in g_game.m_snakes)
|
||||
{
|
||||
snake.kht = snake:thread(snake.KeyHandler, g_game);
|
||||
snake.ut = snake:thread(snake.Update, g_game);
|
||||
}
|
||||
|
||||
// start the gems
|
||||
egt = thread(EmitGems, g_game);
|
||||
rgt = thread(ReclaimGems, g_game);
|
||||
dgt = thread(DrawGems, g_game);
|
||||
mmt = thread(MoveMovers, g_game);
|
||||
smt = g_game:thread(ScoreMonitor);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// test for escape key
|
||||
if(g_game.gameover or ISPRESSED(27))
|
||||
{
|
||||
// kill threads
|
||||
threadKill(egt);
|
||||
threadKill(rgt);
|
||||
threadKill(dgt);
|
||||
threadKill(smt);
|
||||
threadKill(mmt);
|
||||
|
||||
foreach(snake in g_game.m_snakes)
|
||||
{
|
||||
threadKill(snake.kht);
|
||||
threadKill(snake.ut);
|
||||
}
|
||||
exit();
|
||||
}
|
||||
yield();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
413
vscript/languages/gm/src/binds/gmArrayLib.cpp
Normal file
413
vscript/languages/gm/src/binds/gmArrayLib.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmArrayLib.h"
|
||||
#include "gmThread.h"
|
||||
#include "gmMachine.h"
|
||||
#include "gmHelpers.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
//
|
||||
//
|
||||
// Implementation of array binding
|
||||
//
|
||||
//
|
||||
|
||||
#if GM_ARRAY_LIB
|
||||
|
||||
// Statics and globals
|
||||
gmType GM_ARRAY = GM_NULL;
|
||||
gmVariable gmUserArray::m_null;
|
||||
|
||||
|
||||
bool gmUserArray::Construct(gmMachine * a_machine, int a_size)
|
||||
{
|
||||
m_null.Nullify();
|
||||
m_array = NULL;
|
||||
m_size = 0;
|
||||
return Resize(a_machine, a_size);
|
||||
}
|
||||
|
||||
|
||||
void gmUserArray::Destruct(gmMachine * a_machine)
|
||||
{
|
||||
if(m_array)
|
||||
{
|
||||
a_machine->Sys_Free(m_array);
|
||||
m_array = NULL;
|
||||
}
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
|
||||
bool gmUserArray::Resize(gmMachine * a_machine, int a_size)
|
||||
{
|
||||
if(a_size < 0) a_size = 0;
|
||||
int copysize = (a_size > m_size) ? m_size : a_size;
|
||||
gmVariable * array = (gmVariable *) a_machine->Sys_Alloc(sizeof(gmVariable) * a_size);
|
||||
// copy contents.
|
||||
if(m_array)
|
||||
{
|
||||
memcpy(array, m_array, sizeof(gmVariable) * copysize);
|
||||
if(a_size > copysize)
|
||||
{
|
||||
memset(array + copysize, 0, sizeof(gmVariable) * (a_size - copysize));
|
||||
}
|
||||
a_machine->Sys_Free(m_array);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(array, 0, sizeof(gmVariable) * a_size);
|
||||
}
|
||||
m_array = array;
|
||||
m_size = a_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if GM_USE_INCGC
|
||||
bool gmUserArray::Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < m_size; ++i)
|
||||
{
|
||||
if(m_array[i].IsReference())
|
||||
{
|
||||
gmObject * object = GM_MOBJECT(a_machine, m_array[i].m_value.m_ref);
|
||||
a_gc->GetNextObject(object);
|
||||
++a_workDone;
|
||||
}
|
||||
}
|
||||
|
||||
++a_workDone;
|
||||
return true;
|
||||
}
|
||||
#else //GM_USE_INCGC
|
||||
void gmUserArray::Mark(gmMachine * a_machine, gmuint32 a_mark)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < m_size; ++i)
|
||||
{
|
||||
if(m_array[i].IsReference())
|
||||
{
|
||||
gmObject * object = GM_MOBJECT(a_machine, m_array[i].m_value.m_ref);
|
||||
if(object->NeedsMark(a_mark)) object->Mark(a_machine, a_mark);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //GM_USE_INCGC
|
||||
|
||||
|
||||
bool gmUserArray::Shift(int a_shift)
|
||||
{
|
||||
if(a_shift < 0) // shift left
|
||||
{
|
||||
a_shift = -a_shift;
|
||||
if(a_shift >= m_size)
|
||||
{
|
||||
memset(m_array, 0, m_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = m_size - a_shift;
|
||||
memmove(m_array, m_array + a_shift, sizeof(gmVariable) * size);
|
||||
memset(m_array + size, 0, sizeof(gmVariable) * a_shift);
|
||||
}
|
||||
}
|
||||
else if(a_shift > 0) // shift right
|
||||
{
|
||||
if(a_shift >= m_size)
|
||||
{
|
||||
memset(m_array, 0, m_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = m_size - a_shift;
|
||||
memmove(m_array + a_shift, m_array, sizeof(gmVariable) * size);
|
||||
memset(m_array, 0, sizeof(gmVariable) * a_shift);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int gmUserArray::Move(int a_dest, int a_src, int a_size)
|
||||
{
|
||||
int start = a_src;
|
||||
int dest = a_dest;
|
||||
int size = a_size;
|
||||
|
||||
if(start < 0)
|
||||
{
|
||||
size += start;
|
||||
dest -= start;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if(dest < 0)
|
||||
{
|
||||
size += dest;
|
||||
start -= dest;
|
||||
dest = 0;
|
||||
}
|
||||
|
||||
if(size <= 0) return 0;
|
||||
if(start >= m_size) return 0;
|
||||
if(dest >= m_size) return 0;
|
||||
if((dest + size) < 0) return 0;
|
||||
|
||||
if(start + size > m_size)
|
||||
{
|
||||
size = m_size - start;
|
||||
}
|
||||
if(dest + size > m_size)
|
||||
{
|
||||
size = m_size - dest;
|
||||
}
|
||||
if(size <= 0) return 0;
|
||||
|
||||
GM_ASSERT(dest >= 0);
|
||||
GM_ASSERT(start >= 0);
|
||||
GM_ASSERT(start + size <= m_size);
|
||||
GM_ASSERT(dest + size <= m_size);
|
||||
|
||||
memmove(m_array + dest, m_array + start, sizeof(gmVariable) * size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
gmUserArray* gmUserArray_Create(gmMachine* a_machine, int a_size)
|
||||
{
|
||||
gmUserArray * newArray = (gmUserArray *) a_machine->Sys_Alloc(sizeof(gmUserArray));
|
||||
newArray->Construct(a_machine, a_size);
|
||||
return newArray;
|
||||
}
|
||||
|
||||
|
||||
// functions
|
||||
|
||||
static int GM_CDECL gmfArray(gmThread * a_thread) // size
|
||||
{
|
||||
GM_INT_PARAM(size, 0, 0);
|
||||
gmUserArray * array = (gmUserArray *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmUserArray));
|
||||
array->Construct(a_thread->GetMachine(), size);
|
||||
a_thread->PushNewUser(array, GM_ARRAY);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfArraySize(gmThread * a_thread) // return size
|
||||
{
|
||||
gmUserObject * arrayObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
||||
if(arrayObject->m_user)
|
||||
{
|
||||
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
||||
a_thread->PushInt(array->Size());
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfArrayResize(gmThread * a_thread) // size
|
||||
{
|
||||
GM_INT_PARAM(size, 0, 0);
|
||||
gmUserObject * arrayObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
||||
if(arrayObject->m_user)
|
||||
{
|
||||
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
||||
array->Resize(a_thread->GetMachine(), size);
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfArrayShift(gmThread * a_thread) // shift
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_INT_PARAM(shift, 0);
|
||||
|
||||
gmUserObject * arrayObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
||||
if(arrayObject->m_user)
|
||||
{
|
||||
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
||||
array->Shift(shift);
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfArrayMove(gmThread * a_thread) // dst, src, size
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(3);
|
||||
GM_CHECK_INT_PARAM(dst, 0);
|
||||
GM_CHECK_INT_PARAM(src, 1);
|
||||
GM_CHECK_INT_PARAM(size, 2);
|
||||
|
||||
gmUserObject * arrayObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
||||
if(arrayObject->m_user)
|
||||
{
|
||||
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
||||
array->Move(dst, src, size);
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static void GM_CDECL gmArrayGetInd(gmThread * a_thread, gmVariable * a_operands)
|
||||
{
|
||||
gmUserObject * arrayObject = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
|
||||
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
||||
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
||||
if(a_operands[1].m_type == GM_INT)
|
||||
{
|
||||
int index = a_operands[1].m_value.m_int;
|
||||
*a_operands = array->GetAt(index);
|
||||
return;
|
||||
}
|
||||
a_operands->Nullify();
|
||||
}
|
||||
|
||||
static void GM_CDECL gmArraySetInd(gmThread * a_thread, gmVariable * a_operands)
|
||||
{
|
||||
gmUserObject * arrayObject = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
|
||||
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
||||
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
||||
if(a_operands[1].m_type == GM_INT)
|
||||
{
|
||||
int index = a_operands[1].m_value.m_int;
|
||||
|
||||
#if GM_USE_INCGC
|
||||
//Apply write barrier
|
||||
gmVariable oldVar = array->GetAt(index);
|
||||
if(oldVar.IsReference())
|
||||
{
|
||||
a_thread->GetMachine()->GetGC()->WriteBarrier((gmObject*)oldVar.m_value.m_ref);
|
||||
}
|
||||
#endif //GM_USE_INCGC
|
||||
|
||||
array->SetAt(index, a_operands[2]);
|
||||
}
|
||||
}
|
||||
|
||||
#if GM_USE_INCGC
|
||||
static void GM_CDECL gmGCDestructArrayUserType(gmMachine * a_machine, gmUserObject* a_object)
|
||||
{
|
||||
if(a_object->m_user)
|
||||
{
|
||||
gmUserArray * array = (gmUserArray *) a_object->m_user;
|
||||
array->Destruct(a_machine);
|
||||
a_machine->Sys_Free(array);
|
||||
}
|
||||
a_object->m_user = NULL;
|
||||
}
|
||||
|
||||
static bool GM_CDECL gmGCTraceArrayUserType(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
|
||||
{
|
||||
if(a_object->m_user)
|
||||
{
|
||||
gmUserArray * array = (gmUserArray *) a_object->m_user;
|
||||
return array->Trace(a_machine, a_gc, a_workLeftToGo, a_workDone);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else //GM_USE_INCGC
|
||||
static void GM_CDECL gmMarkArrayUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
|
||||
{
|
||||
if(a_object->m_user)
|
||||
{
|
||||
gmUserArray * array = (gmUserArray *) a_object->m_user;
|
||||
array->Mark(a_machine, a_mark);
|
||||
}
|
||||
}
|
||||
|
||||
static void GM_CDECL gmGCArrayUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
|
||||
{
|
||||
if(a_object->m_user)
|
||||
{
|
||||
gmUserArray * array = (gmUserArray *) a_object->m_user;
|
||||
array->Destruct(a_machine);
|
||||
a_machine->Sys_Free(array);
|
||||
}
|
||||
a_object->m_user = NULL;
|
||||
}
|
||||
#endif //GM_USE_INCGC
|
||||
|
||||
// libs
|
||||
|
||||
static gmFunctionEntry s_arrayLib[] =
|
||||
{
|
||||
/*gm
|
||||
\lib gm
|
||||
*/
|
||||
/*gm
|
||||
\function array
|
||||
\brief array will create a fixed size array object
|
||||
\param int size optional (0)
|
||||
\return array
|
||||
*/
|
||||
{"array", gmfArray},
|
||||
};
|
||||
|
||||
static gmFunctionEntry s_arrayTypeLib[] =
|
||||
{
|
||||
/*gm
|
||||
\lib array
|
||||
*/
|
||||
/*gm
|
||||
\function Size
|
||||
\brief Size will return the current size of the fixed array
|
||||
\return int array size
|
||||
*/
|
||||
{"Size", gmfArraySize},
|
||||
/*gm
|
||||
\function Resize
|
||||
\brief Resize will resize the array to a new size
|
||||
\param int size optional (0)
|
||||
\return null
|
||||
*/
|
||||
{"Resize", gmfArrayResize},
|
||||
/*gm
|
||||
\function Shift
|
||||
\brief Shift will shift slide the array elements by a delta, nulls are shifted in
|
||||
\param int delta
|
||||
\return null
|
||||
*/
|
||||
{"Shift", gmfArrayShift},
|
||||
/*gm
|
||||
\function Move
|
||||
\brief Move will perform a non destructive move on the array
|
||||
\param int dst
|
||||
\param int src
|
||||
\param int size
|
||||
\return null
|
||||
*/
|
||||
{"Move", gmfArrayMove},
|
||||
};
|
||||
|
||||
void gmBindArrayLib(gmMachine * a_machine)
|
||||
{
|
||||
gmUserArray::m_null.Nullify(); //Init static null
|
||||
|
||||
a_machine->RegisterLibrary(s_arrayLib, sizeof(s_arrayLib) / sizeof(s_arrayLib[0]));
|
||||
GM_ARRAY = a_machine->CreateUserType("array");
|
||||
a_machine->RegisterTypeLibrary(GM_ARRAY, s_arrayTypeLib, sizeof(s_arrayTypeLib) / sizeof(s_arrayTypeLib[0]));
|
||||
#if GM_USE_INCGC
|
||||
a_machine->RegisterUserCallbacks(GM_ARRAY, gmGCTraceArrayUserType, gmGCDestructArrayUserType);
|
||||
#else //GM_USE_INCGC
|
||||
a_machine->RegisterUserCallbacks(GM_ARRAY, gmMarkArrayUserType, gmGCArrayUserType);
|
||||
#endif //GM_USE_INCGC
|
||||
a_machine->RegisterTypeOperator(GM_ARRAY, O_GETIND, NULL, gmArrayGetInd);
|
||||
a_machine->RegisterTypeOperator(GM_ARRAY, O_SETIND, NULL, gmArraySetInd);
|
||||
}
|
||||
|
||||
#endif // GM_ARRAY_LIB
|
||||
95
vscript/languages/gm/src/binds/gmArrayLib.h
Normal file
95
vscript/languages/gm/src/binds/gmArrayLib.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMARRAYLIB_H_
|
||||
#define _GMARRAYLIB_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmVariable.h"
|
||||
|
||||
// Fwd decls
|
||||
class gmMachine;
|
||||
|
||||
#define GM_ARRAY_LIB 1
|
||||
#define GM_ARRAY_LIB_GROW_BY 16
|
||||
|
||||
#if GM_ARRAY_LIB
|
||||
|
||||
extern gmType GM_ARRAY;
|
||||
|
||||
void gmBindArrayLib(gmMachine * a_machine);
|
||||
|
||||
/*!
|
||||
\class gmUserArray
|
||||
*/
|
||||
class gmUserArray
|
||||
{
|
||||
public:
|
||||
|
||||
/// \brief Construct()
|
||||
bool Construct(gmMachine * a_machine, int a_size);
|
||||
|
||||
/// \brief Destruct()
|
||||
void Destruct(gmMachine * a_machine);
|
||||
#if GM_USE_INCGC
|
||||
bool Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone);
|
||||
#else //GM_USE_INCGC
|
||||
/// \brief Mark()
|
||||
void Mark(gmMachine * a_machine, gmuint32 a_mark);
|
||||
#endif //GM_USE_INCGC
|
||||
|
||||
/// \brief GetAt()
|
||||
GM_FORCEINLINE const gmVariable &GetAt(int a_index)
|
||||
{
|
||||
if(a_index >= 0 && a_index < m_size)
|
||||
{
|
||||
return m_array[a_index];
|
||||
}
|
||||
return m_null;
|
||||
}
|
||||
|
||||
/// \brief SetAt()
|
||||
GM_FORCEINLINE bool SetAt(int a_index, const gmVariable &a_variable)
|
||||
{
|
||||
if(a_index >= 0 && a_index < m_size)
|
||||
{
|
||||
m_array[a_index] = a_variable;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Size()
|
||||
GM_FORCEINLINE int Size() const { return m_size; }
|
||||
|
||||
/// \brief Resize()
|
||||
bool Resize(gmMachine * a_machine, int a_size);
|
||||
|
||||
/// \brief Shift()
|
||||
bool Shift(int a_shift);
|
||||
|
||||
/// \brief Move()
|
||||
int Move(int a_dest, int a_src, int a_size);
|
||||
|
||||
// data
|
||||
gmVariable * m_array;
|
||||
int m_size;
|
||||
|
||||
static gmVariable m_null;
|
||||
};
|
||||
|
||||
|
||||
/// \brief Create a GM_ARRAY. This much be put into a user object.
|
||||
gmUserArray* gmUserArray_Create(gmMachine* a_machine, int a_size = 0);
|
||||
|
||||
#endif // GM_ARRAY_LIB
|
||||
|
||||
#endif // _GMARRAYLIB_H_
|
||||
17
vscript/languages/gm/src/binds/gmCall.cpp
Normal file
17
vscript/languages/gm/src/binds/gmCall.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmCall.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
434
vscript/languages/gm/src/binds/gmCall.h
Normal file
434
vscript/languages/gm/src/binds/gmCall.h
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMCALL_H_
|
||||
#define _GMCALL_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmThread.h"
|
||||
#include "gmMachine.h"
|
||||
|
||||
#undef GetObject //Fix for Win32 where GetObject is #defined
|
||||
|
||||
/// \class gmCall
|
||||
/// \brief A helper class to call script functions from C
|
||||
/// Warning: Do not store any of the reference type return variables (eg. GM_SRING).
|
||||
/// As the object may be garbage collected. Instead, copy immediately as necessary.
|
||||
class gmCall
|
||||
{
|
||||
public:
|
||||
|
||||
/// \brief Constructor
|
||||
gmCall()
|
||||
{
|
||||
#ifdef GM_DEBUG_BUILD
|
||||
m_locked = false;
|
||||
#endif //GM_DEBUG_BUILD
|
||||
}
|
||||
|
||||
/// \brief Begin the call of a global function
|
||||
/// \param a_machine Virtual machine instance
|
||||
/// \param a_funcName Name of function
|
||||
/// \param a_this The 'this' used by the function.
|
||||
/// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
|
||||
/// \return true on sucess, false if function was not found.
|
||||
GM_FORCEINLINE bool BeginGlobalFunction(gmMachine * a_machine, const char * a_funcName,
|
||||
const gmVariable& a_this = gmVariable::s_null,
|
||||
bool a_delayExecuteFlag = false)
|
||||
{
|
||||
GM_ASSERT(a_machine);
|
||||
|
||||
gmStringObject * funcNameStringObj = a_machine->AllocPermanantStringObject(a_funcName); // Slow
|
||||
|
||||
return BeginGlobalFunction(a_machine, funcNameStringObj, a_this, a_delayExecuteFlag);
|
||||
}
|
||||
|
||||
/// \brief Begin the call of a global function
|
||||
/// \param a_machine Virtual machine instance
|
||||
/// \param a_funcNameStringObj A string object that was found or created earlier, much faster than creating from c string.
|
||||
/// \param a_this The 'this' used by the function.
|
||||
/// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
|
||||
/// \return true on sucess, false if function was not found.
|
||||
GM_FORCEINLINE bool BeginGlobalFunction(gmMachine * a_machine, gmStringObject * a_funcNameStringObj,
|
||||
const gmVariable& a_this = gmVariable::s_null,
|
||||
bool a_delayExecuteFlag = false)
|
||||
{
|
||||
GM_ASSERT(a_machine);
|
||||
GM_ASSERT(a_funcNameStringObj);
|
||||
|
||||
gmVariable lookUpVar;
|
||||
gmVariable foundFunc;
|
||||
|
||||
lookUpVar.SetString(a_funcNameStringObj);
|
||||
foundFunc = a_machine->GetGlobals()->Get(lookUpVar);
|
||||
|
||||
if( GM_FUNCTION == foundFunc.m_type ) // Check found variable is a function
|
||||
{
|
||||
gmFunctionObject * functionObj = (gmFunctionObject *)foundFunc.m_value.m_ref; //Func Obj from variable
|
||||
|
||||
return BeginFunction(a_machine, functionObj, a_this, a_delayExecuteFlag);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Begin the call of a object function
|
||||
/// \param a_machine Virtual machine instance
|
||||
/// \param a_funcName Name of function.
|
||||
/// \param a_tableObj The table on the object to look up the function.
|
||||
/// \param a_this The 'this' used by the function.
|
||||
/// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
|
||||
/// \return true on sucess, false if function was not found.
|
||||
GM_FORCEINLINE bool BeginTableFunction(gmMachine * a_machine, const char * a_funcName,
|
||||
gmTableObject * a_tableObj, const gmVariable& a_this = gmVariable::s_null,
|
||||
bool a_delayExecuteFlag = false)
|
||||
{
|
||||
GM_ASSERT(a_machine);
|
||||
GM_ASSERT(a_funcName);
|
||||
|
||||
gmStringObject * funcNameStringObj = a_machine->AllocPermanantStringObject(a_funcName); // Slow
|
||||
|
||||
return BeginTableFunction(a_machine, funcNameStringObj, a_tableObj, a_this, a_delayExecuteFlag);
|
||||
}
|
||||
|
||||
/// \brief Begin the call of a object function
|
||||
/// \param a_machine Virtual machine instance
|
||||
/// \param a_funcNameStringObj A string object that was found or created earlier, much faster than creating from c string.
|
||||
/// \param a_tableObj The table on the object to look up the function.
|
||||
/// \param a_this The 'this' used by the function.
|
||||
/// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
|
||||
/// \return true on sucess, false if function was not found.
|
||||
GM_FORCEINLINE bool BeginTableFunction(gmMachine * a_machine, gmStringObject * a_funcNameStringObj,
|
||||
gmTableObject * a_tableObj, const gmVariable& a_this = gmVariable::s_null,
|
||||
bool a_delayExecuteFlag = false)
|
||||
{
|
||||
GM_ASSERT(a_machine);
|
||||
//GM_ASSERT(a_tableObj);
|
||||
GM_ASSERT(a_funcNameStringObj);
|
||||
|
||||
gmVariable lookUpVar;
|
||||
gmVariable foundFunc;
|
||||
|
||||
if ( !a_tableObj )
|
||||
{
|
||||
a_tableObj = a_machine->GetGlobals();
|
||||
}
|
||||
|
||||
lookUpVar.SetString(a_funcNameStringObj);
|
||||
foundFunc = a_tableObj->Get(lookUpVar);
|
||||
|
||||
if( GM_FUNCTION == foundFunc.m_type ) // Check found variable is a function
|
||||
{
|
||||
gmFunctionObject * functionObj = (gmFunctionObject *)foundFunc.m_value.m_ref; //Func Obj from variable
|
||||
return BeginFunction(a_machine, functionObj, a_this, a_delayExecuteFlag);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Begin the call of a object function
|
||||
/// \param a_funcObj A function object that was found or created earlier.
|
||||
/// \param a_tableObj The table on the object to look up the function.
|
||||
/// \param a_this The 'this' used by the function.
|
||||
/// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
|
||||
/// \return true on sucess, false if function was not found.
|
||||
GM_FORCEINLINE bool BeginFunction(gmMachine * a_machine, gmFunctionObject * a_funcObj,
|
||||
const gmVariable &a_thisVar = gmVariable::s_null,
|
||||
bool a_delayExecuteFlag = false)
|
||||
{
|
||||
GM_ASSERT(a_machine);
|
||||
GM_ASSERT(a_funcObj);
|
||||
|
||||
#ifdef GM_DEBUG_BUILD
|
||||
// YOU CANNOT NEST gmCall::Begin
|
||||
GM_ASSERT(m_locked == false);
|
||||
m_locked = true;
|
||||
#endif //GM_DEBUG_BUILD
|
||||
|
||||
Reset(a_machine);
|
||||
|
||||
if( GM_FUNCTION == a_funcObj->GetType() ) // Check found variable is a function
|
||||
{
|
||||
m_thread = m_machine->CreateThread(); // Create thread for func to run on
|
||||
m_thread->Push(a_thisVar); // this
|
||||
m_thread->PushFunction(a_funcObj); // function
|
||||
m_delayExecuteFlag = a_delayExecuteFlag;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef GM_DEBUG_BUILD
|
||||
GM_ASSERT(m_locked == true);
|
||||
m_locked = false;
|
||||
#endif //GM_DEBUG_BUILD
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Add a parameter variable
|
||||
GM_FORCEINLINE void AddParam(const gmVariable& a_var)
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
GM_ASSERT(m_thread);
|
||||
|
||||
m_thread->Push(a_var);
|
||||
++m_paramCount;
|
||||
}
|
||||
|
||||
/// \brief Add a parameter that is null
|
||||
GM_FORCEINLINE void AddParamNull()
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
GM_ASSERT(m_thread);
|
||||
|
||||
m_thread->PushNull();
|
||||
++m_paramCount;
|
||||
}
|
||||
|
||||
/// \brief Add a parameter that is a integer
|
||||
GM_FORCEINLINE void AddParamInt(const int a_value)
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
GM_ASSERT(m_thread);
|
||||
|
||||
m_thread->PushInt(a_value);
|
||||
++m_paramCount;
|
||||
}
|
||||
|
||||
/// \brief Add a parameter that is a float
|
||||
GM_FORCEINLINE void AddParamFloat(const float a_value)
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
GM_ASSERT(m_thread);
|
||||
|
||||
m_thread->PushFloat(a_value);
|
||||
++m_paramCount;
|
||||
}
|
||||
|
||||
/// \brief Add a parameter that is a string
|
||||
GM_FORCEINLINE void AddParamString(const char * a_value, int a_len = -1)
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
GM_ASSERT(m_thread);
|
||||
|
||||
m_thread->PushNewString(a_value, a_len);
|
||||
++m_paramCount;
|
||||
}
|
||||
|
||||
/// \brief Add a parameter that is a string (faster version since c string does not need lookup)
|
||||
GM_FORCEINLINE void AddParamString(gmStringObject * a_value)
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
GM_ASSERT(m_thread);
|
||||
|
||||
m_thread->PushString(a_value);
|
||||
++m_paramCount;
|
||||
}
|
||||
|
||||
/// \brief Add a parameter that is a user object. Creates a new user object.
|
||||
/// \param a_value Pointer to user object data
|
||||
/// \param a_userType Type of user object beyond GM_USER
|
||||
GM_FORCEINLINE void AddParamUser(void * a_value, int a_userType)
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
GM_ASSERT(m_thread);
|
||||
|
||||
m_thread->PushNewUser(a_value, a_userType);
|
||||
++m_paramCount;
|
||||
}
|
||||
|
||||
/// \brief Add a parameter that is a user object.
|
||||
/// \param a_userObj Pushes an existing user object without creating a new one.
|
||||
GM_FORCEINLINE void AddParamUser(gmUserObject * a_userObj)
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
GM_ASSERT(m_thread);
|
||||
|
||||
m_thread->PushUser(a_userObj);
|
||||
++m_paramCount;
|
||||
}
|
||||
|
||||
/// \brief Add a parameter that is a table object.
|
||||
/// \param a_tableObj Pushes an existing table object without creating a new one.
|
||||
GM_FORCEINLINE void AddParamTable(gmTableObject * a_tableObj)
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
GM_ASSERT(m_thread);
|
||||
|
||||
m_thread->PushTable(a_tableObj);
|
||||
++m_paramCount;
|
||||
}
|
||||
|
||||
/// \brief Make the call. If a return value was expected, it will be set in here.
|
||||
/// \param a_threadId Optional
|
||||
GM_FORCEINLINE void End(int * a_threadId = NULL)
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
GM_ASSERT(m_thread);
|
||||
|
||||
#ifdef GM_DEBUG_BUILD
|
||||
// CAN ONLY CALL ::End() after a successful ::Begin
|
||||
GM_ASSERT(m_locked == true);
|
||||
m_locked = false;
|
||||
#endif //GM_DEBUG_BUILD
|
||||
|
||||
int state = m_thread->PushStackFrame(m_paramCount);
|
||||
if(state != gmThread::KILLED) // Can be killed immedialy if it was a C function
|
||||
{
|
||||
if(m_delayExecuteFlag)
|
||||
{
|
||||
state = m_thread->GetState();
|
||||
}
|
||||
else
|
||||
{
|
||||
state = m_thread->Sys_Execute(&m_returnVar);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Was a C function call, grab return var off top of stack
|
||||
m_returnVar = *(m_thread->GetTop() - 1);
|
||||
m_machine->Sys_SwitchState(m_thread, gmThread::KILLED);
|
||||
}
|
||||
|
||||
// If we requested a thread Id
|
||||
if(a_threadId)
|
||||
{
|
||||
if(state != gmThread::KILLED)
|
||||
{
|
||||
*a_threadId = m_thread->GetId();
|
||||
}
|
||||
else
|
||||
{
|
||||
*a_threadId = GM_INVALID_THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
if(state == gmThread::KILLED)
|
||||
{
|
||||
m_returnFlag = true; // Function always returns something, null if not explicit.
|
||||
m_thread = NULL; // Thread has exited, no need to remember it.
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Accesss thread created for function call.
|
||||
GM_FORCEINLINE gmThread * GetThread() { return m_thread; }
|
||||
|
||||
/// \brief Returns reference to 'return' variable. Never fails, but variable may be a 'null' varaible if none was returned.
|
||||
const gmVariable& GetReturnedVariable() { return m_returnVar; }
|
||||
|
||||
/// \brief Returns true if function exited and returned a variable.
|
||||
bool DidReturnVariable() { return m_returnFlag; }
|
||||
|
||||
/// \brief Did function return a null?
|
||||
/// \return true if function returned null.
|
||||
bool GetReturnedNull()
|
||||
{
|
||||
if(m_returnFlag && (m_returnVar.m_type == GM_NULL))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Get returned int
|
||||
/// \return true if function returned an int.
|
||||
bool GetReturnedInt(int& a_value)
|
||||
{
|
||||
if(m_returnFlag && (m_returnVar.m_type == GM_INT))
|
||||
{
|
||||
a_value = m_returnVar.m_value.m_int;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Get returned float
|
||||
/// \return true if function returned an float.
|
||||
bool GetReturnedFloat(float& a_value)
|
||||
{
|
||||
if(m_returnFlag && (m_returnVar.m_type == GM_FLOAT))
|
||||
{
|
||||
a_value = m_returnVar.m_value.m_float;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Get returned string
|
||||
/// \return true if function returned an string.
|
||||
bool GetReturnedString(const char *& a_value)
|
||||
{
|
||||
if(m_returnFlag && (m_returnVar.m_type == GM_STRING))
|
||||
{
|
||||
a_value = ((gmStringObject *)m_machine->GetObject(m_returnVar.m_value.m_ref))->GetString();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Get returned user
|
||||
/// \return true if function returned an user.
|
||||
bool GetReturnedUser(void *& a_value, int a_userType)
|
||||
{
|
||||
if(m_returnFlag && (m_returnVar.m_type == a_userType))
|
||||
{
|
||||
a_value = (void *)m_returnVar.m_value.m_ref;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Get returned user or null
|
||||
/// \return true if function returned an user or null.
|
||||
bool GetReturnedUserOrNull(void *& a_value, int a_userType)
|
||||
{
|
||||
if(m_returnFlag)
|
||||
{
|
||||
if(m_returnVar.m_type == a_userType)
|
||||
{
|
||||
a_value = (void *)m_returnVar.m_value.m_ref;
|
||||
return true;
|
||||
}
|
||||
else if(m_returnVar.m_type == GM_NULL)
|
||||
{
|
||||
a_value = (void *)NULL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
gmMachine * m_machine;
|
||||
gmThread * m_thread;
|
||||
gmVariable m_returnVar;
|
||||
int m_paramCount;
|
||||
bool m_returnFlag;
|
||||
bool m_delayExecuteFlag;
|
||||
#ifdef GM_DEBUG_BUILD
|
||||
bool m_locked;
|
||||
#endif //GM_DEBUG_BUILD
|
||||
|
||||
/// \brief Used internally to clear call information.
|
||||
GM_FORCEINLINE void Reset(gmMachine * a_machine)
|
||||
{
|
||||
GM_ASSERT(a_machine);
|
||||
m_machine = a_machine;
|
||||
m_thread = NULL;
|
||||
m_returnVar.Nullify();
|
||||
m_returnFlag = false;
|
||||
m_paramCount = 0;
|
||||
m_delayExecuteFlag = false;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // _GMCALL_H_
|
||||
215
vscript/languages/gm/src/binds/gmGCRoot.cpp
Normal file
215
vscript/languages/gm/src/binds/gmGCRoot.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// gmGCRoot.cpp
|
||||
//
|
||||
|
||||
#include "gmGCRoot.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
// Init statics and constants
|
||||
gmGCRootManager* gmGCRootManager::s_staticInstance;
|
||||
|
||||
|
||||
gmGCRootManager::gmGCRootManager()
|
||||
{
|
||||
m_machineHolderSet.Init(0, 4);
|
||||
}
|
||||
|
||||
|
||||
gmGCRootManager::~gmGCRootManager()
|
||||
{
|
||||
DestroyAllMachines();
|
||||
}
|
||||
|
||||
|
||||
void gmGCRootManager::Init()
|
||||
{
|
||||
GM_ASSERT( !s_staticInstance );
|
||||
|
||||
if( !s_staticInstance )
|
||||
{
|
||||
s_staticInstance = new gmGCRootManager;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gmGCRootManager::Destroy()
|
||||
{
|
||||
if( s_staticInstance )
|
||||
{
|
||||
delete s_staticInstance;
|
||||
s_staticInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gmgcrHolder* gmGCRootManager::FindOrAdd(gmObject* a_object, gmMachine* a_machine)
|
||||
{
|
||||
GM_ASSERT(a_object);
|
||||
GM_ASSERT(a_machine);
|
||||
|
||||
if( !a_object || !a_machine )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MachineHolders* machineSet = FindOrAddMachine(a_machine);
|
||||
|
||||
gmgcrHolder* holder = NULL;
|
||||
if( !machineSet->m_mapPtrToHolder.GetAt(a_object, holder) )
|
||||
{
|
||||
// Add new
|
||||
holder = m_memHolder.Alloc();
|
||||
holder->Init(a_object, a_machine);
|
||||
machineSet->m_mapPtrToHolder.SetAt(a_object, holder);
|
||||
|
||||
a_machine->AddCPPOwnedGMObject(a_object); // Add to GC roots
|
||||
}
|
||||
return holder;
|
||||
}
|
||||
|
||||
|
||||
void gmGCRootManager::RemoveObject(gmObject* a_object, gmMachine* a_machine)
|
||||
{
|
||||
if( a_object && a_machine )
|
||||
{
|
||||
a_machine->RemoveCPPOwnedGMObject(a_object); // Remove from GC roots
|
||||
|
||||
gmGCRootManager::MachineHolders* machineSet = FindOrAddMachine(a_machine);
|
||||
|
||||
machineSet->m_mapPtrToHolder.RemoveAt(a_object);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gmGCRootManager::FreeHolder(const gmgcrHolder* a_holder)
|
||||
{
|
||||
if( a_holder )
|
||||
{
|
||||
m_memHolder.Free( const_cast<gmgcrHolder*>(a_holder) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gmGCRootManager::DestroyMachine(gmMachine* a_machine)
|
||||
{
|
||||
int foundIndex = -1;
|
||||
|
||||
// Find existing set
|
||||
for(int mIndex=0; mIndex < m_machineHolderSet.GetSize(); ++mIndex)
|
||||
{
|
||||
if( m_machineHolderSet[mIndex].m_machine == a_machine )
|
||||
{
|
||||
foundIndex = mIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( foundIndex >= 0 )
|
||||
{
|
||||
MachineHolders* destroySet = &m_machineHolderSet[foundIndex];
|
||||
|
||||
// Cleanup all internal pointers
|
||||
gmgcrMap<gmObject*, gmgcrHolder*>::Iterator it;
|
||||
|
||||
// Iterate carefully as we are deleting while iterating
|
||||
destroySet->m_mapPtrToHolder.GetFirst(it);
|
||||
while( !destroySet->m_mapPtrToHolder.IsNull(it) )
|
||||
{
|
||||
gmgcrHolder* toDelete = NULL;
|
||||
if( destroySet->m_mapPtrToHolder.GetValue(it, toDelete) )
|
||||
{
|
||||
// NOTE: Delete this found value after incrementing iterator
|
||||
}
|
||||
destroySet->m_mapPtrToHolder.GetNext(it);
|
||||
if( toDelete )
|
||||
{
|
||||
toDelete->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove machine set
|
||||
m_machineHolderSet.RemoveAt(foundIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gmGCRootManager::MachineHolders* gmGCRootManager::FindOrAddMachine(gmMachine* a_machine)
|
||||
{
|
||||
// NOTE: Expect very small number of machines. If this is not so, accelerate search.
|
||||
|
||||
GM_ASSERT( a_machine );
|
||||
|
||||
// Find existing set
|
||||
for(int mIndex=0; mIndex < m_machineHolderSet.GetSize(); ++mIndex)
|
||||
{
|
||||
if( m_machineHolderSet[mIndex].m_machine == a_machine )
|
||||
{
|
||||
return &m_machineHolderSet[mIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// Create new set
|
||||
int newIndex = m_machineHolderSet.AddEmpty();
|
||||
MachineHolders* newSet = &m_machineHolderSet[newIndex];
|
||||
newSet->m_machine = a_machine;
|
||||
return newSet;
|
||||
}
|
||||
|
||||
|
||||
void gmGCRootManager::DestroyAllMachines()
|
||||
{
|
||||
while( m_machineHolderSet.GetSize() )
|
||||
{
|
||||
DestroyMachine( m_machineHolderSet[m_machineHolderSet.GetLastIndex()].m_machine );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
// Test the gmGCRoot system
|
||||
void TestGMGCRoot()
|
||||
{
|
||||
gmGCRootManager::Init(); // Init system
|
||||
{
|
||||
gmMachine machine1;
|
||||
|
||||
gmGCRoot<gmStringObject> ptr1;
|
||||
|
||||
gmStringObject* stringObj1 = machine1.AllocStringObject("hello");
|
||||
|
||||
ptr1.Set(stringObj1, &machine1); // Initialize via func
|
||||
|
||||
gmStringObject* nat1 = ptr1; // Assign pointer
|
||||
|
||||
gmGCRoot<gmStringObject> ptr2;
|
||||
ptr2 = ptr1;
|
||||
|
||||
ptr1 = NULL; // Assign to null
|
||||
ptr2 = NULL;
|
||||
//ptr2 = nat1; // ERROR
|
||||
|
||||
gmStringObject* stringObj2 = machine1.AllocStringObject("apple");
|
||||
gmGCRoot<gmStringObject> ptr3(stringObj2, &machine1); // Initialize via constructor
|
||||
gmGCRoot<gmStringObject> ptr4(stringObj2, &machine1); // Duplicate without copy
|
||||
|
||||
gmGCRoot<gmStringObject> ptr5;
|
||||
{
|
||||
gmMachine machine2;
|
||||
|
||||
gmStringObject* stringObj3 = machine2.AllocStringObject("bannana");
|
||||
ptr5.Set(stringObj3, &machine2);
|
||||
|
||||
gmGCRootManager::Get()->DestroyMachine(&machine2); // Null associated pointers
|
||||
}
|
||||
if( ptr5 )
|
||||
{
|
||||
int i=1; // Won't get here
|
||||
}
|
||||
|
||||
}
|
||||
gmGCRootManager::Destroy();
|
||||
}
|
||||
#endif
|
||||
399
vscript/languages/gm/src/binds/gmGCRoot.h
Normal file
399
vscript/languages/gm/src/binds/gmGCRoot.h
Normal file
@@ -0,0 +1,399 @@
|
||||
#ifndef GMGCROOT_H
|
||||
#define GMGCROOT_H
|
||||
|
||||
//
|
||||
// gmGCRoot.h
|
||||
//
|
||||
|
||||
#include "gmThread.h"
|
||||
#include "gmGCRootUtil.h"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// gmGCRoot
|
||||
//
|
||||
// Templated smart pointer style wrapper for gmObject*
|
||||
// Automatically adds and removed CPP owned gmObject* from gmMachine roots
|
||||
//
|
||||
// It works by storing the gmObject* inside a shared reference counted holder
|
||||
// object. When a gmObject* is first wrapped, a holder object is created to
|
||||
// store it. Subsequent initializations will share the same holder object. It
|
||||
// is much more efficient to assign the pointer from one gmGCRoot to another
|
||||
// than create a new one from the raw gmObject*.
|
||||
//
|
||||
//
|
||||
// To use, you must initialize and destroy the system with:
|
||||
// gmGCRootManager::Init();
|
||||
// gmGCRootManager::Destroy();
|
||||
// When you destruct a gmMachine that was used by the system, call this first:
|
||||
// gmGCRootManager::Get()->DestroyMachine(myMachine);
|
||||
// Initialize a pointer by either:
|
||||
// gmGCRoot<gmStringObject> ptr1(myObject, myMachine);
|
||||
// gmGCRoot<gmStringObject> ptr2; ptr2.Set(myObject, myMachine);
|
||||
// Copy and test pointers as you would a C style pointer eg:
|
||||
// if( ptr1 ) { int type = ptr1->GetType(); }
|
||||
// ptr2 = ptr1;
|
||||
// ptr1 = NULL;
|
||||
//
|
||||
//
|
||||
// Example usage:
|
||||
// gmGCRootManager::Init(); // Call once first eg. App initialization
|
||||
// gmGCRoot<gmStringObject> ptr1; // A null pointer
|
||||
// gmGCRoot<gmStringObject> ptr2(myString, myMachine); // A pointer
|
||||
// gmStringObject* nat1 = ptr2; // Cast to native
|
||||
// gmGCRoot<gmStringObject> ptr3 = ptr2; // Assign from other (Fast copy of shared pointer)
|
||||
// ptr2 = NULL; // Assign to null
|
||||
// ptr2 = nat1; // COMPILE ERROR! can't assign without gmMachine
|
||||
// gmGCRootManager::Get()->DestroyMachine(myMachine); // Call before destructing a gmMachine
|
||||
// gmGCRootManager::Destroy(); // Call once before App exit
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Fwd decls
|
||||
class gmGCRootManager;
|
||||
class gmgcrHolder;
|
||||
|
||||
|
||||
/// Bass class for objects reference counted types
|
||||
class gmgcrRefCount
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct, starts reference count at 0 (Must be wrapped in smart ptr to increment and later release)
|
||||
gmgcrRefCount() { m_referenceCount = 0; }
|
||||
/// Destructor
|
||||
virtual ~gmgcrRefCount() { }
|
||||
|
||||
/// Get reference count
|
||||
int GetReferenceCount() const { return m_referenceCount; }
|
||||
|
||||
/// Increment reference count
|
||||
void AddReference() const { ++m_referenceCount; }
|
||||
|
||||
/// Decrement reference count
|
||||
int ReleaseReference() const
|
||||
{
|
||||
GM_ASSERT( m_referenceCount > 0 );
|
||||
if( --m_referenceCount == 0 )
|
||||
{
|
||||
DeleteThis();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Delete this object. May override if this is not desired behavior.
|
||||
/// NOTE: This function is 'const' so override with const also!
|
||||
virtual void DeleteThis() const { delete this; }
|
||||
|
||||
private:
|
||||
|
||||
mutable int m_referenceCount; ///< Reference counter
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// Manager for gm gc roots
|
||||
class gmGCRootManager
|
||||
{
|
||||
public:
|
||||
|
||||
/// Access singleton
|
||||
static gmGCRootManager* Get() { return s_staticInstance; }
|
||||
|
||||
/// Initialize gm gcroot manager
|
||||
static void Init();
|
||||
|
||||
/// Destroy gm gcroot manager
|
||||
static void Destroy();
|
||||
|
||||
~gmGCRootManager();
|
||||
|
||||
// Find or Add shard holder for gmObject
|
||||
gmgcrHolder* FindOrAdd(gmObject* a_object, gmMachine* a_machine);
|
||||
|
||||
// Free holder memory only
|
||||
void FreeHolder(const gmgcrHolder* a_holder);
|
||||
|
||||
// Dissassociate holder contents
|
||||
void RemoveObject(gmObject* a_object, gmMachine* a_machine);
|
||||
|
||||
/// Call before destroying a gmMachine so that any associated objects can be disassociated and have pointers nullified.
|
||||
void DestroyMachine(gmMachine* a_machine);
|
||||
|
||||
protected:
|
||||
|
||||
// Store gmObjects per machine
|
||||
class MachineHolders
|
||||
{
|
||||
public:
|
||||
gmMachine* m_machine;
|
||||
//gmgcrMap m_mapPtrToHolder;
|
||||
gmgcrMap<gmObject*, gmgcrHolder*> m_mapPtrToHolder;
|
||||
};
|
||||
|
||||
static gmGCRootManager* s_staticInstance; // Singleton
|
||||
|
||||
gmgcrArray< MachineHolders > m_machineHolderSet; // Holders per machine
|
||||
gmgcrMemFixed m_memHolder; // Holder memory
|
||||
|
||||
// Non public constructor to prevent multiple instances
|
||||
gmGCRootManager();
|
||||
|
||||
// Find or add machine
|
||||
MachineHolders* FindOrAddMachine(gmMachine* a_machine);
|
||||
|
||||
// Destroy all machine associations
|
||||
void DestroyAllMachines();
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// Holder that is always valid and may contain the real pointer
|
||||
class gmgcrHolder : public gmgcrRefCount
|
||||
{
|
||||
public:
|
||||
|
||||
/// Default contsructor for simple allocation, Init() should be called immediately.
|
||||
gmgcrHolder()
|
||||
{
|
||||
m_object = NULL;
|
||||
m_machine = NULL;
|
||||
}
|
||||
|
||||
/// Initialize with real object
|
||||
void Init(gmObject* a_object,
|
||||
gmMachine* a_machine)
|
||||
{
|
||||
m_object = a_object;
|
||||
m_machine = a_machine;
|
||||
}
|
||||
|
||||
/// Destroy object pointer (May occur before this holder dies)
|
||||
void Destroy() const
|
||||
{
|
||||
if( m_object )
|
||||
{
|
||||
GM_ASSERT( gmGCRootManager::Get() );
|
||||
gmGCRootManager::Get()->RemoveObject(m_object, m_machine);
|
||||
}
|
||||
|
||||
m_object = NULL;
|
||||
m_machine = NULL;
|
||||
}
|
||||
|
||||
/// Get the real pointer
|
||||
void* GetPtr() { return m_object; }
|
||||
|
||||
private:
|
||||
|
||||
/// Called when reference count reaches zero
|
||||
void DeleteThis() const
|
||||
{
|
||||
Destroy();
|
||||
|
||||
GM_ASSERT( gmGCRootManager::Get() );
|
||||
|
||||
gmGCRootManager::Get()->FreeHolder(this);
|
||||
}
|
||||
|
||||
mutable gmObject* m_object; ///< The object
|
||||
mutable gmMachine* m_machine; ///< The owning machine
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// This is the smart pointer to a gmObject
|
||||
/// It adds the gmObject to the gmMachine GC roots so that the object is not collected while held
|
||||
/// Initialize via constructor or Set(). eg. gmGCRoot<gmStringObject> ptr(myString, myMachine);
|
||||
/// Fast cast to native type and fast copy to compatible pointer.
|
||||
/// Slower FIRST construct and FINAL destruct as shared holder object performs map insert/removal.
|
||||
/// If gmMachine is destructed, contents will be nullified. (As long as manager was called correctly.)
|
||||
template< typename TYPE >
|
||||
class gmGCRoot
|
||||
{
|
||||
public:
|
||||
|
||||
/// Empty Constructor. Equals NULL.
|
||||
gmGCRoot() { m_ptrToHolder = NULL; }
|
||||
|
||||
// NOTE: Currently not very safe since holder and controller are not typed.
|
||||
/// Construct from holder
|
||||
explicit gmGCRoot(gmgcrHolder* a_holder)
|
||||
{
|
||||
m_ptrToHolder = a_holder;
|
||||
if( m_ptrToHolder )
|
||||
{
|
||||
m_ptrToHolder->AddReference();
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct from pointers
|
||||
gmGCRoot(TYPE* a_object, gmMachine* a_machine)
|
||||
{
|
||||
m_ptrToHolder = NULL;
|
||||
Set(a_object, a_machine);
|
||||
}
|
||||
|
||||
/// Set pointer
|
||||
void Set(TYPE* a_object, gmMachine* a_machine)
|
||||
{
|
||||
if( m_ptrToHolder )
|
||||
{
|
||||
m_ptrToHolder->ReleaseReference();
|
||||
}
|
||||
|
||||
if( !a_object ) // Handle null assignment
|
||||
{
|
||||
m_ptrToHolder = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
GM_ASSERT( gmGCRootManager::Get() );
|
||||
|
||||
m_ptrToHolder = gmGCRootManager::Get()->FindOrAdd( (gmObject*)a_object, (gmMachine*) a_machine );
|
||||
m_ptrToHolder->AddReference();
|
||||
}
|
||||
|
||||
/// Copy Constructor
|
||||
gmGCRoot(const gmGCRoot<TYPE>& a_ref)
|
||||
{
|
||||
m_ptrToHolder = a_ref.m_ptrToHolder;
|
||||
if (m_ptrToHolder != NULL)
|
||||
{
|
||||
m_ptrToHolder->AddReference();
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
~gmGCRoot()
|
||||
{
|
||||
if( m_ptrToHolder )
|
||||
{
|
||||
m_ptrToHolder->ReleaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the referenced type
|
||||
operator TYPE* () { return (TYPE*)m_ptrToHolder->GetPtr(); }
|
||||
operator const TYPE* () const { return (TYPE*)m_ptrToHolder->GetPtr(); }
|
||||
|
||||
/// Access the object as pointer
|
||||
TYPE* operator -> () { return (TYPE*)m_ptrToHolder->GetPtr(); }
|
||||
const TYPE* operator -> () const { return (TYPE*)m_ptrToHolder->GetPtr(); }
|
||||
|
||||
/// Access the object as reference
|
||||
TYPE& operator * () { return *((TYPE*)m_ptrToHolder->GetPtr()); }
|
||||
const TYPE& operator * () const { return *((TYPE*)m_ptrToHolder->GetPtr()); }
|
||||
|
||||
/// Access object with standard function instead of operator. Useful when compiler finds conversion ambigous.
|
||||
TYPE* Resolve() { return (TYPE*)m_ptrToHolder->GetPtr(); }
|
||||
const TYPE* Resolve() const { return (TYPE*)m_ptrToHolder->GetPtr(); }
|
||||
|
||||
/// Assign from other reference
|
||||
gmGCRoot<TYPE>& operator = (const gmGCRoot<TYPE>& a_ref)
|
||||
{
|
||||
if( m_ptrToHolder != a_ref.GetHolder() )
|
||||
{
|
||||
if( m_ptrToHolder != NULL )
|
||||
{
|
||||
m_ptrToHolder->ReleaseReference();
|
||||
}
|
||||
m_ptrToHolder = a_ref.GetHolder();
|
||||
if(m_ptrToHolder)
|
||||
{
|
||||
m_ptrToHolder->AddReference();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Assign to NULL. Make shorter code path for assignment with zero (null in C++)
|
||||
gmGCRoot<TYPE>& operator = (int a_null)
|
||||
{
|
||||
GM_ASSERT(a_null == 0);
|
||||
if( m_ptrToHolder != NULL )
|
||||
{
|
||||
m_ptrToHolder->ReleaseReference();
|
||||
}
|
||||
m_ptrToHolder = NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Equality test
|
||||
int operator == (const gmGCRoot<TYPE>& a_ref) const
|
||||
{
|
||||
if( m_ptrToHolder == a_ref.GetHolder() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Both holders cannot be NULL at this point, but one maybe.
|
||||
if( (a_ref.GetHolder() == NULL) && (m_ptrToHolder->GetPtr() == NULL) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if( m_ptrToHolder == NULL )
|
||||
{
|
||||
if( a_ref.GetHolder()->GetPtr() == NULL )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( a_ref.GetHolder() != NULL )
|
||||
{
|
||||
if( a_ref.GetHolder()->GetPtr() == m_ptrToHolder->GetPtr() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Non equality test
|
||||
int operator != (const gmGCRoot<TYPE>& a_ref) const { return !operator==(a_ref); }
|
||||
|
||||
/// Less than comparison for use in Containers
|
||||
int operator < (const gmGCRoot<TYPE>& a_ref) const
|
||||
{
|
||||
if( a_ref.GetHolder() == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( m_ptrToHolder == NULL )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return ( m_ptrToHolder->GetPtr() < a_ref.GetHolder()->GetPtr() );
|
||||
}
|
||||
|
||||
/// Greater than comparison for use in Containers
|
||||
int operator > (const gmGCRoot<TYPE>& a_ref) const
|
||||
{
|
||||
if( m_ptrToHolder == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( a_ref.GetHolder() == NULL )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return ( m_ptrToHolder->GetPtr() > a_ref.GetHolder()->GetPtr() );
|
||||
}
|
||||
|
||||
/// Access the holder. Necessary since this is a templated class.
|
||||
gmgcrHolder* GetHolder() const { return m_ptrToHolder; }
|
||||
|
||||
private:
|
||||
|
||||
mutable gmgcrHolder* m_ptrToHolder; ///< Pointer to holder of the real pointer
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //GMGCROOT_H
|
||||
33
vscript/languages/gm/src/binds/gmGCRootUtil.cpp
Normal file
33
vscript/languages/gm/src/binds/gmGCRootUtil.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// gmGCRootUtil.cpp
|
||||
//
|
||||
|
||||
#include "gmGCRoot.h"
|
||||
#include "gmGCRootUtil.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
|
||||
gmgcrMemFixed::gmgcrMemFixed()
|
||||
: gmMemFixed( sizeof(gmgcrHolder) )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
gmgcrHolder* gmgcrMemFixed::Alloc()
|
||||
{
|
||||
gmgcrHolder* ptr = (gmgcrHolder*)gmMemFixed::Alloc();
|
||||
gmConstructElement<gmgcrHolder>(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void gmgcrMemFixed::Free(gmgcrHolder* a_ptr)
|
||||
{
|
||||
if( a_ptr )
|
||||
{
|
||||
gmDestructElement(a_ptr);
|
||||
gmMemFixed::Free(a_ptr);
|
||||
}
|
||||
}
|
||||
171
vscript/languages/gm/src/binds/gmGCRootUtil.h
Normal file
171
vscript/languages/gm/src/binds/gmGCRootUtil.h
Normal file
@@ -0,0 +1,171 @@
|
||||
#ifndef GMGCROOTUTIL_H
|
||||
#define GMGCROOTUTIL_H
|
||||
|
||||
//
|
||||
// gmGCRootUtil.h
|
||||
//
|
||||
|
||||
#include "gmThread.h"
|
||||
|
||||
//
|
||||
// This file has containers and utilities for implementing gmGCRoot
|
||||
// You may replace these implementations with your own containers with relative ease.
|
||||
//
|
||||
|
||||
// Fwd decls
|
||||
class gmGCRootManager;
|
||||
class gmgcrHolder;
|
||||
|
||||
|
||||
// STL version
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
template<typename KEY, typename VALUE>
|
||||
class gmgcrMap
|
||||
{
|
||||
typename std::map<KEY, VALUE> m_map;
|
||||
|
||||
public:
|
||||
|
||||
// Iterator, derive or contain native container iterator
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
typename std::map<KEY, VALUE>::iterator m_it;
|
||||
};
|
||||
|
||||
// Get Value at unique Key, return true if found
|
||||
int GetAt(KEY a_key, VALUE& a_value)
|
||||
{
|
||||
typename std::map<KEY, VALUE>::iterator it = m_map.find(a_key);
|
||||
if( it != m_map.end() )
|
||||
{
|
||||
a_value = (*it).second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Set Value and unique Key
|
||||
void SetAt(KEY a_key, VALUE& a_value)
|
||||
{
|
||||
m_map[a_key] = a_value;
|
||||
}
|
||||
|
||||
// Remove pair at unique Key
|
||||
void RemoveAt(KEY a_key)
|
||||
{
|
||||
m_map.erase(a_key);
|
||||
}
|
||||
|
||||
// Iteration Get first pair
|
||||
void GetFirst(Iterator& a_it)
|
||||
{
|
||||
a_it.m_it = m_map.begin();
|
||||
}
|
||||
|
||||
// Iteration Get next pair
|
||||
void GetNext(Iterator& a_it)
|
||||
{
|
||||
if( a_it.m_it != m_map.end() )
|
||||
{
|
||||
++a_it.m_it;
|
||||
}
|
||||
}
|
||||
|
||||
// Iteration Is iterator valid. Return false if not.
|
||||
int IsNull(Iterator& a_it)
|
||||
{
|
||||
if( a_it.m_it == m_map.end() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iteration Get Value from iterator, return false if failed
|
||||
int GetValue(Iterator& a_it, VALUE& a_value)
|
||||
{
|
||||
if( a_it.m_it != m_map.end() )
|
||||
{
|
||||
a_value = (*a_it.m_it).second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Array, derive or contain array style container
|
||||
template<typename TYPE>
|
||||
class gmgcrArray
|
||||
{
|
||||
typename std::vector<TYPE> m_array;
|
||||
|
||||
public:
|
||||
|
||||
// Set initial array size and any grow / presize for efficiency
|
||||
void Init(int a_initialSize, int a_growSize)
|
||||
{
|
||||
SetSize(a_initialSize);
|
||||
}
|
||||
|
||||
// Set array size, and grow size
|
||||
void SetSize(int a_size)
|
||||
{
|
||||
m_array.resize(a_size);
|
||||
}
|
||||
|
||||
// Return number of elements in array
|
||||
int GetSize()
|
||||
{
|
||||
return m_array.size();
|
||||
}
|
||||
|
||||
// Return last index eg. GetSize()-1
|
||||
int GetLastIndex()
|
||||
{
|
||||
return GetSize() - 1;
|
||||
}
|
||||
|
||||
// Remove element at index
|
||||
void RemoveAt(int a_index)
|
||||
{
|
||||
m_array.erase(m_array.begin() + a_index);
|
||||
}
|
||||
|
||||
// Access element at index (by reference)
|
||||
TYPE& operator[](int a_index)
|
||||
{
|
||||
return m_array[a_index];
|
||||
}
|
||||
|
||||
// Add (default) element to array, returning index of new element
|
||||
int AddEmpty()
|
||||
{
|
||||
int oldCount = GetSize(); // old count becomes new index
|
||||
SetSize(oldCount+1);
|
||||
return oldCount;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Allocator, derive or contain frame style allocator
|
||||
class gmgcrMemFixed : public gmMemFixed
|
||||
{
|
||||
public:
|
||||
|
||||
gmgcrMemFixed();
|
||||
|
||||
// Allocate and construct
|
||||
gmgcrHolder* Alloc();
|
||||
|
||||
// Destruct and free
|
||||
void Free(gmgcrHolder* a_ptr);
|
||||
|
||||
};
|
||||
|
||||
#endif //GMGCROOTUTIL_H
|
||||
64
vscript/languages/gm/src/binds/gmHelpers.cpp
Normal file
64
vscript/languages/gm/src/binds/gmHelpers.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmHelpers.h"
|
||||
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
/*!
|
||||
\brief gmRandomInt() returns a random int b/n two values
|
||||
\return number is >= min and < max (exclusive of max)
|
||||
*/
|
||||
int gmRandomInt(int a_min, int a_max)
|
||||
{
|
||||
if(a_min > a_max)
|
||||
{
|
||||
int temp = a_max;
|
||||
a_max = a_min;
|
||||
a_min = temp;
|
||||
}
|
||||
else if(a_min == a_max)
|
||||
{
|
||||
return a_min; // hmmm, not good.
|
||||
}
|
||||
|
||||
int randVal = rand();
|
||||
int val = (randVal % (a_max - a_min)) + a_min;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief gmRandomInt() returns a random int b/n two values
|
||||
\return number is >= min and < max (exclusive of max)
|
||||
*/
|
||||
float gmRandomFloat(float a_min, float a_max)
|
||||
{
|
||||
if(a_min > a_max)
|
||||
{
|
||||
float temp = a_max;
|
||||
a_max = a_min;
|
||||
a_min = temp;
|
||||
}
|
||||
else if(a_min == a_max)
|
||||
{
|
||||
return a_min; // hmmm, not good.
|
||||
}
|
||||
|
||||
int randVal = rand() % RAND_MAX;
|
||||
float frandVal = (float)randVal / (float)RAND_MAX;
|
||||
return a_min + (frandVal * (a_max - a_min));
|
||||
}
|
||||
179
vscript/languages/gm/src/binds/gmHelpers.h
Normal file
179
vscript/languages/gm/src/binds/gmHelpers.h
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMHELPERS_H_
|
||||
#define _GMHELPERS_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmThread.h"
|
||||
#include <math.h>
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
#define GM_PI_VALUE 3.1415927f
|
||||
|
||||
// Clamp value between to range
|
||||
template <class TYPE>
|
||||
GM_FORCEINLINE TYPE gmClamp(const TYPE a_min, const TYPE a_value, const TYPE a_max)
|
||||
{
|
||||
if(a_value < a_min)
|
||||
{
|
||||
return a_min;
|
||||
}
|
||||
else if(a_value > a_max)
|
||||
{
|
||||
return a_max;
|
||||
}
|
||||
return a_value;
|
||||
}
|
||||
|
||||
template <class TYPE>
|
||||
GM_FORCEINLINE TYPE gmMin3(const TYPE a_x, const TYPE a_y, const TYPE a_z)
|
||||
{
|
||||
if(a_y < a_x)
|
||||
{
|
||||
if(a_z < a_y)
|
||||
{
|
||||
return a_z;
|
||||
}
|
||||
else
|
||||
{
|
||||
return a_y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a_z < a_x)
|
||||
{
|
||||
return a_z;
|
||||
}
|
||||
else
|
||||
{
|
||||
return a_x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class TYPE>
|
||||
GM_FORCEINLINE TYPE gmMax3(const TYPE a_x, const TYPE a_y, const TYPE a_z)
|
||||
{
|
||||
if(a_y > a_x)
|
||||
{
|
||||
if(a_z > a_y)
|
||||
{
|
||||
return a_z;
|
||||
}
|
||||
else
|
||||
{
|
||||
return a_y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a_z > a_x)
|
||||
{
|
||||
return a_z;
|
||||
}
|
||||
else
|
||||
{
|
||||
return a_x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GM_FORCEINLINE float gmGetFloatOrIntParamAsFloat(gmThread * a_thread, int a_paramIndex)
|
||||
{
|
||||
if(a_thread->ParamType(a_paramIndex) == GM_INT)
|
||||
{
|
||||
return (float)a_thread->Param(a_paramIndex).m_value.m_int;
|
||||
}
|
||||
else
|
||||
{
|
||||
return a_thread->Param(a_paramIndex).m_value.m_float;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GM_FORCEINLINE bool gmGetFloatOrIntParamAsFloat(gmThread * a_thread, int a_paramIndex, float& a_retValue)
|
||||
{
|
||||
if(a_thread->ParamType(a_paramIndex) == GM_INT)
|
||||
{
|
||||
a_retValue = (float)a_thread->Param(a_paramIndex).m_value.m_int;
|
||||
return true;
|
||||
}
|
||||
else if(a_thread->ParamType(a_paramIndex) == GM_FLOAT)
|
||||
{
|
||||
a_retValue = a_thread->Param(a_paramIndex).m_value.m_float;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GM_FORCEINLINE int gmGetFloatOrIntParamAsInt(gmThread * a_thread, int a_paramIndex)
|
||||
{
|
||||
if(a_thread->ParamType(a_paramIndex) == GM_INT)
|
||||
{
|
||||
return a_thread->Param(a_paramIndex).m_value.m_int;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int)a_thread->Param(a_paramIndex).m_value.m_float;
|
||||
}
|
||||
}
|
||||
|
||||
GM_FORCEINLINE bool gmGetFloatOrIntParamAsInt(gmThread * a_thread, int a_paramIndex, int& a_retValue)
|
||||
{
|
||||
if(a_thread->ParamType(a_paramIndex) == GM_INT)
|
||||
{
|
||||
a_retValue = a_thread->Param(a_paramIndex).m_value.m_int;
|
||||
return true;
|
||||
}
|
||||
else if(a_thread->ParamType(a_paramIndex) == GM_FLOAT)
|
||||
{
|
||||
a_retValue = (int)a_thread->Param(a_paramIndex).m_value.m_float;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief gmRandomInt() returns a random int b/n two values
|
||||
Beware of overflow since ints are only 32bit on Intel
|
||||
\return number is >= min and < max (exclusive of max)
|
||||
*/
|
||||
int gmRandomInt(int a_min, int a_max);
|
||||
|
||||
/*!
|
||||
\brief gmRandomInt() returns a random int b/n two values
|
||||
Note this is a low precision random value since it is generated from an int.
|
||||
\return number is >= min and < max (exclusive of max)
|
||||
*/
|
||||
float gmRandomFloat(float a_min, float a_max);
|
||||
|
||||
/*!
|
||||
\brief Returns the sine and cosine of values
|
||||
Note should make platform specific version
|
||||
*/
|
||||
GM_FORCEINLINE void gmSinCos(const float a_angle, float& a_sin, float& a_cos)
|
||||
{
|
||||
a_sin = (float)sinf(a_angle);
|
||||
a_cos = (float)cosf(a_angle);
|
||||
}
|
||||
|
||||
#endif // _GMHELPERS_H_
|
||||
860
vscript/languages/gm/src/binds/gmMathLib.cpp
Normal file
860
vscript/languages/gm/src/binds/gmMathLib.cpp
Normal file
@@ -0,0 +1,860 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmMathLib.h"
|
||||
#include "gmThread.h"
|
||||
#include "gmMachine.h"
|
||||
#include "gmHelpers.h"
|
||||
#include "gmUtil.h"
|
||||
#include <math.h>
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Conversion
|
||||
//
|
||||
|
||||
int GM_CDECL gmfToString(gmThread * a_thread)
|
||||
{
|
||||
const gmVariable * var = a_thread->GetThis();
|
||||
|
||||
if(GM_INT == var->m_type)
|
||||
{
|
||||
char numberAsStringBuffer[64];
|
||||
sprintf(numberAsStringBuffer, "%d", var->m_value.m_int); // this won't be > 64 chars
|
||||
a_thread->PushNewString(numberAsStringBuffer);
|
||||
}
|
||||
else if (GM_FLOAT == var->m_type)
|
||||
{
|
||||
char numberAsStringBuffer[64];
|
||||
|
||||
sprintf(numberAsStringBuffer, "%f", var->m_value.m_float); // this won't be > 64 chars
|
||||
|
||||
a_thread->PushNewString(numberAsStringBuffer);
|
||||
}
|
||||
else if (GM_STRING == var->m_type)
|
||||
{
|
||||
a_thread->PushString( (gmStringObject *) GM_OBJECT(var->m_value.m_ref) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int GM_CDECL gmfToFloat(gmThread * a_thread)
|
||||
{
|
||||
const gmVariable * var = a_thread->GetThis();
|
||||
|
||||
if(GM_INT == var->m_type)
|
||||
{
|
||||
a_thread->PushFloat((float)var->m_value.m_int);
|
||||
}
|
||||
else if (GM_FLOAT == var->m_type)
|
||||
{
|
||||
a_thread->PushFloat(var->m_value.m_float);
|
||||
}
|
||||
else if (GM_STRING == var->m_type)
|
||||
{
|
||||
gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
|
||||
const char * cstr = * strObj;
|
||||
|
||||
a_thread->PushFloat( (float)atof(cstr) );
|
||||
}
|
||||
else
|
||||
{
|
||||
//a_thread->PushFloat( 0.0f );
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int GM_CDECL gmfToInt(gmThread * a_thread)
|
||||
{
|
||||
const gmVariable * var = a_thread->GetThis();
|
||||
|
||||
if(GM_INT == var->m_type)
|
||||
{
|
||||
a_thread->PushInt(var->m_value.m_int);
|
||||
}
|
||||
else if (GM_FLOAT == var->m_type)
|
||||
{
|
||||
a_thread->PushInt( (int)var->m_value.m_float);
|
||||
}
|
||||
else if (GM_STRING == var->m_type)
|
||||
{
|
||||
gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
|
||||
const char * cstr = * strObj;
|
||||
|
||||
a_thread->PushInt( atoi(cstr) );
|
||||
}
|
||||
else
|
||||
{
|
||||
//a_thread->PushInt( 0 );
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Math
|
||||
//
|
||||
|
||||
|
||||
|
||||
// int randint(int a_min, int a_max);
|
||||
// returned number is >= a_min and < a_max (exclusive of max)
|
||||
static int GM_CDECL gmfRandInt(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
GM_CHECK_INT_PARAM(min, 0);
|
||||
GM_CHECK_INT_PARAM(max, 1);
|
||||
|
||||
a_thread->PushInt( gmRandomInt(min, max) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// float randfloat(float a_min, float a_max);
|
||||
// returned number is >= a_min and < a_max (exclusive of max)
|
||||
static int GM_CDECL gmfRandFloat(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
GM_CHECK_FLOAT_PARAM(min, 0);
|
||||
GM_CHECK_FLOAT_PARAM(max, 1);
|
||||
|
||||
a_thread->PushFloat( gmRandomFloat(min, max) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// void randseed(int a_seed);
|
||||
static int GM_CDECL gmfRandSeed(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_INT_PARAM(seed, 0);
|
||||
|
||||
srand(seed);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfAbs(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT)
|
||||
{
|
||||
int intValue = a_thread->Param(0).m_value.m_int;
|
||||
a_thread->PushInt(abs(intValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT)
|
||||
{
|
||||
float floatValue = a_thread->Param(0).m_value.m_float;
|
||||
a_thread->PushFloat((float)fabsf(floatValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfSqrt(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT)
|
||||
{
|
||||
int intValue = a_thread->Param(0).m_value.m_int;
|
||||
a_thread->PushInt((int)sqrtf((float)intValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT)
|
||||
{
|
||||
float floatValue = a_thread->Param(0).m_value.m_float;
|
||||
a_thread->PushFloat(sqrtf(floatValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfPower(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
|
||||
int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
|
||||
if(minType < GM_INT)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
|
||||
|
||||
if(maxType == GM_INT)
|
||||
{
|
||||
int valX = a_thread->Param(0).m_value.m_int;
|
||||
int valY = a_thread->Param(1).m_value.m_int;
|
||||
a_thread->PushInt((int)pow((float)valX, (float)valY));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else if(maxType == GM_FLOAT)
|
||||
{
|
||||
float valX = gmGetFloatOrIntParamAsFloat(a_thread, 0);
|
||||
float valY = gmGetFloatOrIntParamAsFloat(a_thread, 1);
|
||||
a_thread->PushFloat((float)pow(valX, valY));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfFloor(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) //Do nothing if Int
|
||||
{
|
||||
int intValue = a_thread->Param(0).m_value.m_int;
|
||||
a_thread->PushInt(intValue);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT)
|
||||
{
|
||||
float floatValue = a_thread->Param(0).m_value.m_float;
|
||||
a_thread->PushFloat(floorf(floatValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfCeil(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) //Do nothing if Int
|
||||
{
|
||||
int intValue = a_thread->Param(0).m_value.m_int;
|
||||
a_thread->PushInt(intValue);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT)
|
||||
{
|
||||
float floatValue = a_thread->Param(0).m_value.m_float;
|
||||
a_thread->PushFloat(ceilf(floatValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfRound(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) //Do nothing if Int
|
||||
{
|
||||
int intValue = a_thread->Param(0).m_value.m_int;
|
||||
a_thread->PushInt(intValue);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT)
|
||||
{
|
||||
float floatValue = a_thread->Param(0).m_value.m_float;
|
||||
a_thread->PushFloat(floorf(floatValue + 0.5f));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfDegToRad(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
float floatValue;
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
|
||||
else { return GM_EXCEPTION; }
|
||||
|
||||
a_thread->PushFloat( floatValue * (GM_PI_VALUE / 180.0f) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfRadToDeg(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
float floatValue;
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
|
||||
else { return GM_EXCEPTION; }
|
||||
|
||||
a_thread->PushFloat( floatValue * (180.0f / GM_PI_VALUE) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfSin(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
float floatValue;
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
|
||||
else { return GM_EXCEPTION; }
|
||||
|
||||
a_thread->PushFloat(sinf(floatValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfASin(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
float floatValue;
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
|
||||
else { return GM_EXCEPTION; }
|
||||
|
||||
a_thread->PushFloat(asinf(floatValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfCos(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
float floatValue;
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
|
||||
else { return GM_EXCEPTION; }
|
||||
|
||||
a_thread->PushFloat(cosf(floatValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfACos(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
float floatValue;
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
|
||||
else { return GM_EXCEPTION; }
|
||||
|
||||
a_thread->PushFloat(acosf(floatValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfTan(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
float floatValue;
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
|
||||
else { return GM_EXCEPTION; }
|
||||
|
||||
a_thread->PushFloat(tanf(floatValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfATan(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
float floatValue;
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
|
||||
else { return GM_EXCEPTION; }
|
||||
|
||||
a_thread->PushFloat(atanf(floatValue));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//returns the arctangent of y/x
|
||||
static int GM_CDECL gmfATan2(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
|
||||
float floatValueY;
|
||||
float floatValueX;
|
||||
|
||||
if(a_thread->ParamType(0) == GM_INT) {floatValueY = (float) a_thread->Param(0).m_value.m_int;}
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT) {floatValueY = a_thread->Param(0).m_value.m_float;}
|
||||
else {return GM_EXCEPTION;}
|
||||
|
||||
if(a_thread->ParamType(1) == GM_INT) {floatValueX = (float) a_thread->Param(1).m_value.m_int;}
|
||||
else if(a_thread->ParamType(1) == GM_FLOAT) {floatValueX = a_thread->Param(1).m_value.m_float;}
|
||||
else {return GM_EXCEPTION;}
|
||||
|
||||
a_thread->PushFloat(atan2f(floatValueY, floatValueX));
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfLog(gmThread * a_thread)
|
||||
{
|
||||
int numParams = GM_THREAD_ARG->GetNumParams();
|
||||
|
||||
if(numParams == 1) //Natural log
|
||||
{
|
||||
if(a_thread->ParamType(0) == GM_INT)
|
||||
{
|
||||
float floatValue = (float) a_thread->Param(0).m_value.m_int;
|
||||
a_thread->PushInt( (int) log(floatValue) );
|
||||
return GM_OK;
|
||||
}
|
||||
else if(a_thread->ParamType(0) == GM_FLOAT)
|
||||
{
|
||||
float floatValue = (float) a_thread->Param(0).m_value.m_float;
|
||||
|
||||
a_thread->PushFloat( logf(floatValue) );
|
||||
return GM_OK;
|
||||
}
|
||||
else {return GM_EXCEPTION;}
|
||||
}
|
||||
else if(numParams == 2) //Log to base params: base, value
|
||||
{
|
||||
int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
|
||||
if(minType < GM_INT)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
|
||||
|
||||
if(maxType == GM_INT)
|
||||
{
|
||||
int base = a_thread->Param(0).m_value.m_int;
|
||||
int value = a_thread->Param(1).m_value.m_int;
|
||||
|
||||
a_thread->PushInt( (int)( log10f((float)value) / log10f((float)base) ) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else if(maxType == GM_FLOAT)
|
||||
{
|
||||
float base = gmGetFloatOrIntParamAsFloat(a_thread, 0);
|
||||
float value = gmGetFloatOrIntParamAsFloat(a_thread, 1);
|
||||
|
||||
a_thread->PushFloat( (float)( log10(value) / log10(base) ) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfMin(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
|
||||
int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
|
||||
if(minType < GM_INT)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
|
||||
|
||||
if(maxType == GM_INT)
|
||||
{
|
||||
int valX = a_thread->Param(0).m_value.m_int;
|
||||
int valY = a_thread->Param(1).m_value.m_int;
|
||||
a_thread->PushInt( gmMin(valX, valY) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else if(maxType == GM_FLOAT)
|
||||
{
|
||||
float valX = gmGetFloatOrIntParamAsFloat(a_thread, 0);
|
||||
float valY = gmGetFloatOrIntParamAsFloat(a_thread, 1);
|
||||
a_thread->PushFloat( gmMin(valX, valY) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfMax(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
|
||||
int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
|
||||
if(minType < GM_INT)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
|
||||
|
||||
if(maxType == GM_INT)
|
||||
{
|
||||
int valX = a_thread->Param(0).m_value.m_int;
|
||||
int valY = a_thread->Param(1).m_value.m_int;
|
||||
a_thread->PushInt( gmMax(valX, valY) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else if(maxType == GM_FLOAT)
|
||||
{
|
||||
float valX = gmGetFloatOrIntParamAsFloat(a_thread, 0);
|
||||
float valY = gmGetFloatOrIntParamAsFloat(a_thread, 1);
|
||||
a_thread->PushFloat( gmMax(valX, valY) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GM_CDECL gmfClamp(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(3);
|
||||
|
||||
//params: min, value, max
|
||||
|
||||
int minType = gmMin3(a_thread->ParamType(0), a_thread->ParamType(1), a_thread->ParamType(2));
|
||||
if(minType < GM_INT)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
int maxType = gmMax3(a_thread->ParamType(0), a_thread->ParamType(1), a_thread->ParamType(2));
|
||||
|
||||
if(maxType == GM_INT)
|
||||
{
|
||||
int limitMin = a_thread->Param(0).m_value.m_int;
|
||||
int value = a_thread->Param(1).m_value.m_int;
|
||||
int limitMax = a_thread->Param(2).m_value.m_int;
|
||||
|
||||
a_thread->PushInt( gmClamp(limitMin, value, limitMax) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else if(maxType == GM_FLOAT)
|
||||
{
|
||||
float limitMin = gmGetFloatOrIntParamAsFloat(a_thread, 0);
|
||||
float value = gmGetFloatOrIntParamAsFloat(a_thread, 1);
|
||||
float limitMax = gmGetFloatOrIntParamAsFloat(a_thread, 2);
|
||||
|
||||
a_thread->PushFloat( gmClamp(limitMin, value, limitMax) );
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Libs and bindings
|
||||
//
|
||||
|
||||
|
||||
static gmFunctionEntry s_mathLib[] =
|
||||
{
|
||||
/*gm
|
||||
\lib math
|
||||
*/
|
||||
/*gm
|
||||
\function abs
|
||||
\brief abs will return the absolute value of the passed int \ float
|
||||
\param int\float
|
||||
\return int\float abs(param)
|
||||
*/
|
||||
{"abs", gmfAbs},
|
||||
/*gm
|
||||
\function sqrt
|
||||
\brief sqrt will return the square root of the passed int \ float
|
||||
\param int\float
|
||||
\return int\float sqrt(param)
|
||||
*/
|
||||
{"sqrt", gmfSqrt},
|
||||
/*gm
|
||||
\function sqrt
|
||||
\brief sqrt will return the square root of the passed int \ float
|
||||
\param int\float A
|
||||
\param int\float B
|
||||
\return int\float A to the power of B
|
||||
*/
|
||||
{"power", gmfPower},
|
||||
/*gm
|
||||
\function floor
|
||||
\brief floor
|
||||
\param float A
|
||||
\return float floor(A)
|
||||
*/
|
||||
{"floor", gmfFloor},
|
||||
/*gm
|
||||
\function ceil
|
||||
\brief ceil
|
||||
\param float A
|
||||
\return float ceil(A)
|
||||
*/
|
||||
{"ceil", gmfCeil},
|
||||
/*gm
|
||||
\function round
|
||||
\brief round
|
||||
\param float A
|
||||
\return float round(A)
|
||||
*/
|
||||
{"round", gmfRound},
|
||||
/*gm
|
||||
\function degtorad
|
||||
\brief degtorad will convert degrees to radians
|
||||
\param float\int deg
|
||||
\return float
|
||||
*/
|
||||
{"degtorad", gmfDegToRad},
|
||||
/*gm
|
||||
\function radtodeg
|
||||
\brief radtodeg will convert radians to degrees
|
||||
\param float\int rad
|
||||
\return float
|
||||
*/
|
||||
{"radtodeg", gmfRadToDeg},
|
||||
/*gm
|
||||
\function cos
|
||||
\brief cos will return the radian cosine
|
||||
\param float
|
||||
\return float
|
||||
*/
|
||||
{"cos", gmfCos},
|
||||
/*gm
|
||||
\function sin
|
||||
\brief sin will return the radian sine
|
||||
\param float
|
||||
\return float
|
||||
*/
|
||||
{"sin", gmfSin},
|
||||
/*gm
|
||||
\function tan
|
||||
\brief tan will return the radian tan (sin/cos)
|
||||
\param float
|
||||
\return float
|
||||
*/
|
||||
{"tan", gmfTan},
|
||||
/*gm
|
||||
\function acos
|
||||
\brief acos will return the radian arc cosine
|
||||
\param float
|
||||
\return float
|
||||
*/
|
||||
{"acos", gmfACos},
|
||||
/*gm
|
||||
\function asin
|
||||
\brief asin will return the radian arc sine
|
||||
\param float
|
||||
\return float
|
||||
*/
|
||||
{"asin", gmfASin},
|
||||
/*gm
|
||||
\function atan
|
||||
\brief atan will return the radian arc tangent
|
||||
\param float
|
||||
\return float
|
||||
*/
|
||||
{"atan", gmfATan},
|
||||
/*gm
|
||||
\function atan
|
||||
\brief atan will return the radian arc tangent of x / y
|
||||
\param float x
|
||||
\param float y
|
||||
\return float
|
||||
*/
|
||||
{"atan2", gmfATan2},
|
||||
/*gm
|
||||
\function log
|
||||
\brief log will return the natural logarithm of 1 parameter, or (base, value) the logarithm to base
|
||||
\param float natural \ base
|
||||
\param float value (optional)
|
||||
\return float
|
||||
*/
|
||||
{"log", gmfLog},
|
||||
/*gm
|
||||
\function min
|
||||
\brief min will return the min of the 2 passed values
|
||||
\param float\int A
|
||||
\param float\int B
|
||||
\return float \ int min(A, B)
|
||||
*/
|
||||
{"min", gmfMin},
|
||||
/*gm
|
||||
\function max
|
||||
\brief max will return the max of the 2 passed values
|
||||
\param float\int A
|
||||
\param float\int B
|
||||
\return float \ int max(A, B)
|
||||
*/
|
||||
{"max", gmfMax},
|
||||
/*gm
|
||||
\function clamp
|
||||
\brief clamp will return the clamed value. clamp(min, val, max)
|
||||
\param float\int MIN
|
||||
\param float\int VALUE
|
||||
\param float\int MAX
|
||||
\return float\int value clamped to min, max
|
||||
*/
|
||||
{"clamp", gmfClamp},
|
||||
/*gm
|
||||
\function randint
|
||||
\brief randint will return a random int from lower inclusive to upper.
|
||||
\param int lower inclusive
|
||||
\param int upper
|
||||
\return int
|
||||
*/
|
||||
#if 0 // Using Source versions [2/13/2008 tom]
|
||||
{"randint", gmfRandInt},
|
||||
/*gm
|
||||
\function randfloat
|
||||
\brief randfloat will return a random float from lower inclusive to upper.
|
||||
\param float lower inclusive
|
||||
\param float upper
|
||||
\return float
|
||||
*/
|
||||
{"randfloat", gmfRandFloat},
|
||||
/*gm
|
||||
\function randseed
|
||||
\brief randseed will seed the random number generator
|
||||
\param int seed
|
||||
*/
|
||||
{"randseed", gmfRandSeed},
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
static gmFunctionEntry s_intLib[] =
|
||||
{
|
||||
{"String", gmfToString},
|
||||
{"Float", gmfToFloat},
|
||||
{"Int", gmfToInt},
|
||||
};
|
||||
|
||||
|
||||
|
||||
static gmFunctionEntry s_floatLib[] =
|
||||
{
|
||||
{"String", gmfToString},
|
||||
{"Float", gmfToFloat},
|
||||
{"Int", gmfToInt},
|
||||
};
|
||||
|
||||
|
||||
void gmBindMathLib(gmMachine * a_machine)
|
||||
{
|
||||
a_machine->RegisterLibrary(s_mathLib, sizeof(s_mathLib) / sizeof(s_mathLib[0]));
|
||||
a_machine->RegisterTypeLibrary(GM_INT, s_intLib, sizeof(s_intLib) / sizeof(s_intLib[0]));
|
||||
a_machine->RegisterTypeLibrary(GM_FLOAT, s_floatLib, sizeof(s_floatLib) / sizeof(s_floatLib[0]));
|
||||
}
|
||||
|
||||
21
vscript/languages/gm/src/binds/gmMathLib.h
Normal file
21
vscript/languages/gm/src/binds/gmMathLib.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMMATHLIB_H_
|
||||
#define _GMMATHLIB_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
|
||||
class gmMachine;
|
||||
|
||||
void gmBindMathLib(gmMachine * a_machine);
|
||||
|
||||
#endif // _GMMATHLIB_H_
|
||||
1174
vscript/languages/gm/src/binds/gmStringLib.cpp
Normal file
1174
vscript/languages/gm/src/binds/gmStringLib.cpp
Normal file
File diff suppressed because it is too large
Load Diff
21
vscript/languages/gm/src/binds/gmStringLib.h
Normal file
21
vscript/languages/gm/src/binds/gmStringLib.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMSTRINGLIB_H_
|
||||
#define _GMSTRINGLIB_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
|
||||
class gmMachine;
|
||||
|
||||
void gmBindStringLib(gmMachine * a_machine);
|
||||
|
||||
#endif // _GMSTRINGLIB_H_
|
||||
986
vscript/languages/gm/src/binds/gmSystemLib.cpp
Normal file
986
vscript/languages/gm/src/binds/gmSystemLib.cpp
Normal file
@@ -0,0 +1,986 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmSystemLib.h"
|
||||
#include "gmThread.h"
|
||||
#include "gmMachine.h"
|
||||
#include "gmHelpers.h"
|
||||
|
||||
#if GM_SYSTEM_LIB
|
||||
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <process.h>
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
||||
#undef GetObject
|
||||
|
||||
//
|
||||
//
|
||||
// system functions
|
||||
//
|
||||
//
|
||||
|
||||
extern void gmConcat(gmMachine * a_machine, char * &a_dst, int &a_len, int &a_size, const char * a_src, int a_growBy = 32);
|
||||
|
||||
|
||||
static int GM_CDECL gmfSystem(gmThread * a_thread)
|
||||
{
|
||||
const int bufferSize = 256;
|
||||
int len = 0, size = 0, i, ret = -1;
|
||||
char * str = NULL, buffer[bufferSize];
|
||||
|
||||
// build the string
|
||||
for(i = 0; i < a_thread->GetNumParams(); ++i)
|
||||
{
|
||||
gmConcat(a_thread->GetMachine(), str, len, size, a_thread->Param(i).AsString(a_thread->GetMachine(), buffer, bufferSize), 64);
|
||||
|
||||
if(str)
|
||||
{
|
||||
GM_ASSERT(len < size);
|
||||
str[len++] = ' ';
|
||||
str[len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
// print the string
|
||||
if(str)
|
||||
{
|
||||
ret = system(str);
|
||||
a_thread->GetMachine()->Sys_Free(str);
|
||||
}
|
||||
|
||||
a_thread->PushInt(ret);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfDoFile(gmThread * a_thread) // filename, now (1), return thread id, null on error, exception on compile error.
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_STRING_PARAM(filename, 0);
|
||||
GM_INT_PARAM(now, 1, 1);
|
||||
gmVariable paramThis = a_thread->Param(2, gmVariable::s_null); // 3rd param is 'this'
|
||||
|
||||
int id = GM_INVALID_THREAD;
|
||||
if(filename)
|
||||
{
|
||||
char * string = NULL;
|
||||
|
||||
FILE * fp = fopen(filename, "rb");
|
||||
if(fp)
|
||||
{
|
||||
fseek(fp, 0, SEEK_END);
|
||||
int size = ftell(fp);
|
||||
rewind(fp);
|
||||
string = new char[size + 1];
|
||||
fread(string, 1, size, fp);
|
||||
string[size] = 0;
|
||||
fclose(fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
GM_EXCEPTION_MSG("failed to open file '%s'", filename);
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
if(string == NULL) return GM_OK;
|
||||
|
||||
int errors = a_thread->GetMachine()->ExecuteString(string, &id, (now) ? true : false, filename, ¶mThis);
|
||||
delete[] string;
|
||||
if(errors)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
a_thread->PushInt(id);
|
||||
}
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Implementation of ansi file binding
|
||||
//
|
||||
//
|
||||
|
||||
static gmType s_gmFileType = GM_NULL;
|
||||
|
||||
static int GM_CDECL gmfFile(gmThread * a_thread)
|
||||
{
|
||||
a_thread->PushNewUser(NULL, s_gmFileType);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfFileExists(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_STRING_PARAM(filename, 0);
|
||||
|
||||
FILE * fp = fopen(filename, "rb");
|
||||
if(fp)
|
||||
{
|
||||
a_thread->PushInt(1);
|
||||
fclose(fp);
|
||||
return GM_OK;
|
||||
}
|
||||
a_thread->PushInt(0);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfFileOpen(gmThread * a_thread) // path, readonly(true), return 1 on success.
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_STRING_PARAM(filename, 0);
|
||||
GM_INT_PARAM(readonly, 1, 1);
|
||||
|
||||
gmUserObject * fileObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(fileObject->m_userType == s_gmFileType);
|
||||
if(fileObject->m_user) fclose((FILE *) fileObject->m_user);
|
||||
fileObject->m_user = (void *) fopen(filename, (readonly) ? "rb" : "wb");
|
||||
if(fileObject->m_user) a_thread->PushInt(1);
|
||||
else a_thread->PushInt(0);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfFileOpenText(gmThread * a_thread) // path, readonly(true), return 1 on success.
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_STRING_PARAM(filename, 0);
|
||||
GM_INT_PARAM(readonly, 1, 1);
|
||||
|
||||
gmUserObject * fileObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(fileObject->m_userType == s_gmFileType);
|
||||
if(fileObject->m_user) fclose((FILE *) fileObject->m_user);
|
||||
fileObject->m_user = (void *) fopen(filename, (readonly) ? "r" : "w");
|
||||
if(fileObject->m_user) a_thread->PushInt(1);
|
||||
else a_thread->PushInt(0);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfFileClose(gmThread * a_thread)
|
||||
{
|
||||
gmUserObject * fileObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(fileObject->m_userType == s_gmFileType);
|
||||
if(fileObject->m_user) fclose((FILE *) fileObject->m_user);
|
||||
fileObject->m_user = NULL;
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfFileIsOpen(gmThread * a_thread) // return 1 if open, else 0
|
||||
{
|
||||
gmUserObject * fileObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(fileObject->m_userType == s_gmFileType);
|
||||
a_thread->PushInt((fileObject->m_user) ? 1 : 0);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
static void GM_CDECL gmFileOpGetDot(gmThread * a_thread, gmVariable * a_operands)
|
||||
{
|
||||
gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
|
||||
if(user && user->m_user)
|
||||
{
|
||||
gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref);
|
||||
|
||||
GM_ASSERT(sizeof(gmptr) == sizeof(time_t));
|
||||
|
||||
if(strcmp(member->GetString(), "SEEK_CUR") == 0)
|
||||
a_operands->SetInt(SEEK_CUR);
|
||||
else if(strcmp(member->GetString(), "SEEK_END") == 0)
|
||||
a_operands->SetInt(SEEK_END);
|
||||
else if(strcmp(member->GetString(), "SEEK_SET") == 0)
|
||||
a_operands->SetInt(SEEK_SET);
|
||||
else
|
||||
{
|
||||
a_operands->Nullify();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
a_operands->Nullify();
|
||||
}
|
||||
|
||||
|
||||
static int GM_CDECL gmfFileSeek(gmThread * a_thread) // return false on error
|
||||
{
|
||||
gmUserObject * fileObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(fileObject->m_userType == s_gmFileType);
|
||||
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
GM_CHECK_INT_PARAM(offset, 0);
|
||||
GM_CHECK_INT_PARAM(origin, 1);
|
||||
|
||||
if( origin != SEEK_CUR
|
||||
&& origin != SEEK_END
|
||||
&& origin != SEEK_SET )
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
int result = fseek((FILE*)fileObject->m_user, offset, origin);
|
||||
if(result != 0)
|
||||
{
|
||||
a_thread->PushInt(false);
|
||||
}
|
||||
a_thread->PushInt(true);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfFileTell(gmThread * a_thread) // return -1 on error, else file pos.
|
||||
{
|
||||
gmUserObject * fileObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(fileObject->m_userType == s_gmFileType);
|
||||
long pos = -1;
|
||||
if(fileObject->m_user) pos = ftell((FILE *) fileObject->m_user);
|
||||
a_thread->PushInt(pos);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfFileReadLine(gmThread * a_thread) // flag keep \n (0), return string, or null on eof
|
||||
{
|
||||
GM_INT_PARAM(keepLF, 0, 0);
|
||||
const int len = GM_SYSTEM_LIB_MAX_LINE;
|
||||
char buffer[len];
|
||||
gmUserObject * fileObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(fileObject->m_userType == s_gmFileType);
|
||||
if(fileObject->m_user)
|
||||
{
|
||||
char * str = fgets(buffer, len, (FILE *) fileObject->m_user);
|
||||
if(str)
|
||||
{
|
||||
int slen = strlen(str);
|
||||
if(!keepLF)
|
||||
{
|
||||
if(!feof((FILE *) fileObject->m_user))
|
||||
str[--slen] = '\0';
|
||||
}
|
||||
a_thread->PushNewString(str, slen);
|
||||
}
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfFileReadChar(gmThread * a_thread) // return int, return NULL on eof, or on error
|
||||
{
|
||||
gmUserObject * fileObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(fileObject->m_userType == s_gmFileType);
|
||||
if(fileObject->m_user)
|
||||
{
|
||||
int c = fgetc((FILE*) fileObject->m_user);
|
||||
if(c != EOF) a_thread->PushInt(c);
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfFileWriteChar(gmThread * a_thread) // int, return char written, or NULL on error
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_INT_PARAM(c, 0);
|
||||
|
||||
gmUserObject * fileObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(fileObject->m_userType == s_gmFileType);
|
||||
if(fileObject->m_user)
|
||||
{
|
||||
int r = fputc(c, (FILE *) fileObject->m_user);
|
||||
if(r != EOF) a_thread->PushInt(r);
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
static int GM_CDECL gmfFileWriteString(gmThread * a_thread) // string, return 1 on success, or NULL on error
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_STRING_PARAM(s, 0);
|
||||
|
||||
gmUserObject * fileObject = a_thread->ThisUserObject();
|
||||
GM_ASSERT(fileObject->m_userType == s_gmFileType);
|
||||
if(fileObject->m_user)
|
||||
{
|
||||
if(fputs(s, (FILE *) fileObject->m_user) != EOF) a_thread->PushInt(1);
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
#if GM_USE_INCGC
|
||||
static void GM_CDECL gmGCDestructFileUserType(gmMachine * a_machine, gmUserObject* a_object)
|
||||
{
|
||||
if(a_object->m_user) fclose((FILE *) a_object->m_user);
|
||||
a_object->m_user = NULL;
|
||||
}
|
||||
#else //GM_USE_INCGC
|
||||
static void GM_CDECL gmGCFileUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
|
||||
{
|
||||
if(a_object->m_user) fclose((FILE *) a_object->m_user);
|
||||
a_object->m_user = NULL;
|
||||
}
|
||||
#endif //GM_USE_INCGC
|
||||
|
||||
//
|
||||
//
|
||||
// File Find user type
|
||||
//
|
||||
//
|
||||
|
||||
static gmType s_gmFileFindType = GM_NULL;
|
||||
|
||||
struct gmFileFindUser
|
||||
{
|
||||
WIN32_FIND_DATA m_findData;
|
||||
HANDLE m_iterator;
|
||||
};
|
||||
|
||||
//
|
||||
//
|
||||
// File Info user type
|
||||
//
|
||||
//
|
||||
|
||||
static gmType s_gmFileInfoType = GM_NULL;
|
||||
|
||||
struct gmFileInfoUser
|
||||
{
|
||||
time_t m_accessedTime; // last access time
|
||||
time_t m_creationTime; // creation time
|
||||
time_t m_modifiedTime; // last modify time
|
||||
unsigned int m_size; // size
|
||||
};
|
||||
|
||||
//
|
||||
//
|
||||
// System lib
|
||||
//
|
||||
//
|
||||
|
||||
static int GM_CDECL gmfFindFirstFile(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_STRING_PARAM(filename, 0);
|
||||
|
||||
gmFileFindUser * fileFind = (gmFileFindUser *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmFileFindUser));
|
||||
fileFind->m_iterator = FindFirstFile(filename, &fileFind->m_findData);
|
||||
|
||||
if(fileFind->m_iterator == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
a_thread->GetMachine()->Sys_Free(fileFind);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
a_thread->PushNewUser(fileFind, s_gmFileFindType);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
static int GM_CDECL gmfFindNextFile(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
|
||||
if(a_thread->ParamType(0) == s_gmFileFindType)
|
||||
{
|
||||
gmFileFindUser * fileFind = (gmFileFindUser *) a_thread->ParamUser(0);
|
||||
if(fileFind && fileFind->m_iterator != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if(FindNextFile(fileFind->m_iterator, &fileFind->m_findData))
|
||||
{
|
||||
a_thread->PushUser(a_thread->ParamUserObject(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
static int GM_CDECL gmfFileInfo(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_STRING_PARAM(filename, 0);
|
||||
|
||||
struct _stat buf;
|
||||
int fh, result;
|
||||
|
||||
if((fh = _open(filename, _O_RDONLY)) == -1) return GM_OK; // return null
|
||||
result = _fstat(fh, &buf); // Get data associated with "fh"
|
||||
if(result == 0) //function obtained data correctly (0 == success, -1 == fail)
|
||||
{
|
||||
// create and push a gmFileInfoUser object
|
||||
gmFileInfoUser * fileInfo = (gmFileInfoUser *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmFileInfoUser));
|
||||
fileInfo->m_creationTime = buf.st_ctime;
|
||||
fileInfo->m_accessedTime = buf.st_atime;
|
||||
fileInfo->m_modifiedTime = buf.st_mtime;
|
||||
fileInfo->m_size = buf.st_size;
|
||||
a_thread->PushNewUser(fileInfo, s_gmFileInfoType);
|
||||
}
|
||||
_close( fh );
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
static int GM_CDECL gmfCreateFolder(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_STRING_PARAM(path, 0);
|
||||
BOOL result = CreateDirectory(path, NULL);
|
||||
if(result)
|
||||
{
|
||||
a_thread->PushInt(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
WIN32_FIND_DATA findData;
|
||||
HANDLE handle = FindFirstFile(path, &findData);
|
||||
if(handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
a_thread->PushInt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
a_thread->PushInt(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_thread->PushInt(0);
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
bool RecurseDeletePath(const char * a_path)
|
||||
{
|
||||
WIN32_FIND_DATA findData;
|
||||
|
||||
char path[MAX_PATH] = "";
|
||||
strcpy(path,a_path);
|
||||
|
||||
// remove trailing '\' char
|
||||
int last = strlen(path) - 1;
|
||||
if(path[last] == '\\')
|
||||
{
|
||||
path[last] = '\0';
|
||||
}
|
||||
|
||||
// is path valid
|
||||
HANDLE h = FindFirstFile(path,&findData);
|
||||
|
||||
// path not could not be found OR path is a file, not a folder
|
||||
if((h == INVALID_HANDLE_VALUE) || (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FindClose(h);
|
||||
h = NULL;
|
||||
|
||||
// push current working directory
|
||||
char currDir[MAX_PATH + 1] = "";
|
||||
GetCurrentDirectory(MAX_PATH,currDir);
|
||||
SetCurrentDirectory(path);
|
||||
|
||||
// iterate over contents of folder
|
||||
h = FindFirstFile("*",&findData);
|
||||
|
||||
if(h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(strcmp(findData.cFileName,".") != 0 && strcmp(findData.cFileName,"..") != 0)
|
||||
{
|
||||
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
RecurseDeletePath(findData.cFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD attrs = GetFileAttributes(findData.cFileName);
|
||||
if(attrs & FILE_ATTRIBUTE_READONLY)
|
||||
{
|
||||
SetFileAttributes(findData.cFileName,attrs ^ FILE_ATTRIBUTE_READONLY);
|
||||
}
|
||||
if(!DeleteFile(findData.cFileName))
|
||||
{
|
||||
DWORD res = GetLastError();
|
||||
printf("\nDeleteFile() returned '%d'..\n",(int)res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!FindNextFile(h,&findData)) break;
|
||||
}
|
||||
}
|
||||
|
||||
// pop current working directory
|
||||
SetCurrentDirectory(currDir);
|
||||
|
||||
FindClose(h);
|
||||
h = NULL;
|
||||
|
||||
// remove this directory
|
||||
DWORD attrs = GetFileAttributes(path);
|
||||
if(attrs & FILE_ATTRIBUTE_READONLY)
|
||||
{
|
||||
SetFileAttributes(path,attrs ^ FILE_ATTRIBUTE_READONLY);
|
||||
}
|
||||
return RemoveDirectory(path) != 0;
|
||||
}
|
||||
|
||||
|
||||
static int GM_CDECL gmfDeleteFolder(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_STRING_PARAM(path, 0);
|
||||
GM_INT_PARAM(removeSubFolders, 1, 0);
|
||||
|
||||
if(removeSubFolders)
|
||||
{
|
||||
a_thread->PushInt(RecurseDeletePath(path) ? 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_thread->PushInt(RemoveDirectory(path) ? 1 : 0);
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
static int GM_CDECL gmfTime(gmThread * a_thread)
|
||||
{
|
||||
time_t t;
|
||||
time(&t);
|
||||
GM_ASSERT(sizeof(time_t) == sizeof(gmptr));
|
||||
a_thread->PushInt((gmptr) t);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
static int GM_CDECL gmfFormatTime(gmThread * a_thread)
|
||||
{
|
||||
GM_INT_PARAM(t, 0, -1);
|
||||
GM_STRING_PARAM(format, 1, "%A %d %B %Y, %I:%M:%S %p");
|
||||
char buffer[256];
|
||||
|
||||
if(t == -1)
|
||||
{
|
||||
time_t lt;
|
||||
time(<);
|
||||
t = (int) lt;
|
||||
}
|
||||
struct tm * ct = localtime((time_t *) &t);
|
||||
strftime(buffer, 256, format, ct);
|
||||
a_thread->PushNewString(buffer);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
static int GM_CDECL gmfFileFindGetAttribute(gmThread * a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_INT_PARAM(attribute, 0);
|
||||
|
||||
GM_ASSERT(a_thread->GetThis()->m_type == s_gmFileFindType);
|
||||
gmFileFindUser * fileFind = (gmFileFindUser *) a_thread->ThisUser();
|
||||
|
||||
DWORD attr = 0;
|
||||
if(attribute == 'r')
|
||||
{
|
||||
attr = FILE_ATTRIBUTE_READONLY;
|
||||
}
|
||||
else if(attribute == 'a')
|
||||
{
|
||||
attr = FILE_ATTRIBUTE_ARCHIVE;
|
||||
}
|
||||
else if(attribute == 's')
|
||||
{
|
||||
attr = FILE_ATTRIBUTE_SYSTEM;
|
||||
}
|
||||
else if(attribute == 'h')
|
||||
{
|
||||
attr = FILE_ATTRIBUTE_HIDDEN;
|
||||
}
|
||||
else if(attribute == 'd')
|
||||
{
|
||||
attr = FILE_ATTRIBUTE_DIRECTORY;
|
||||
}
|
||||
else if(attribute == 'c')
|
||||
{
|
||||
attr = FILE_ATTRIBUTE_COMPRESSED;
|
||||
}
|
||||
|
||||
a_thread->PushInt((fileFind->m_findData.dwFileAttributes & attr) ? 1 : 0);
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
static void GM_CDECL gmFileFindOpGetDot(gmThread * a_thread, gmVariable * a_operands)
|
||||
{
|
||||
gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
|
||||
if(user && user->m_user)
|
||||
{
|
||||
gmFileFindUser * fileFind = (gmFileFindUser *) user->m_user;
|
||||
gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref);
|
||||
|
||||
if(strcmp(member->GetString(), "filename") == 0)
|
||||
{
|
||||
a_operands->SetString(a_thread->GetMachine()->AllocStringObject(fileFind->m_findData.cFileName));
|
||||
return;
|
||||
}
|
||||
else if(strcmp(member->GetString(), "size") == 0)
|
||||
{
|
||||
a_operands->SetInt(fileFind->m_findData.nFileSizeLow);
|
||||
return;
|
||||
}
|
||||
}
|
||||
a_operands->Nullify();
|
||||
}
|
||||
|
||||
#if GM_USE_INCGC
|
||||
static void GM_CDECL gmGCDestructFileFindUserType(gmMachine * a_machine, gmUserObject* a_object)
|
||||
{
|
||||
if(a_object->m_user)
|
||||
{
|
||||
gmFileFindUser * fileFind = (gmFileFindUser *) a_object->m_user;
|
||||
if(fileFind->m_iterator != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FindClose(fileFind->m_iterator);
|
||||
}
|
||||
a_machine->Sys_Free(a_object->m_user);
|
||||
}
|
||||
a_object->m_user = NULL;
|
||||
}
|
||||
#else //GM_USE_INCGC
|
||||
static void GM_CDECL gmGCFileFindUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
|
||||
{
|
||||
if(a_object->m_user)
|
||||
{
|
||||
gmFileFindUser * fileFind = (gmFileFindUser *) a_object->m_user;
|
||||
if(fileFind->m_iterator != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FindClose(fileFind->m_iterator);
|
||||
}
|
||||
a_machine->Sys_Free(a_object->m_user);
|
||||
}
|
||||
a_object->m_user = NULL;
|
||||
}
|
||||
#endif //GM_USE_INCGC
|
||||
|
||||
|
||||
static void GM_CDECL gmFileInfoOpGetDot(gmThread * a_thread, gmVariable * a_operands)
|
||||
{
|
||||
gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
|
||||
if(user && user->m_user)
|
||||
{
|
||||
gmFileInfoUser * fileInfo = (gmFileInfoUser *) user->m_user;
|
||||
gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref);
|
||||
|
||||
GM_ASSERT(sizeof(gmptr) == sizeof(time_t));
|
||||
|
||||
if(strcmp(member->GetString(), "creationTime") == 0)
|
||||
a_operands->SetInt((gmptr) fileInfo->m_creationTime);
|
||||
else if(strcmp(member->GetString(), "accessedTime") == 0)
|
||||
a_operands->SetInt((gmptr) fileInfo->m_accessedTime);
|
||||
else if(strcmp(member->GetString(), "modifiedTime") == 0)
|
||||
a_operands->SetInt((gmptr) fileInfo->m_modifiedTime);
|
||||
else if(strcmp(member->GetString(), "size") == 0)
|
||||
a_operands->SetInt((gmptr) fileInfo->m_size);
|
||||
else
|
||||
{
|
||||
a_operands->Nullify();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
a_operands->Nullify();
|
||||
}
|
||||
|
||||
|
||||
#if GM_USE_INCGC
|
||||
static void GM_CDECL gmGCDestructFileInfoUserType(gmMachine * a_machine, gmUserObject* a_object)
|
||||
{
|
||||
if(a_object->m_user)
|
||||
{
|
||||
a_machine->Sys_Free(a_object->m_user);
|
||||
}
|
||||
a_object->m_user = NULL;
|
||||
}
|
||||
#else //GM_USE_INCGC
|
||||
static void GM_CDECL gmGCFileInfoUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
|
||||
{
|
||||
if(a_object->m_user)
|
||||
{
|
||||
a_machine->Sys_Free(a_object->m_user);
|
||||
}
|
||||
a_object->m_user = NULL;
|
||||
}
|
||||
#endif //GM_USE_INCGC
|
||||
|
||||
//
|
||||
//
|
||||
// binding
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
static gmFunctionEntry s_systemLib[] =
|
||||
{
|
||||
/*gm
|
||||
\lib system
|
||||
\brief system functions are bound in a "system" table.
|
||||
*/
|
||||
/*gm
|
||||
\function Exec
|
||||
\brief Exec will execute a system command
|
||||
\param string params will be concatinated together with a single space to form the final system command string
|
||||
\return integer value returned from system exec call, -1 on error
|
||||
*/
|
||||
{"Exec", gmfSystem},
|
||||
/*gm
|
||||
\function DoFile
|
||||
\brief DoFile will execute the gm script in the named file
|
||||
\param string filename
|
||||
\param int optional (1) as 1 will execute string before returning, 0 will execute later.
|
||||
\param ref optional (null) set 'this'
|
||||
\return thread id of new thread created to execute file
|
||||
*/
|
||||
{"DoFile", gmfDoFile},
|
||||
/*gm
|
||||
\function File
|
||||
\brief File will create a file object
|
||||
\return file object
|
||||
*/
|
||||
{"File", gmfFile},
|
||||
/*gm
|
||||
\function FileExists
|
||||
\brief FileExists will test to see if a file exists
|
||||
\param string filename
|
||||
\return 1 if the file exists, otherwise 0
|
||||
*/
|
||||
{"FileExists", gmfFileExists},
|
||||
/*gm
|
||||
\function FileFindFirst
|
||||
\brief FileFindFirst will start a file search will test to see if a file exists
|
||||
\param string filesearch (may contain wildcards, eg, `c:\temp\*.txt`)
|
||||
\return fileFind object. fileFind object has .filename and .size member
|
||||
\sa fileFind
|
||||
*/
|
||||
{"FileFindFirst", gmfFindFirstFile},
|
||||
/*gm
|
||||
\function FileFindNext
|
||||
\brief FileFindNext will get the next file matching the file find
|
||||
\param fileFind object returned by FileFindFirst call or FileFindNext call
|
||||
\return fileFind object
|
||||
\sa fileFind
|
||||
*/
|
||||
{"FileFindNext", gmfFindNextFile},
|
||||
|
||||
/*gm
|
||||
\function FileInfo
|
||||
\brief FileInfo will return a file info object that has readonly members for .creationDate
|
||||
\param string path
|
||||
\return fileInfo object, fileInfo object has a .creationTime, .accessedTime, .modifiedTime and a .size
|
||||
*/
|
||||
{"FileInfo", gmfFileInfo},
|
||||
|
||||
/*gm
|
||||
\function CreateFolder
|
||||
\brief CreateFolder will create a file path if it does not already exist
|
||||
\param string path
|
||||
\return int 0 on failure, 1 on successful create, 2 if folder already exists
|
||||
*/
|
||||
{"CreateFolder", gmfCreateFolder},
|
||||
/*gm
|
||||
\function DeleteFolder
|
||||
\brief DeleteFolder will remove a file path
|
||||
\param string path
|
||||
\param int remove subfiles and folders optional (0)
|
||||
\return int 1 on success, 0 con failure
|
||||
*/
|
||||
{"DeleteFolder", gmfDeleteFolder},
|
||||
|
||||
/*gm
|
||||
\function Time
|
||||
\brief Time will return a unix style time_t as an int
|
||||
\return the current time
|
||||
*/
|
||||
{"Time", gmfTime},
|
||||
/*gm
|
||||
\function FormatTime
|
||||
\brief FormatTime will take a int (time_t) value and format according to the passed format string.
|
||||
\param int time (-1) is a (time_t) to be converted to a string, passing -1 gets current time
|
||||
\param string format ("%A %d %B %Y, %I:%M:%S %p") is the format string to use.<BR>
|
||||
%a : Abbreviated weekday name<BR>
|
||||
%A : Full weekday name<BR>
|
||||
%b : Abbreviated month name<BR>
|
||||
%B : Full month name<BR>
|
||||
%c : Date and time representation appropriate for locale<BR>
|
||||
%d : Day of month as decimal number (01 <20> 31)<BR>
|
||||
%H : Hour in 24-hour format (00 <20> 23)<BR>
|
||||
%I : Hour in 12-hour format (01 <20> 12)<BR>
|
||||
%j : Day of year as decimal number (001 <20> 366)<BR>
|
||||
%m : Month as decimal number (01 <20> 12)<BR>
|
||||
%M : Minute as decimal number (00 <20> 59)<BR>
|
||||
%p : Current locale<6C>s A.M./P.M. indicator for 12-hour clock<BR>
|
||||
%S : Second as decimal number (00 <20> 59)<BR>
|
||||
%U : Week of year as decimal number, with Sunday as first day of week (00 <20> 53)<BR>
|
||||
%w : Weekday as decimal number (0 <20> 6; Sunday is 0)<BR>
|
||||
%W : Week of year as decimal number, with Monday as first day of week (00 <20> 53)<BR>
|
||||
%x : Date representation for current locale<BR>
|
||||
%X : Time representation for current locale<BR>
|
||||
%y : Year without century, as decimal number (00 <20> 99)<BR>
|
||||
%Y : Year with century, as decimal number<BR>
|
||||
%z, %Z : Time-zone name or abbreviation; no characters if time zone is unknown<BR>
|
||||
%% : Percent sign<BR>
|
||||
\return the time as a string.
|
||||
*/
|
||||
{"FormatTime", gmfFormatTime},
|
||||
};
|
||||
|
||||
static gmFunctionEntry s_fileFindLib[] =
|
||||
{
|
||||
/*gm
|
||||
\lib fileFind
|
||||
\brief fileFind object has a "filename" and "size" member
|
||||
*/
|
||||
/*gm
|
||||
\function GetAttribute
|
||||
\brief GetAttribute will test a file attribute.
|
||||
\param int char attribute 'r' readonly, 'a' archive, 's' system, 'h' hidden, 'c' compressed, 'd' directory
|
||||
\return 1 if the attribute is set, 0 otherwise
|
||||
*/
|
||||
{"GetAttribute", gmfFileFindGetAttribute},
|
||||
};
|
||||
|
||||
|
||||
static gmFunctionEntry s_fileLib[] =
|
||||
{
|
||||
/*gm
|
||||
\lib file
|
||||
*/
|
||||
/*gm
|
||||
\function Open
|
||||
\brief Open will open a file in binary mode
|
||||
\param string filename
|
||||
\param int readonly optional (1)
|
||||
\return 1 if the open was successful, 0 otherwise
|
||||
*/
|
||||
{"Open", gmfFileOpen},
|
||||
/*gm
|
||||
\function OpenText
|
||||
\brief OpenText will open a file in text mode
|
||||
\param string filename
|
||||
\param int readonly optional (1)
|
||||
\return 1 if the open was successful, 0 otherwise
|
||||
*/
|
||||
{"OpenText", gmfFileOpenText},
|
||||
/*gm
|
||||
\function Close
|
||||
\brief Close will close a file
|
||||
*/
|
||||
{"Close", gmfFileClose},
|
||||
/*gm
|
||||
\function IsOpen
|
||||
\brief IsOpen will test to see if a file is open
|
||||
\return 1 if the file is open, 0 otherwise
|
||||
*/
|
||||
{"IsOpen", gmfFileIsOpen},
|
||||
/*gm
|
||||
\function Seek
|
||||
\brief Move to position within file
|
||||
\param int offset positional offset relative to origin
|
||||
\param int origin eg. myFile.SEEK_CUR, myFile.SEEK_END, myFile.SEEK_SET for current, end, start origins.
|
||||
\return int 1 if operation succeeded, 0 if failed.
|
||||
*/
|
||||
{"Seek", gmfFileSeek},
|
||||
/*gm
|
||||
\function Tell
|
||||
\brief Tell will return the current cursor position of the file
|
||||
\return int the current cursor position, -1 on error
|
||||
*/
|
||||
{"Tell", gmfFileTell},
|
||||
/*gm
|
||||
\function ReadLine
|
||||
\brief ReadLine will read a line of text from the file.
|
||||
\param int keep optional (0) as 1 will keep the "\n" char on the line, otherwise it is removed
|
||||
\return string, or null on eof
|
||||
*/
|
||||
{"ReadLine", gmfFileReadLine},
|
||||
/*gm
|
||||
\function ReadChar
|
||||
\brief ReadChar will read a char from the file
|
||||
\return int char or null on eof
|
||||
*/
|
||||
{"ReadChar", gmfFileReadChar},
|
||||
/*gm
|
||||
\function WriteString
|
||||
\brief WriteString will write a string to the file
|
||||
\param string to write to file
|
||||
\return 1 on success, null on error
|
||||
*/
|
||||
{"WriteString", gmfFileWriteString},
|
||||
/*gm
|
||||
\function WriteChar
|
||||
\brief WriteChar will write a char to the file
|
||||
\param int char to write to file
|
||||
\return 1 on success, null on error
|
||||
*/
|
||||
{"WriteChar", gmfFileWriteChar},
|
||||
};
|
||||
|
||||
|
||||
void gmBindSystemLib(gmMachine * a_machine)
|
||||
{
|
||||
// system
|
||||
a_machine->RegisterLibrary(s_systemLib, sizeof(s_systemLib) / sizeof(s_systemLib[0]), "system");
|
||||
|
||||
// file
|
||||
s_gmFileType = a_machine->CreateUserType("file");
|
||||
#if GM_USE_INCGC
|
||||
a_machine->RegisterUserCallbacks(s_gmFileType, NULL, gmGCDestructFileUserType);
|
||||
#else //GM_USE_INCGC
|
||||
a_machine->RegisterUserCallbacks(s_gmFileType, NULL, gmGCFileUserType);
|
||||
#endif //GM_USE_INCGC
|
||||
a_machine->RegisterTypeLibrary(s_gmFileType, s_fileLib, sizeof(s_fileLib) / sizeof(s_fileLib[0]));
|
||||
a_machine->RegisterTypeOperator(s_gmFileType, O_GETDOT, NULL, gmFileOpGetDot);
|
||||
|
||||
// fileFind
|
||||
s_gmFileFindType = a_machine->CreateUserType("fileFind");
|
||||
a_machine->RegisterTypeLibrary(s_gmFileFindType, s_fileFindLib, sizeof(s_fileFindLib) / sizeof(s_fileFindLib[0]));
|
||||
#if GM_USE_INCGC
|
||||
a_machine->RegisterUserCallbacks(s_gmFileFindType, NULL, gmGCDestructFileFindUserType);
|
||||
#else //GM_USE_INCGC
|
||||
a_machine->RegisterUserCallbacks(s_gmFileFindType, NULL, gmGCFileFindUserType);
|
||||
#endif //GM_USE_INCGC
|
||||
a_machine->RegisterTypeOperator(s_gmFileFindType, O_GETDOT, NULL, gmFileFindOpGetDot);
|
||||
|
||||
// fileInfo
|
||||
s_gmFileInfoType = a_machine->CreateUserType("fileInfo");
|
||||
#if GM_USE_INCGC
|
||||
a_machine->RegisterUserCallbacks(s_gmFileInfoType, NULL, gmGCDestructFileInfoUserType);
|
||||
#else //GM_USE_INCGC
|
||||
a_machine->RegisterUserCallbacks(s_gmFileInfoType, NULL, gmGCFileInfoUserType);
|
||||
#endif //GM_USE_INCGC
|
||||
a_machine->RegisterTypeOperator(s_gmFileInfoType, O_GETDOT, NULL, gmFileInfoOpGetDot);
|
||||
}
|
||||
|
||||
#endif // GM_SYSTEM_LIB
|
||||
28
vscript/languages/gm/src/binds/gmSystemLib.h
Normal file
28
vscript/languages/gm/src/binds/gmSystemLib.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMSYSTEMLIB_H_
|
||||
#define _GMSYSTEMLIB_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
|
||||
class gmMachine;
|
||||
|
||||
#define GM_SYSTEM_LIB 1
|
||||
#define GM_SYSTEM_LIB_MAX_LINE 1024 // maximum file line length
|
||||
|
||||
#if GM_SYSTEM_LIB
|
||||
|
||||
void gmBindSystemLib(gmMachine * a_machine);
|
||||
|
||||
#endif // GM_SYSTEM_LIB
|
||||
|
||||
#endif // _GMSYSTEMLIB_H_
|
||||
1059
vscript/languages/gm/src/binds/gmVector3Lib.cpp
Normal file
1059
vscript/languages/gm/src/binds/gmVector3Lib.cpp
Normal file
File diff suppressed because it is too large
Load Diff
37
vscript/languages/gm/src/binds/gmVector3Lib.h
Normal file
37
vscript/languages/gm/src/binds/gmVector3Lib.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMVECTOR3LIB_H_
|
||||
#define _GMVECTOR3LIB_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmVariable.h"
|
||||
|
||||
class gmMachine;
|
||||
class gmThread;
|
||||
class gmUserObject;
|
||||
|
||||
// Bind the Vector3 Library.
|
||||
void gmBindVector3Lib(gmMachine * a_machine);
|
||||
|
||||
// Push a Vector3 onto the stack
|
||||
void gmVector3_Push(gmThread* a_thread, const float* a_vec);
|
||||
|
||||
// Create a Vector3 user object and fill it
|
||||
gmUserObject* gmVector3_Create(gmMachine* a_machine, const float* a_vec);
|
||||
|
||||
// The Vector3 type Id.
|
||||
extern gmType GM_VECTOR3;
|
||||
|
||||
// Example of getting Vector3 from parameter
|
||||
// GM_CHECK_USER_PARAM(float*, GM_VECTOR3, vec1, 0);
|
||||
|
||||
#endif // _GMVECTOR3LIB_H_
|
||||
473
vscript/languages/gm/src/examples/GameObject/App.cpp
Normal file
473
vscript/languages/gm/src/examples/GameObject/App.cpp
Normal file
@@ -0,0 +1,473 @@
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h> // multimedia timer (may need winmm.lib)
|
||||
#include "App.h"
|
||||
#include "ScriptSys.h"
|
||||
#include "GameObj.h"
|
||||
#include "ScriptObj.h"
|
||||
#include "gmCall.h"
|
||||
#include "InputKBWin32.h"
|
||||
|
||||
App* App::s_instancePtr = NULL;
|
||||
|
||||
|
||||
App::App()
|
||||
{
|
||||
s_instancePtr = this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
App::~App()
|
||||
{
|
||||
s_instancePtr = NULL;
|
||||
}
|
||||
|
||||
|
||||
bool App::Init()
|
||||
{
|
||||
InputKBWin32::Get().Init();
|
||||
|
||||
// Init console
|
||||
InitConsole();
|
||||
|
||||
// Init script system for game objects
|
||||
ScriptSys::Init();
|
||||
|
||||
// Register app bindings
|
||||
RegisterScriptBindings();
|
||||
|
||||
// Init timer
|
||||
m_deltaTime = 0;
|
||||
m_lastTime = timeGetTime();
|
||||
|
||||
// Compile and run script
|
||||
ScriptSys::Get()->ExecuteFile("TestGameObj.gm");
|
||||
|
||||
//TEST REMOVE
|
||||
// ClearScreen();
|
||||
// SetColor(COLOR_YELLOW, COLOR_RED);
|
||||
// SetCursor(10,10);
|
||||
// Print("Hello");
|
||||
// PrintAt(10,10,"Hello");
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void App::Destroy()
|
||||
{
|
||||
ScriptSys::Destroy();
|
||||
}
|
||||
|
||||
|
||||
bool App::Update()
|
||||
{
|
||||
int curTime = timeGetTime();
|
||||
m_deltaTime = curTime - m_lastTime;
|
||||
m_lastTime = curTime;
|
||||
|
||||
// Update input
|
||||
InputKBWin32::Get().Update();
|
||||
|
||||
// Execute some script
|
||||
ScriptSys::Get()->Execute(m_deltaTime);
|
||||
|
||||
if(InputKBWin32::Get().IsKeyPressed('A'))
|
||||
{
|
||||
TestA();
|
||||
}
|
||||
else if(InputKBWin32::Get().IsKeyPressed('B'))
|
||||
{
|
||||
TestB();
|
||||
}
|
||||
else if(InputKBWin32::Get().IsKeyPressed('C'))
|
||||
{
|
||||
TestC();
|
||||
}
|
||||
|
||||
if(InputKBWin32::Get().IsKeyPressed(VK_ESCAPE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void App::InitConsole()
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
m_console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
GetConsoleScreenBufferInfo(m_console, &csbi);
|
||||
|
||||
DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
|
||||
|
||||
m_screenSizeX = csbi.dwSize.X;
|
||||
m_screenSizeY = csbi.dwSize.Y;
|
||||
}
|
||||
|
||||
|
||||
void App::TestA()
|
||||
{
|
||||
// Test create a game object and set member in script
|
||||
GameObj* newObj = new GameObj;
|
||||
|
||||
printf("new GameObj = %x\n", newObj);
|
||||
printf("GameObj.m_scriptObj = %x\n", newObj->GetScriptObj());
|
||||
|
||||
newObj->GetScriptObj()->SetMemberString("m_name", "MangoBoy");
|
||||
newObj->GetScriptObj()->ExecuteGlobalFunctionOnThis("WhatsMyName");
|
||||
|
||||
delete newObj;
|
||||
|
||||
gmCall call;
|
||||
if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), "RunGlobalObject"))
|
||||
{
|
||||
call.End();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void App::TestB()
|
||||
{
|
||||
// Test call script function that keeps running for a bit
|
||||
gmCall call;
|
||||
if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), "ThreadYieldTest"))
|
||||
{
|
||||
call.End();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void App::TestC()
|
||||
{
|
||||
// Quick test of collect garbage and cpp owned objects
|
||||
|
||||
GameObj* newObj = new GameObj;
|
||||
|
||||
printf("new GameObj = %x\n", newObj);
|
||||
printf("GameObj.m_scriptObj = %x\n", newObj->GetScriptObj());
|
||||
|
||||
newObj->GetScriptObj()->SetMemberString("m_name", "PotatoHead");
|
||||
//newObj->GetScriptObj()->ExecuteGlobalFunctionOnThis("WhatsMyName");
|
||||
|
||||
ScriptSys::Get()->GetMachine()->CollectGarbage(true);
|
||||
|
||||
delete newObj;
|
||||
}
|
||||
|
||||
|
||||
void App::SetCursor(int a_x, int a_y)
|
||||
{
|
||||
ClipScreenCoordsi(a_x, a_y);
|
||||
|
||||
COORD point;
|
||||
|
||||
point.X = (short) a_x;
|
||||
point.Y = (short) a_y;
|
||||
|
||||
SetConsoleCursorPosition(m_console, point);
|
||||
}
|
||||
|
||||
|
||||
void App::ClearScreen()
|
||||
{
|
||||
COORD coordScreen = { 0, 0 };
|
||||
DWORD cCharsWritten;
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
DWORD dwConSize;
|
||||
GetConsoleScreenBufferInfo(m_console, &csbi);
|
||||
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
|
||||
FillConsoleOutputCharacter(m_console, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
|
||||
GetConsoleScreenBufferInfo(m_console, &csbi);
|
||||
FillConsoleOutputAttribute(m_console, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
|
||||
SetConsoleCursorPosition(m_console, coordScreen);
|
||||
}
|
||||
|
||||
|
||||
void App::Print(const char* a_string)
|
||||
{
|
||||
printf("%s", a_string);
|
||||
}
|
||||
|
||||
|
||||
void App::PrintAt(int a_x, int a_y, const char* a_string)
|
||||
{
|
||||
SetCursor(a_x, a_y);
|
||||
Print(a_string);
|
||||
}
|
||||
|
||||
|
||||
int App::GetAttribFromColIndex(int a_colorIndex, bool a_isForeground)
|
||||
{
|
||||
// WARNING These struct must match the color enums
|
||||
static int foreColors[COLOR_MAX]=
|
||||
{
|
||||
0, //COLOR_BLACK
|
||||
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, //COLOR_WHITE,
|
||||
FOREGROUND_RED, //COLOR_RED
|
||||
FOREGROUND_GREEN, //COLOR_GREEN,
|
||||
FOREGROUND_BLUE, //COLOR_BLUE,
|
||||
FOREGROUND_RED | FOREGROUND_BLUE, //COLOR_MAGENTA,
|
||||
FOREGROUND_GREEN | FOREGROUND_BLUE, //COLOR_CYAN,
|
||||
FOREGROUND_RED | FOREGROUND_GREEN, //COLOR_YELLOW,
|
||||
};
|
||||
|
||||
static int backColors[COLOR_MAX]=
|
||||
{
|
||||
0, //COLOR_BLACK
|
||||
BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE, //COLOR_WHITE,
|
||||
BACKGROUND_RED, //COLOR_RED
|
||||
BACKGROUND_GREEN, //COLOR_GREEN,
|
||||
BACKGROUND_BLUE, //COLOR_BLUE,
|
||||
BACKGROUND_RED | BACKGROUND_BLUE, //COLOR_MAGENTA,
|
||||
BACKGROUND_GREEN | BACKGROUND_BLUE, //COLOR_CYAN,
|
||||
BACKGROUND_RED | BACKGROUND_GREEN, //COLOR_YELLOW,
|
||||
};
|
||||
|
||||
|
||||
if( (a_colorIndex < COLOR_MIN) && (a_colorIndex >= COLOR_MAX) )
|
||||
{
|
||||
if(a_isForeground)
|
||||
{
|
||||
return foreColors[COLOR_WHITE];
|
||||
}
|
||||
else
|
||||
{
|
||||
return backColors[COLOR_WHITE];
|
||||
}
|
||||
}
|
||||
|
||||
if(a_isForeground)
|
||||
{
|
||||
return foreColors[a_colorIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
return backColors[a_colorIndex];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void App::SetColor(int a_foreColorIndex, int a_backColorIndex)
|
||||
{
|
||||
int foreCol = GetAttribFromColIndex(a_foreColorIndex, true);
|
||||
int backCol = GetAttribFromColIndex(a_backColorIndex, false);
|
||||
|
||||
int param = foreCol | backCol;
|
||||
|
||||
SetConsoleTextAttribute(m_console, (short) param);
|
||||
}
|
||||
|
||||
|
||||
bool App::ClipScreenCoordsf(float& a_posX, float& a_posY)
|
||||
{
|
||||
bool wasClipped = false;
|
||||
|
||||
if(a_posX < 0.0f)
|
||||
{
|
||||
a_posX = 0.0f;
|
||||
wasClipped = true;
|
||||
}
|
||||
else if(a_posX >= (float)App::Get()->GetScreenSizeX())
|
||||
{
|
||||
a_posX = (float)(App::Get()->GetScreenSizeX() - 1);
|
||||
wasClipped = true;
|
||||
}
|
||||
|
||||
if(a_posY < 0.0f)
|
||||
{
|
||||
a_posY = 0.0f;
|
||||
wasClipped = true;
|
||||
}
|
||||
else if(a_posY >= (float)App::Get()->GetScreenSizeY())
|
||||
{
|
||||
a_posY = (float)(App::Get()->GetScreenSizeY() - 1);
|
||||
wasClipped = true;
|
||||
}
|
||||
|
||||
return wasClipped;
|
||||
}
|
||||
|
||||
|
||||
bool App::ClipScreenCoordsi(int& a_posX, int& a_posY)
|
||||
{
|
||||
bool wasClipped = false;
|
||||
|
||||
if(a_posX < 0)
|
||||
{
|
||||
a_posX = 0;
|
||||
wasClipped = true;
|
||||
}
|
||||
else if(a_posX >= App::Get()->GetScreenSizeX())
|
||||
{
|
||||
a_posX = App::Get()->GetScreenSizeX() - 1;
|
||||
wasClipped = true;
|
||||
}
|
||||
|
||||
if(a_posY < 0)
|
||||
{
|
||||
a_posY = 0;
|
||||
wasClipped = true;
|
||||
}
|
||||
else if(a_posY >= App::Get()->GetScreenSizeY())
|
||||
{
|
||||
a_posY = App::Get()->GetScreenSizeY() - 1;
|
||||
wasClipped = true;
|
||||
}
|
||||
|
||||
return wasClipped;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Script bindings
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
|
||||
int GM_CDECL App::Console_Print(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_STRING_PARAM(a_string, 0);
|
||||
|
||||
App::Get()->Print(a_string);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
int GM_CDECL App::Console_SetCursor(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
GM_CHECK_INT_PARAM(a_curX, 0);
|
||||
GM_CHECK_INT_PARAM(a_curY, 1);
|
||||
|
||||
App::Get()->ClipScreenCoordsi(a_curX, a_curY);
|
||||
App::Get()->SetCursor(a_curX, a_curY);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
int GM_CDECL App::Console_SetColor(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
GM_CHECK_INT_PARAM(a_foreCol, 0);
|
||||
GM_CHECK_INT_PARAM(a_backCol, 1);
|
||||
|
||||
App::Get()->SetColor(a_foreCol, a_backCol);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
int GM_CDECL App::Input_KeyPressed(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_INT_PARAM(a_vKey, 0);
|
||||
|
||||
if(InputKBWin32::Get().IsKeyPressed(a_vKey))
|
||||
{
|
||||
a_thread->PushInt(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_thread->PushInt(0);
|
||||
}
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
int GM_CDECL App::Input_KeyDown(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_INT_PARAM(a_vKey, 0);
|
||||
|
||||
if(InputKBWin32::Get().IsKeyDown(a_vKey))
|
||||
{
|
||||
a_thread->PushInt(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_thread->PushInt(0);
|
||||
}
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
void App::RegisterScriptBindings()
|
||||
{
|
||||
static gmFunctionEntry ConsoleLib[] =
|
||||
{
|
||||
/*gm
|
||||
\lib Console
|
||||
\brief Console Library
|
||||
*/
|
||||
/*gm
|
||||
\function Print
|
||||
\brief Print string at current cursor position
|
||||
\param string a_string
|
||||
*/
|
||||
{"Print", Console_Print},
|
||||
/*gm
|
||||
\function SetCursor
|
||||
\brief Set cursor position
|
||||
\param int a_curX Cursor x position
|
||||
\param int a_curY Cursor y position
|
||||
*/
|
||||
{"SetCursor", Console_SetCursor},
|
||||
|
||||
/*gm
|
||||
\function SetColor
|
||||
\brief Set text color
|
||||
\param int a_foreCol Foreground color
|
||||
\param int a_backCol Background color
|
||||
*/
|
||||
{"SetColor", Console_SetColor},
|
||||
};
|
||||
|
||||
|
||||
static gmFunctionEntry InputLib[] =
|
||||
{
|
||||
/*gm
|
||||
\lib Console
|
||||
\brief Console Library
|
||||
*/
|
||||
/*gm
|
||||
\function KeyPressed
|
||||
\brief Was this key pressed
|
||||
\param int a_vKey windows virtual key code (Most match ascii uppercase)
|
||||
\return true if key was pressed this frame
|
||||
*/
|
||||
{"KeyPressed", Input_KeyPressed},
|
||||
|
||||
/*gm
|
||||
\function KeyDown
|
||||
\brief Is this key down
|
||||
\param int a_vKey windows virtual key code (Most match ascii uppercase)
|
||||
\return true if is down
|
||||
*/
|
||||
{"KeyDown", Input_KeyDown},
|
||||
|
||||
};
|
||||
|
||||
gmMachine* machine = ScriptSys::Get()->GetMachine();
|
||||
|
||||
machine->RegisterLibrary(ConsoleLib, sizeof(ConsoleLib) / sizeof(ConsoleLib[0]), "Console");
|
||||
machine->RegisterLibrary(InputLib, sizeof(InputLib) / sizeof(InputLib[0]), "Input");
|
||||
|
||||
// Make some global constants
|
||||
machine->GetGlobals()->Set(machine, "COLOR_BLACK", gmVariable(GM_INT, COLOR_BLACK));
|
||||
machine->GetGlobals()->Set(machine, "COLOR_WHITE", gmVariable(GM_INT, COLOR_WHITE));
|
||||
machine->GetGlobals()->Set(machine, "COLOR_RED", gmVariable(GM_INT, COLOR_RED));
|
||||
machine->GetGlobals()->Set(machine, "COLOR_GREEN", gmVariable(GM_INT, COLOR_GREEN));
|
||||
machine->GetGlobals()->Set(machine, "COLOR_BLUE", gmVariable(GM_INT, COLOR_BLUE));
|
||||
machine->GetGlobals()->Set(machine, "COLOR_MAGENTA", gmVariable(GM_INT, COLOR_MAGENTA));
|
||||
machine->GetGlobals()->Set(machine, "COLOR_CYAN", gmVariable(GM_INT, COLOR_CYAN));
|
||||
machine->GetGlobals()->Set(machine, "COLOR_YELLOW", gmVariable(GM_INT, COLOR_YELLOW));
|
||||
}
|
||||
72
vscript/languages/gm/src/examples/GameObject/App.h
Normal file
72
vscript/languages/gm/src/examples/GameObject/App.h
Normal file
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// Example application
|
||||
//
|
||||
#include <Windows.h>
|
||||
#include "gmThread.h"
|
||||
|
||||
class App
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
COLOR_MIN = 0,
|
||||
|
||||
COLOR_BLACK = COLOR_MIN,
|
||||
COLOR_WHITE,
|
||||
COLOR_RED,
|
||||
COLOR_GREEN,
|
||||
COLOR_BLUE,
|
||||
COLOR_MAGENTA,
|
||||
COLOR_CYAN,
|
||||
COLOR_YELLOW,
|
||||
|
||||
|
||||
COLOR_MAX
|
||||
};
|
||||
|
||||
static App* Get() { return s_instancePtr; }
|
||||
|
||||
App();
|
||||
~App();
|
||||
bool Init();
|
||||
void Destroy();
|
||||
bool Update();
|
||||
|
||||
void SetCursor(int a_x, int a_y);
|
||||
void ClearScreen();
|
||||
void Print(const char* a_string);
|
||||
void PrintAt(int a_x, int a_y, const char* a_string);
|
||||
void SetColor(int a_foreColorIndex, int a_backColorIndex);
|
||||
int GetScreenSizeX() { return m_screenSizeX; }
|
||||
int GetScreenSizeY() { return m_screenSizeY; }
|
||||
|
||||
bool ClipScreenCoordsf(float& a_posX, float& a_posY);
|
||||
bool ClipScreenCoordsi(int& a_posX, int& a_posY);
|
||||
|
||||
protected:
|
||||
|
||||
int GetAttribFromColIndex(int a_colorIndex, bool a_isForeground);
|
||||
void InitConsole();
|
||||
void RegisterScriptBindings();
|
||||
|
||||
static int GM_CDECL Console_Print(gmThread* a_thread);
|
||||
static int GM_CDECL Console_SetCursor(gmThread* a_thread);
|
||||
static int GM_CDECL Console_SetColor(gmThread* a_thread);
|
||||
|
||||
static int GM_CDECL Input_KeyPressed(gmThread* a_thread);
|
||||
static int GM_CDECL Input_KeyDown(gmThread* a_thread);
|
||||
|
||||
void TestA();
|
||||
void TestB();
|
||||
void TestC();
|
||||
|
||||
int m_deltaTime;
|
||||
int m_lastTime;
|
||||
|
||||
int m_screenSizeX;
|
||||
int m_screenSizeY;
|
||||
HANDLE m_console;
|
||||
|
||||
static App* s_instancePtr; ///< Ptr to instance of this class when created
|
||||
};
|
||||
317
vscript/languages/gm/src/examples/GameObject/GameObj.cpp
Normal file
317
vscript/languages/gm/src/examples/GameObject/GameObj.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
//
|
||||
// GameObj.cpp
|
||||
//
|
||||
|
||||
#include <math.h>
|
||||
#include "GameObj.h"
|
||||
#include "App.h"
|
||||
#include "ScriptObj.h"
|
||||
#include "ScriptSys.h"
|
||||
|
||||
GameObj::GameObj()
|
||||
{
|
||||
m_scriptObj = NULL;
|
||||
m_posX = -1;
|
||||
m_posY = -1;
|
||||
m_destX = m_posX;
|
||||
m_destY = m_posY;
|
||||
m_speed = 1.0f;
|
||||
m_colorIndex = App::COLOR_WHITE;
|
||||
|
||||
m_scriptObj = new ScriptObj(this);
|
||||
}
|
||||
|
||||
|
||||
GameObj::~GameObj()
|
||||
{
|
||||
if(m_scriptObj)
|
||||
{
|
||||
delete m_scriptObj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float GameObj::GetPosX() const
|
||||
{
|
||||
return m_posX;
|
||||
}
|
||||
|
||||
|
||||
float GameObj::GetPosY() const
|
||||
{
|
||||
return m_posY;
|
||||
}
|
||||
|
||||
|
||||
void GameObj::SetPos(const float a_x, const float a_y)
|
||||
{
|
||||
m_posX = a_x;
|
||||
m_posY = a_y;
|
||||
|
||||
App::Get()->ClipScreenCoordsf(m_posX, m_posY);
|
||||
|
||||
m_destX = m_posX;
|
||||
m_destY = m_posY;
|
||||
}
|
||||
|
||||
|
||||
void GameObj::MoveTo(const float a_x, const float a_y)
|
||||
{
|
||||
m_destX = a_x;
|
||||
m_destY = a_y;
|
||||
|
||||
App::Get()->ClipScreenCoordsf(m_destX, m_destY);
|
||||
}
|
||||
|
||||
|
||||
void GameObj::SetSpeed(const float a_speed)
|
||||
{
|
||||
m_speed = a_speed;
|
||||
}
|
||||
|
||||
|
||||
float GameObj::GetSpeed()
|
||||
{
|
||||
return m_speed;
|
||||
}
|
||||
|
||||
|
||||
void GameObj::SetColor(int a_colorIndex)
|
||||
{
|
||||
if(a_colorIndex >= App::COLOR_MIN && a_colorIndex < App::COLOR_MAX)
|
||||
{
|
||||
m_colorIndex = a_colorIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GameObj::Update(float a_deltaTime)
|
||||
{
|
||||
// NOTE: A real game would probably not update each object each frame
|
||||
// but instead only update an object in a particular way when required.
|
||||
// This example will do all updating in one place, and do so each frame.
|
||||
|
||||
// Update movement
|
||||
{
|
||||
float dx = m_destX - m_posX;
|
||||
float dy = m_destY - m_posY;
|
||||
|
||||
float len2 = dx*dx + dy*dy;
|
||||
|
||||
if(len2 > 0.0f)
|
||||
{
|
||||
float moveThisFrame = a_deltaTime * m_speed;
|
||||
float distToGo = sqrtf(len2);
|
||||
|
||||
// We can reach dest this frame
|
||||
if(moveThisFrame > distToGo)
|
||||
{
|
||||
m_posX = m_destX;
|
||||
m_posY = m_destY;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_posX += dx * moveThisFrame;
|
||||
m_posY += dy * moveThisFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GameObj::Render()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Script
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
|
||||
int GM_CDECL GameObj::GameObj_MoveTo(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
GM_CHECK_FLOAT_PARAM(destX, 0);
|
||||
GM_CHECK_FLOAT_PARAM(destY, 1);
|
||||
GameObj* thisPtr = GetThisGameObj(a_thread);
|
||||
if(!thisPtr)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
thisPtr->MoveTo(destX, destY);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
int GM_CDECL GameObj::GameObj_SetPos(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(2);
|
||||
GM_CHECK_FLOAT_PARAM(posX, 0);
|
||||
GM_CHECK_FLOAT_PARAM(posY, 1);
|
||||
GameObj* thisPtr = GetThisGameObj(a_thread);
|
||||
if(!thisPtr)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
thisPtr->SetPos(posX, posY);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
int GM_CDECL GameObj::GameObj_GetPosX(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(0);
|
||||
GameObj* thisPtr = GetThisGameObj(a_thread);
|
||||
if(!thisPtr)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
a_thread->PushFloat(thisPtr->GetPosX());
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
int GM_CDECL GameObj::GameObj_GetPosY(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(0);
|
||||
GameObj* thisPtr = GetThisGameObj(a_thread);
|
||||
if(!thisPtr)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
a_thread->PushFloat(thisPtr->GetPosY());
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
int GM_CDECL GameObj::GameObj_IsValid(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(0);
|
||||
GameObj* thisPtr = GetThisGameObj(a_thread);
|
||||
if(!thisPtr)
|
||||
{
|
||||
a_thread->PushInt(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_thread->PushInt(true);
|
||||
}
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int GM_CDECL GameObj::GameObj_SetSpeed(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_FLOAT_PARAM(speed, 0);
|
||||
GameObj* thisPtr = GetThisGameObj(a_thread);
|
||||
if(!thisPtr)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
thisPtr->SetSpeed(speed);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
int GM_CDECL GameObj::GameObj_GetSpeed(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(0);
|
||||
GameObj* thisPtr = GetThisGameObj(a_thread);
|
||||
if(!thisPtr)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
a_thread->PushFloat(thisPtr->GetSpeed());
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
int GM_CDECL GameObj::GameObj_SetColor(gmThread* a_thread)
|
||||
{
|
||||
GM_CHECK_NUM_PARAMS(1);
|
||||
GM_CHECK_INT_PARAM(colorIndex, 0);
|
||||
GameObj* thisPtr = GetThisGameObj(a_thread);
|
||||
if(!thisPtr)
|
||||
{
|
||||
return GM_EXCEPTION;
|
||||
}
|
||||
|
||||
thisPtr->SetColor(colorIndex);
|
||||
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
void GameObj::RegisterScriptBindings()
|
||||
{
|
||||
static gmFunctionEntry gameObjTypeLib[] =
|
||||
{
|
||||
/*gm
|
||||
\lib GameObj
|
||||
\brief Game Object class
|
||||
*/
|
||||
/*gm
|
||||
\function SetPos
|
||||
\brief Set position
|
||||
\param float a_posX New position X component
|
||||
\param float a_posY New position Y component
|
||||
*/
|
||||
{"SetPos", GameObj_SetPos},
|
||||
/*gm
|
||||
\function GetPosX
|
||||
\brief Get position X
|
||||
\return float Get position X component.
|
||||
*/
|
||||
{"GetPosX", GameObj_GetPosX},
|
||||
/*gm
|
||||
\function GetPosY
|
||||
\brief Get position Y
|
||||
\return float Get position Y component.
|
||||
*/
|
||||
{"GetPosY", GameObj_GetPosY},
|
||||
/*gm
|
||||
\function IsValid
|
||||
\brief Is this a valid object, or has it been deleted or such
|
||||
\return int true if valid
|
||||
*/
|
||||
{"IsValid", GameObj_IsValid},
|
||||
/*gm
|
||||
\function SetSpeed
|
||||
\brief Set speed
|
||||
\param a_speed New speed
|
||||
*/
|
||||
{"SetSpeed", GameObj_SetSpeed},
|
||||
/*gm
|
||||
\function GetSpeed
|
||||
\brief Get speed
|
||||
\return float Current speed
|
||||
*/
|
||||
{"GetSpeed", GameObj_GetSpeed},
|
||||
/*gm
|
||||
\function SetColor
|
||||
\brief Set color
|
||||
\param a_color New color
|
||||
*/
|
||||
{"SetColor", GameObj_SetColor},
|
||||
|
||||
};
|
||||
|
||||
ScriptSys::Get()->GetMachine()->RegisterTypeLibrary(ScriptObj::GMTYPE_GAMEOBJ, gameObjTypeLib, sizeof(gameObjTypeLib) / sizeof(gameObjTypeLib[0]));
|
||||
}
|
||||
61
vscript/languages/gm/src/examples/GameObject/GameObj.h
Normal file
61
vscript/languages/gm/src/examples/GameObject/GameObj.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef GAMEOBJ_H
|
||||
#define GAMEOBJ_H
|
||||
|
||||
#include "gmThread.h"
|
||||
|
||||
//
|
||||
// GameObj.h
|
||||
//
|
||||
// Example game object that uses the script interface component
|
||||
//
|
||||
|
||||
// Fwd decls
|
||||
class ScriptObj;
|
||||
|
||||
|
||||
|
||||
class GameObj
|
||||
{
|
||||
public:
|
||||
|
||||
GameObj();
|
||||
virtual ~GameObj();
|
||||
|
||||
ScriptObj* GetScriptObj() { return m_scriptObj; }
|
||||
|
||||
float GetPosX() const;
|
||||
float GetPosY() const;
|
||||
void SetPos(const float a_x, const float a_y);
|
||||
void MoveTo(const float a_x, const float a_y);
|
||||
void SetSpeed(const float a_speed);
|
||||
float GetSpeed();
|
||||
void SetColor(int a_colorIndex);
|
||||
|
||||
void Update(float a_deltaTime);
|
||||
void Render();
|
||||
|
||||
static void RegisterScriptBindings();
|
||||
|
||||
private:
|
||||
|
||||
static int GM_CDECL GameObj_SetPos(gmThread* a_thread);
|
||||
static int GM_CDECL GameObj_GetPosX(gmThread* a_thread);
|
||||
static int GM_CDECL GameObj_GetPosY(gmThread* a_thread);
|
||||
static int GM_CDECL GameObj_IsValid(gmThread* a_thread);
|
||||
static int GM_CDECL GameObj_MoveTo(gmThread* a_thread);
|
||||
static int GM_CDECL GameObj_SetSpeed(gmThread* a_thread);
|
||||
static int GM_CDECL GameObj_GetSpeed(gmThread* a_thread);
|
||||
static int GM_CDECL GameObj_SetColor(gmThread* a_thread);
|
||||
|
||||
ScriptObj* m_scriptObj;
|
||||
float m_posX;
|
||||
float m_posY;
|
||||
float m_destX;
|
||||
float m_destY;
|
||||
float m_speed;
|
||||
int m_colorIndex;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //GAMEOBJ_H
|
||||
470
vscript/languages/gm/src/examples/GameObject/GameObject.dsp
Normal file
470
vscript/languages/gm/src/examples/GameObject/GameObject.dsp
Normal file
@@ -0,0 +1,470 @@
|
||||
# Microsoft Developer Studio Project File - Name="GameObject" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=GameObject - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "GameObject.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "GameObject.mak" CFG="GameObject - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "GameObject - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "GameObject - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "Perforce Project"
|
||||
# PROP Scc_LocalPath "..\.."
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "GameObject - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\gm" /I "..\..\platform\win32msvc" /I "..\..\binds" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD BASE RSC /l 0xc09 /d "NDEBUG"
|
||||
# ADD RSC /l 0xc09 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Ws2_32.lib /nologo /subsystem:console /machine:I386
|
||||
|
||||
!ELSEIF "$(CFG)" == "GameObject - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\gm" /I "..\..\platform\win32msvc" /I "..\..\binds" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0xc09 /d "_DEBUG"
|
||||
# ADD RSC /l 0xc09 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "GameObject - Win32 Release"
|
||||
# Name "GameObject - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\App.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\App.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\GameObj.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\GameObj.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\InputKBWin32.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\InputKBWin32.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\main.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NetClient.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NetClient.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ReadMe.txt
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ScriptObj.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ScriptObj.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ScriptSys.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ScriptSys.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\StdStuff.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\StdStuff.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\TestGameObj.gm
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "gm"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmArraySimple.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmArraySimple.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmByteCode.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmByteCode.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmByteCodeGen.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmByteCodeGen.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeGen.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeGen.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeGenHooks.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeGenHooks.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeTree.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeTree.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmConfig.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCrc.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCrc.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmDebug.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmDebug.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmFunctionObject.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmFunctionObject.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmHash.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmHash.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmIncGC.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmIncGC.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmIterator.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmLibHooks.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmLibHooks.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmListDouble.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmListDouble.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmLog.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmLog.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMachine.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMachine.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMachineLib.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMachineLib.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMem.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMem.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemChain.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemChain.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemFixed.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemFixed.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemFixedSet.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemFixedSet.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmOperators.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmOperators.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmParser.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmParser.cpp.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmScanner.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmScanner.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStream.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStream.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStreamBuffer.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStreamBuffer.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStringObject.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStringObject.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmTableObject.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmTableObject.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmThread.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmThread.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmUserObject.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmUserObject.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmUtil.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmUtil.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmVariable.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmVariable.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "gmBinds"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\binds\gmCall.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\binds\gmCall.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\binds\gmHelpers.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\binds\gmHelpers.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\binds\gmMathLib.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\binds\gmMathLib.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\binds\gmStringLib.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\binds\gmStringLib.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\binds\gmSystemLib.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\binds\gmSystemLib.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "gmConfig"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\platform\win32msvc\gmConfig_p.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
||||
33
vscript/languages/gm/src/examples/GameObject/GameObject.dsw
Normal file
33
vscript/languages/gm/src/examples/GameObject/GameObject.dsw
Normal file
@@ -0,0 +1,33 @@
|
||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "GameObject"=.\GameObject.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
Perforce Project
|
||||
..\..
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
1383
vscript/languages/gm/src/examples/GameObject/GameObject.vcproj
Normal file
1383
vscript/languages/gm/src/examples/GameObject/GameObject.vcproj
Normal file
File diff suppressed because it is too large
Load Diff
102
vscript/languages/gm/src/examples/GameObject/InputKBWin32.cpp
Normal file
102
vscript/languages/gm/src/examples/GameObject/InputKBWin32.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// InputKBWin32.cpp
|
||||
//
|
||||
|
||||
#include <windows.h>
|
||||
#include "InputKBWin32.h"
|
||||
|
||||
// Init statics and constants
|
||||
InputKBWin32 InputKBWin32::s_staticInstance;
|
||||
|
||||
|
||||
|
||||
InputKBWin32::InputKBWin32()
|
||||
{
|
||||
m_keyDownBufferIndex = 0;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
|
||||
InputKBWin32::~InputKBWin32()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void InputKBWin32::Init()
|
||||
{
|
||||
for(int kIndex=0; kIndex<MAX_KEYS; ++kIndex)
|
||||
{
|
||||
m_keyDownBuffer[0][kIndex] = 0;
|
||||
m_keyDownBuffer[1][kIndex] = 0;
|
||||
m_keyStatus[kIndex] = KEY_STATUS_UP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InputKBWin32::Update()
|
||||
{
|
||||
//#define HAS_WINDOW_UPDATE // Define this if we have a window and a message pump
|
||||
|
||||
int lastBuffIndex = !m_keyDownBufferIndex;
|
||||
int curBuffIndex = m_keyDownBufferIndex;
|
||||
|
||||
m_keyDownBufferIndex = lastBuffIndex; // Flip buffers
|
||||
|
||||
char* bufferCurrent = &m_keyDownBuffer[curBuffIndex][0];
|
||||
char* bufferLast = &m_keyDownBuffer[lastBuffIndex][0];
|
||||
|
||||
// Get the button states from Win32
|
||||
#ifdef HAS_WINDOW_UPDATE
|
||||
// We have a window and message pump
|
||||
BYTE win32KeyBuffer[256];
|
||||
GetKeyboardState(win32KeyBuffer);
|
||||
#else // HAS_WINDOW_UPDATE
|
||||
short win32KeyBuffer[256];
|
||||
for(int vkIndex=0; vkIndex < 256; ++vkIndex)
|
||||
{
|
||||
win32KeyBuffer[vkIndex] = GetAsyncKeyState(vkIndex);
|
||||
}
|
||||
#endif //HAS_WINDOW_UPDATE
|
||||
|
||||
// Find state changes
|
||||
for(int kIndex=0; kIndex < MAX_KEYS; ++kIndex)
|
||||
{
|
||||
int status;
|
||||
|
||||
// Convert win32 keystate to true / false
|
||||
#ifdef HAS_WINDOW_UPDATE
|
||||
if(win32KeyBuffer[kIndex] & (1<<7))
|
||||
#else // HAS_WINDOW_UPDATE
|
||||
if(win32KeyBuffer[kIndex] & (1<<15))
|
||||
#endif // HAS_WINDOW_UPDATE
|
||||
{
|
||||
bufferCurrent[kIndex] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferCurrent[kIndex] = false;
|
||||
}
|
||||
|
||||
status = 0;
|
||||
if(bufferCurrent[kIndex])
|
||||
{
|
||||
status |= KEY_STATUS_DOWN;
|
||||
if(!bufferLast[kIndex])
|
||||
{
|
||||
status |= KEY_STATUS_PRESSED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status |= KEY_STATUS_UP;
|
||||
if(bufferLast[kIndex])
|
||||
{
|
||||
status |= KEY_STATUS_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
m_keyStatus[kIndex] = status;
|
||||
}
|
||||
}
|
||||
90
vscript/languages/gm/src/examples/GameObject/InputKBWin32.h
Normal file
90
vscript/languages/gm/src/examples/GameObject/InputKBWin32.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef INPUTKBWIN32_H
|
||||
#define INPUTKBWIN32_H
|
||||
|
||||
//
|
||||
// InputKBWin32.h
|
||||
//
|
||||
|
||||
#include "gmThread.h" // For some basic types
|
||||
|
||||
/// A simple keyboard input class for Win32.
|
||||
class InputKBWin32
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_KEYS = 256, ///< Max keys on keyboard (for buffer size etc.)
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
KEY_STATUS_UNKNOWN = 0, ///< Invalid status
|
||||
KEY_STATUS_UP = (1<<0), ///< Button is up
|
||||
KEY_STATUS_RELEASED = (1<<1), ///< Button released this frame
|
||||
KEY_STATUS_DOWN = (1<<2), ///< Button is down
|
||||
KEY_STATUS_PRESSED = (1<<3), ///< Button pressed this frame
|
||||
};
|
||||
|
||||
/// Access single instance of this class
|
||||
static InputKBWin32& Get() { return s_staticInstance; }
|
||||
|
||||
/// Destructor
|
||||
virtual ~InputKBWin32();
|
||||
|
||||
/// Initialize. Call before use.
|
||||
void Init();
|
||||
|
||||
/// Call each frame.
|
||||
void Update();
|
||||
|
||||
/// What is the status of a key. Returns one of the KEY_STATUS_* enums.
|
||||
inline int GetKeyStatus(int a_keyIndex)
|
||||
{
|
||||
GM_ASSERT((a_keyIndex >=0) && (a_keyIndex < MAX_KEYS));
|
||||
return m_keyStatus[a_keyIndex];
|
||||
}
|
||||
|
||||
/// Was key pressed this frame? (Non-zero if true)
|
||||
inline int IsKeyPressed(int a_keyIndex)
|
||||
{
|
||||
GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
|
||||
return (m_keyStatus[a_keyIndex] & KEY_STATUS_PRESSED);
|
||||
}
|
||||
|
||||
/// Is key down this frame? (Non-zero if true)
|
||||
inline int IsKeyDown(int a_keyIndex)
|
||||
{
|
||||
GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
|
||||
return (m_keyStatus[a_keyIndex] & KEY_STATUS_DOWN);
|
||||
}
|
||||
|
||||
/// Was key released this frame? (Non-zero if true)
|
||||
inline int IsKeyRelesed(int a_keyIndex)
|
||||
{
|
||||
GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
|
||||
return (m_keyStatus[a_keyIndex] & KEY_STATUS_RELEASED);
|
||||
}
|
||||
|
||||
/// Is key up this frame? (Non-zero if true)
|
||||
inline int IsKeyUp(int a_keyIndex)
|
||||
{
|
||||
GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
|
||||
return (m_keyStatus[a_keyIndex] & KEY_STATUS_UP);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/// Constructor, non-public to prevent multiple instances
|
||||
InputKBWin32();
|
||||
|
||||
char m_keyDownBuffer[2][MAX_KEYS]; ///< Store current and last frame snapshot
|
||||
int m_keyDownBufferIndex; ///< Index to swap buffers for current and last frame
|
||||
int m_keyStatus[MAX_KEYS]; ///< Status of keys for this frame, persists until updated.
|
||||
|
||||
static InputKBWin32 s_staticInstance; ///< Single instance of this class
|
||||
};
|
||||
|
||||
|
||||
#endif //INPUTKBWIN32_H
|
||||
356
vscript/languages/gm/src/examples/GameObject/NetClient.cpp
Normal file
356
vscript/languages/gm/src/examples/GameObject/NetClient.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
// See Copyright Notice in gmMachine.h
|
||||
|
||||
#include "NetClient.h"
|
||||
#include <windows.h>
|
||||
#include <process.h> // Requires Multi threaded library for _beginthread and _endthread
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <conio.h>
|
||||
#include <winsock.h> // Requires Ws2_32.lib
|
||||
#include <math.h>
|
||||
|
||||
#undef SendMessage // stupid windows
|
||||
|
||||
// These two are for MSVS 2005 security consciousness until safe std lib funcs are available
|
||||
#pragma warning(disable : 4996) // Deprecated functions
|
||||
#define _CRT_SECURE_NO_DEPRECATE // Allow old unsecure standard library functions, Disable some 'warning C4996 - function was deprecated'
|
||||
|
||||
|
||||
struct nPacket
|
||||
{
|
||||
int id; // id == 0x4fe27d9a
|
||||
int len;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// QUEUE
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct nQueueNode
|
||||
{
|
||||
char * m_buffer;
|
||||
int m_len;
|
||||
nQueueNode * m_next;
|
||||
};
|
||||
|
||||
class nQueue
|
||||
{
|
||||
public:
|
||||
nQueue()
|
||||
{
|
||||
m_queue = NULL;
|
||||
m_lastDeQueue = NULL;
|
||||
m_mutex = CreateMutex(NULL, FALSE, NULL);
|
||||
}
|
||||
|
||||
~nQueue()
|
||||
{
|
||||
WaitForSingleObject(m_mutex, INFINITE);
|
||||
|
||||
int a;
|
||||
while(DeQueue(a));
|
||||
DeQueue(a);
|
||||
|
||||
ReleaseMutex(m_mutex);
|
||||
|
||||
// destroy mutex... todo
|
||||
}
|
||||
|
||||
bool EnQueue(const char * a_buffer, int a_len)
|
||||
{
|
||||
WaitForSingleObject(m_mutex, INFINITE);
|
||||
|
||||
nQueueNode * node = new nQueueNode;
|
||||
node->m_len = a_len;
|
||||
node->m_buffer = new char[a_len];
|
||||
memcpy(node->m_buffer, a_buffer, a_len);
|
||||
node->m_next = NULL;
|
||||
|
||||
// add to end of list
|
||||
nQueueNode ** n = &m_queue;
|
||||
while(*n) n = &(*n)->m_next;
|
||||
*n = node;
|
||||
|
||||
ReleaseMutex(m_mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char * DeQueue(int &a_len)
|
||||
{
|
||||
const char * ret = NULL;
|
||||
|
||||
WaitForSingleObject(m_mutex, INFINITE);
|
||||
|
||||
if(m_lastDeQueue)
|
||||
{
|
||||
delete[] m_lastDeQueue->m_buffer;
|
||||
delete m_lastDeQueue;
|
||||
m_lastDeQueue = NULL;
|
||||
}
|
||||
|
||||
if(m_queue)
|
||||
{
|
||||
m_lastDeQueue = m_queue;
|
||||
m_queue = m_queue->m_next;
|
||||
a_len = m_lastDeQueue->m_len;
|
||||
ret = m_lastDeQueue->m_buffer;
|
||||
}
|
||||
|
||||
ReleaseMutex(m_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IsEmpty() { return (m_queue == NULL); }
|
||||
|
||||
private:
|
||||
|
||||
HANDLE m_mutex;
|
||||
|
||||
nQueueNode * m_lastDeQueue;
|
||||
nQueueNode * m_queue;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CLIENT
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
struct nClientData
|
||||
{
|
||||
SOCKET client;
|
||||
nQueue messages;
|
||||
CRITICAL_SECTION criticalSection;
|
||||
bool threadAlive;
|
||||
};
|
||||
|
||||
|
||||
|
||||
nClient::nClient()
|
||||
{
|
||||
nClientData * cd = new nClientData;
|
||||
cd->threadAlive = false;
|
||||
cd->client = INVALID_SOCKET;
|
||||
InitializeCriticalSection(&cd->criticalSection);
|
||||
m_data = cd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
nClient::~nClient()
|
||||
{
|
||||
Close();
|
||||
nClientData * cd = (nClientData *) m_data;
|
||||
DeleteCriticalSection(&cd->criticalSection);
|
||||
delete cd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void nClient::Close()
|
||||
{
|
||||
nClientData * cd = (nClientData *) m_data;
|
||||
EnterCriticalSection(&cd->criticalSection);
|
||||
|
||||
if(cd->client != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(cd->client);
|
||||
cd->client = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&cd->criticalSection);
|
||||
|
||||
// wait for the thread to die.
|
||||
while(cd->threadAlive)
|
||||
{
|
||||
_sleep(0);
|
||||
}
|
||||
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool nClient::IsConnected()
|
||||
{
|
||||
bool result = false;
|
||||
nClientData * cd = (nClientData *) m_data;
|
||||
EnterCriticalSection(&cd->criticalSection);
|
||||
if(cd->client != INVALID_SOCKET)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
LeaveCriticalSection(&cd->criticalSection);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool nClient::SendMessage(const char * a_buffer, int a_len)
|
||||
{
|
||||
bool res = false;
|
||||
nClientData * cd = (nClientData *) m_data;
|
||||
nPacket packet;
|
||||
packet.id = 0x4fe27d9a;
|
||||
packet.len = a_len;
|
||||
|
||||
EnterCriticalSection(&cd->criticalSection);
|
||||
if(cd->client != INVALID_SOCKET)
|
||||
{
|
||||
send(cd->client, (const char *) &packet, sizeof(nPacket), 0);
|
||||
send(cd->client, (const char *) a_buffer, a_len, 0);
|
||||
res = true;
|
||||
}
|
||||
LeaveCriticalSection(&cd->criticalSection);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char * nClient::PumpMessage(int &a_len)
|
||||
{
|
||||
nClientData * cd = (nClientData *) m_data;
|
||||
const char * buffer = cd->messages.DeQueue(a_len);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void nClientThread(void * param)
|
||||
{
|
||||
nClientData * cd = (nClientData *) param;
|
||||
EnterCriticalSection(&cd->criticalSection);
|
||||
cd->threadAlive = true;
|
||||
SOCKET client = cd->client;
|
||||
LeaveCriticalSection(&cd->criticalSection);
|
||||
|
||||
char * dbuffer = NULL;
|
||||
char buffer[4096];
|
||||
char * sbp;
|
||||
int state = 0; // 0 searching for packet, 1 getting message
|
||||
int need = sizeof(nPacket);
|
||||
|
||||
// packet header
|
||||
nPacket packet;
|
||||
char * dbp = (char *) &packet;
|
||||
|
||||
// packet data
|
||||
int dbufferSize = 0, n;
|
||||
|
||||
// read loop
|
||||
for(;;)
|
||||
{
|
||||
// read
|
||||
n = recv(client, buffer, 4096, 0);
|
||||
if(n == SOCKET_ERROR || n == 0) break;
|
||||
sbp = buffer;
|
||||
|
||||
// consume
|
||||
while(n > 0)
|
||||
{
|
||||
int have = (n > need) ? need : n;
|
||||
need -= have;
|
||||
n -= have;
|
||||
memcpy(dbp, sbp, have);
|
||||
sbp += have;
|
||||
dbp += have;
|
||||
|
||||
// can we change state?
|
||||
if(need == 0)
|
||||
{
|
||||
if(state == 0)
|
||||
{
|
||||
if(packet.id != 0x4fe27d9a) goto terror;
|
||||
state = 1;
|
||||
need = packet.len;
|
||||
|
||||
// allocate the dbuffer
|
||||
if(need > dbufferSize)
|
||||
{
|
||||
if(dbuffer) { delete[] dbuffer; }
|
||||
dbufferSize = need + 512;
|
||||
dbuffer = new char[dbufferSize];
|
||||
}
|
||||
dbp = dbuffer;
|
||||
}
|
||||
else if(state == 1)
|
||||
{
|
||||
cd->messages.EnQueue(dbuffer, packet.len);
|
||||
dbp = (char *) &packet;
|
||||
need = sizeof(nPacket);
|
||||
state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
terror:
|
||||
|
||||
if(dbuffer) { delete[] dbuffer; }
|
||||
|
||||
EnterCriticalSection(&cd->criticalSection);
|
||||
cd->threadAlive = false;
|
||||
LeaveCriticalSection(&cd->criticalSection);
|
||||
|
||||
_endthread();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool nClient::Connect(const char * a_server, short a_port)
|
||||
{
|
||||
WSADATA wsaData;
|
||||
struct hostent *hp;
|
||||
unsigned int addr;
|
||||
struct sockaddr_in server;
|
||||
|
||||
int wsaret=WSAStartup(0x101,&wsaData);
|
||||
if(wsaret)
|
||||
return false;
|
||||
|
||||
SOCKET conn;
|
||||
|
||||
conn = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
|
||||
if(conn==INVALID_SOCKET)
|
||||
return false;
|
||||
|
||||
addr=inet_addr(a_server);
|
||||
hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
|
||||
|
||||
if(hp==NULL)
|
||||
{
|
||||
closesocket(conn);
|
||||
return false;
|
||||
}
|
||||
|
||||
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
|
||||
server.sin_family=AF_INET;
|
||||
server.sin_port=htons((u_short) a_port);
|
||||
|
||||
if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
|
||||
{
|
||||
closesocket(conn);
|
||||
return false;
|
||||
}
|
||||
|
||||
nClientData * cd = (nClientData *) m_data;
|
||||
EnterCriticalSection(&cd->criticalSection);
|
||||
cd->client = conn;
|
||||
LeaveCriticalSection(&cd->criticalSection);
|
||||
|
||||
_beginthread(nClientThread, 0, cd);
|
||||
_sleep(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
30
vscript/languages/gm/src/examples/GameObject/NetClient.h
Normal file
30
vscript/languages/gm/src/examples/GameObject/NetClient.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef _NETCLIENT_H_
|
||||
#define _NETCLIENT_H_
|
||||
|
||||
// See Copyright Notice in gmMachine.h
|
||||
|
||||
#undef SendMessage // windows clash
|
||||
|
||||
//
|
||||
// nClient
|
||||
//
|
||||
class nClient
|
||||
{
|
||||
public:
|
||||
nClient();
|
||||
~nClient();
|
||||
|
||||
bool Connect(const char * a_server, short a_port);
|
||||
void Close();
|
||||
bool IsConnected();
|
||||
|
||||
bool SendMessage(const char * a_buffer, int a_len);
|
||||
const char * PumpMessage(int &a_len);
|
||||
|
||||
private:
|
||||
|
||||
void * m_data;
|
||||
};
|
||||
|
||||
|
||||
#endif //_NETCLIENT_H_
|
||||
10
vscript/languages/gm/src/examples/GameObject/ReadMe.txt
Normal file
10
vscript/languages/gm/src/examples/GameObject/ReadMe.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
This example is simply one way to implement 'Game Objects'. The example is a work in
|
||||
progress, and is included because some of the code may provide useful to look at.
|
||||
|
||||
|
||||
TODO
|
||||
o make 'triggers' and 'units'
|
||||
o make IsUnitInSquare function to clip movement
|
||||
o use states for units to be 'player' 'following' 'mental'
|
||||
o make triggers that change unit color and triggers that create mental state if not player
|
||||
o highlight player by color and allow player to cycle through units to control
|
||||
326
vscript/languages/gm/src/examples/GameObject/ScriptObj.cpp
Normal file
326
vscript/languages/gm/src/examples/GameObject/ScriptObj.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
//
|
||||
// ScriptObj.cpp
|
||||
//
|
||||
|
||||
#include "gmCall.h"
|
||||
#include "ScriptObj.h"
|
||||
#include "ScriptSys.h"
|
||||
#include "GameObj.h"
|
||||
|
||||
|
||||
// Init statics and constants
|
||||
gmType ScriptObj::GMTYPE_GAMEOBJ = -1;
|
||||
|
||||
|
||||
ScriptObj::ScriptObj(GameObj* a_gameObj)
|
||||
{
|
||||
GM_ASSERT(ScriptSys::Get());
|
||||
|
||||
m_userObject = NULL; // A user object will be created when it is first used, and shared by all script variables.
|
||||
m_gameObj = a_gameObj;
|
||||
m_tableObject = ScriptSys::Get()->GetMachine()->AllocTableObject();
|
||||
|
||||
ScriptSys::Get()->GetMachine()->AddCPPOwnedGMObject(m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
ScriptObj::~ScriptObj()
|
||||
{
|
||||
// Stop related threads
|
||||
KillThreads();
|
||||
|
||||
if(m_userObject)
|
||||
{
|
||||
// Nullify script link to C object
|
||||
m_userObject->m_user = NULL;
|
||||
}
|
||||
|
||||
// Destruct the gmObjects
|
||||
#if GM_USE_INCGC
|
||||
// Do nothing, it will be collected later, just nullify all reference to it
|
||||
#else
|
||||
if(m_userObject)
|
||||
{
|
||||
m_userObject->Destruct(ScriptSys::Get()->GetMachine());
|
||||
}
|
||||
m_tableObject->Destruct(ScriptSys::Get()->GetMachine());
|
||||
#endif
|
||||
|
||||
// Remove object from the list of all objects
|
||||
ScriptSys::Get()->GetMachine()->RemoveCPPOwnedGMObject(m_tableObject);
|
||||
if( m_userObject )
|
||||
{
|
||||
ScriptSys::Get()->GetMachine()->RemoveCPPOwnedGMObject(m_userObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gmUserObject* ScriptObj::GetUserObject()
|
||||
{
|
||||
if(!m_userObject)
|
||||
{
|
||||
m_userObject = ScriptSys::Get()->GetMachine()->AllocUserObject(this, GMTYPE_GAMEOBJ);
|
||||
ScriptSys::Get()->GetMachine()->AddCPPOwnedGMObject(m_userObject);
|
||||
}
|
||||
|
||||
return m_userObject;
|
||||
}
|
||||
|
||||
|
||||
void ScriptObj::KillThreads()
|
||||
{
|
||||
for(unsigned int tIndex=0; tIndex<m_threads.GetSize(); ++tIndex)
|
||||
{
|
||||
ScriptSys::Get()->RemoveThreadIdButDontTouchGameObj(m_threads[tIndex]);
|
||||
ScriptSys::Get()->GetMachine()->KillThread(m_threads[tIndex]);
|
||||
}
|
||||
m_threads.Reset();
|
||||
}
|
||||
|
||||
|
||||
void ScriptObj::ExecuteStringOnThis(const char* a_string)
|
||||
{
|
||||
gmMachine* machine = ScriptSys::Get()->GetMachine();
|
||||
gmVariable thisVar;
|
||||
thisVar.SetUser(GetUserObject());
|
||||
int threadId = GM_INVALID_THREAD;
|
||||
|
||||
int errors = machine->ExecuteString(a_string, &threadId, true, NULL, &thisVar);
|
||||
if(errors)
|
||||
{
|
||||
bool first = true;
|
||||
const char * message;
|
||||
while((message = machine->GetLog().GetEntry(first)))
|
||||
{
|
||||
ScriptSys::Get()->LogError("%s\n", message);
|
||||
}
|
||||
machine->GetLog().Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptSys::Get()->AssociateThreadIdWithGameObj(threadId, *GetGameObj());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ScriptObj::ExecuteGlobalFunctionOnThis(const char* a_functionName)
|
||||
{
|
||||
gmVariable thisVar;
|
||||
thisVar.SetUser(GetUserObject());
|
||||
|
||||
gmCall call;
|
||||
if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), a_functionName, thisVar, false))
|
||||
{
|
||||
call.End();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void ScriptObj::SetMemberInt(const char* a_memberName, int a_int)
|
||||
{
|
||||
ScriptSys::Get()->SetTableInt(a_memberName, a_int, m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
void ScriptObj::SetMemberFloat(const char* a_memberName, float a_float)
|
||||
{
|
||||
ScriptSys::Get()->SetTableFloat(a_memberName, a_float, m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
void ScriptObj::SetMemberString(const char* a_memberName, const char* a_string, int a_strLength)
|
||||
{
|
||||
ScriptSys::Get()->SetTableString(a_memberName, a_string, a_strLength, m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
void ScriptObj::SetMemberGameObj(const char* a_memberName, GameObj* a_gameObj)
|
||||
{
|
||||
ScriptSys::Get()->SetTableGameObj(a_memberName, a_gameObj, m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
gmTableObject* ScriptObj::SetMemberTable(const char* a_memberName)
|
||||
{
|
||||
return ScriptSys::Get()->SetTableTable(a_memberName, m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
bool ScriptObj::GetMemberInt(const char* a_memberName, int& a_int)
|
||||
{
|
||||
return ScriptSys::Get()->GetTableInt(a_memberName, a_int, m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
bool ScriptObj::GetMemberFloat(const char* a_memberName, float& a_float)
|
||||
{
|
||||
return ScriptSys::Get()->GetTableFloat(a_memberName, a_float, m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
bool ScriptObj::GetMemberString(const char* a_memberName, String& a_string)
|
||||
{
|
||||
return ScriptSys::Get()->GetTableString(a_memberName, a_string, m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
bool ScriptObj::GetMemberGameObj(const char* a_memberName, GameObj*& a_gameObj)
|
||||
{
|
||||
return ScriptSys::Get()->GetTableGameObj(a_memberName, a_gameObj, m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
bool ScriptObj::GetMemberTable(const char* a_memberName, gmTableObject*& a_retTable)
|
||||
{
|
||||
return ScriptSys::Get()->GetTableTable(a_memberName, a_retTable, m_tableObject);
|
||||
}
|
||||
|
||||
|
||||
void GM_CDECL ScriptObj::GameObjCallback_AsString(gmUserObject * a_object, char* a_buffer, int a_bufferLen)
|
||||
{
|
||||
char mixBuffer[128];
|
||||
|
||||
ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
|
||||
GameObj* gameObjPtr = NULL;
|
||||
|
||||
if(scriptObj)
|
||||
{
|
||||
gameObjPtr = scriptObj->GetGameObj();
|
||||
}
|
||||
|
||||
sprintf(mixBuffer,"CPtr: %x", gameObjPtr);
|
||||
|
||||
int mixLength = strlen(mixBuffer);
|
||||
int useLength = gmMin(mixLength, a_bufferLen-1);
|
||||
GM_ASSERT(useLength > 0);
|
||||
strncpy(a_buffer, mixBuffer, useLength);
|
||||
a_buffer[useLength] = 0;
|
||||
}
|
||||
|
||||
|
||||
#if GM_USE_INCGC
|
||||
|
||||
bool GM_CDECL ScriptObj::GameObjCallback_GCTrace(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workRemaining, int& a_workDone)
|
||||
{
|
||||
GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
|
||||
ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
|
||||
|
||||
if(scriptObj)
|
||||
{
|
||||
a_gc->GetNextObject(scriptObj->GetTableObject());
|
||||
}
|
||||
a_workDone +=2;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void GM_CDECL ScriptObj::GameObjCallback_GCDestruct(gmMachine * a_machine, gmUserObject * a_object)
|
||||
{
|
||||
GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
|
||||
ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
|
||||
|
||||
if(scriptObj)
|
||||
{
|
||||
scriptObj->m_userObject = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#else //GM_USE_INCGC
|
||||
|
||||
void GM_CDECL ScriptObj::GameObjCallback_GCMark(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
|
||||
{
|
||||
GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
|
||||
ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
|
||||
|
||||
if(scriptObj)
|
||||
{
|
||||
if(scriptObj->GetTableObject()->NeedsMark(a_mark))
|
||||
{
|
||||
scriptObj->GetTableObject()->Mark(a_machine, a_mark);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GM_CDECL ScriptObj::GameObjCallback_GCCollect(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
|
||||
{
|
||||
GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
|
||||
ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
|
||||
|
||||
if(scriptObj)
|
||||
{
|
||||
scriptObj->m_userObject = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //GM_USE_INCGC
|
||||
|
||||
|
||||
// NOTE: If you wanted to enable other dot operator behavior
|
||||
// here is the place to do it, in the GetDot and SetDot operators.
|
||||
// This example merely uses the gmTable embedded in the GameObj
|
||||
// to allow script functions and data to be members of this object type.
|
||||
// GameObj also registers 'type' functions that are accessed via the
|
||||
// dot operator.
|
||||
|
||||
|
||||
void GM_CDECL ScriptObj::GameObj_GetDot(gmThread * a_thread, gmVariable * a_operands)
|
||||
{
|
||||
//O_GETDOT = 0, // object, "member" (tos is a_operands + 2)
|
||||
GM_ASSERT(a_operands[0].m_type == GMTYPE_GAMEOBJ);
|
||||
|
||||
gmUserObject* userObj = (gmUserObject*) GM_OBJECT(a_operands[0].m_value.m_ref);
|
||||
ScriptObj* scriptObj = (ScriptObj*)userObj->m_user;
|
||||
|
||||
if(!scriptObj)
|
||||
{
|
||||
a_operands[0].Nullify();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
a_operands[0] = scriptObj->GetTableObject()->Get(a_operands[1]);
|
||||
}
|
||||
|
||||
|
||||
void GM_CDECL ScriptObj::GameObj_SetDot(gmThread * a_thread, gmVariable * a_operands)
|
||||
{
|
||||
//O_SETDOT, // object, value, "member" (tos is a_operands + 3)
|
||||
GM_ASSERT(a_operands[0].m_type == GMTYPE_GAMEOBJ);
|
||||
|
||||
gmUserObject* userObj = (gmUserObject*) GM_OBJECT(a_operands[0].m_value.m_ref);
|
||||
ScriptObj* scriptObj = (ScriptObj*)userObj->m_user;
|
||||
|
||||
if(scriptObj)
|
||||
{
|
||||
scriptObj->GetTableObject()->Set(a_thread->GetMachine(), a_operands[2], a_operands[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScriptObj::RegisterScriptBindings()
|
||||
{
|
||||
gmMachine* machine = ScriptSys::Get()->GetMachine();
|
||||
|
||||
GM_ASSERT(machine);
|
||||
|
||||
// Register new user type
|
||||
GMTYPE_GAMEOBJ = machine->CreateUserType("GameObj");
|
||||
// Register garbage collection for our new type
|
||||
#if GM_USE_INCGC
|
||||
machine->RegisterUserCallbacks(GMTYPE_GAMEOBJ, GameObjCallback_GCTrace, GameObjCallback_GCDestruct, GameObjCallback_AsString);
|
||||
#else //GM_USE_INCGC
|
||||
machine->RegisterUserCallbacks(GMTYPE_GAMEOBJ, GameObjCallback_GCMark, GameObjCallback_GCCollect, GameObjCallback_AsString);
|
||||
#endif //GM_USE_INCGC
|
||||
// Bind Get dot operator for our type
|
||||
machine->RegisterTypeOperator(GMTYPE_GAMEOBJ, O_GETDOT, NULL, GameObj_GetDot);
|
||||
// Bind Set dot operator for our type
|
||||
machine->RegisterTypeOperator(GMTYPE_GAMEOBJ, O_SETDOT, NULL, GameObj_SetDot);
|
||||
// Bind functions
|
||||
// machine->RegisterLibrary(regFuncList, sizeof(regFuncList) / sizeof(regFuncList[0]));
|
||||
// Bind type functions
|
||||
// machine->RegisterTypeLibrary(GM_GOB, regTypeFuncList, sizeof(regTypeFuncList) / sizeof(regTypeFuncList[0]));
|
||||
}
|
||||
165
vscript/languages/gm/src/examples/GameObject/ScriptObj.h
Normal file
165
vscript/languages/gm/src/examples/GameObject/ScriptObj.h
Normal file
@@ -0,0 +1,165 @@
|
||||
#ifndef SCRIPTOBJ_H
|
||||
#define SCRIPTOBJ_H
|
||||
|
||||
//
|
||||
// ScriptObj.h
|
||||
//
|
||||
// Example script interface component for game object
|
||||
//
|
||||
|
||||
#include "gmThread.h"
|
||||
#include "StdStuff.h"
|
||||
|
||||
// Fwd decls
|
||||
class GameObj;
|
||||
|
||||
|
||||
// NOTE: In this implementation, the 'gmUserObject' only exists when the cpp object is used or needed by script.
|
||||
// This implementation also shares that single user object amongst all referencing variables in script.
|
||||
// Because of this, the cpp code does not need to handle the user object as if it were owned by cpp.
|
||||
// The cpp object does however always contain a gmTableObject, and this is owned by cpp as it may not
|
||||
// exist (be referenced) within the script. For this reason, the gmTableObject must be handled as a
|
||||
// cpp owned object to allow correct GC handling.
|
||||
//
|
||||
// An alternate method, would be to always have a gmUserObject and let cpp code own this. This
|
||||
// user object would be the root of its own child objects like the gmTableObject. This method may
|
||||
// be simpler.
|
||||
//
|
||||
|
||||
// Script interface for game objects
|
||||
class ScriptObj
|
||||
{
|
||||
public:
|
||||
|
||||
static gmType GMTYPE_GAMEOBJ; ///< The user type of a game object
|
||||
|
||||
static void RegisterScriptBindings(); ///< Register game object script bindings
|
||||
|
||||
ScriptObj(GameObj* a_gameObj);
|
||||
virtual ~ScriptObj();
|
||||
|
||||
GameObj* GetGameObj() { return m_gameObj; }
|
||||
|
||||
gmTableObject* GetTableObject() { return m_tableObject; }
|
||||
gmUserObject* GetUserObject();
|
||||
|
||||
void AddThreadId(int a_threadId)
|
||||
{
|
||||
m_threads.InsertLast(a_threadId);
|
||||
}
|
||||
|
||||
void RemoveThreadId(int a_threadId)
|
||||
{
|
||||
for(unsigned int tIndex=0; tIndex < m_threads.GetSize(); ++tIndex)
|
||||
{
|
||||
if(m_threads[tIndex] == a_threadId)
|
||||
{
|
||||
m_threads.RemoveSwapLast(tIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kill all threads running on this object
|
||||
void KillThreads();
|
||||
|
||||
void ExecuteStringOnThis(const char* a_string);
|
||||
|
||||
bool ExecuteGlobalFunctionOnThis(const char* a_functionName);
|
||||
|
||||
void SetMemberInt(const char* a_memberName, int a_int);
|
||||
void SetMemberFloat(const char* a_memberName, float a_float);
|
||||
void SetMemberString(const char* a_memberName, const char* a_string, int a_strLength = -1);
|
||||
void SetMemberGameObj(const char* a_memberName, GameObj* a_gameObj);
|
||||
gmTableObject* SetMemberTable(const char* a_memberName);
|
||||
|
||||
bool GetMemberInt(const char* a_memberName, int& a_int);
|
||||
bool GetMemberFloat(const char* a_memberName, float& a_float);
|
||||
bool GetMemberString(const char* a_memberName, String& a_string);
|
||||
bool GetMemberGameObj(const char* a_memberName, GameObj*& a_gameObj);
|
||||
bool GetMemberTable(const char* a_memberName, gmTableObject*& a_retTable);
|
||||
|
||||
protected:
|
||||
|
||||
static void GM_CDECL GameObjCallback_AsString(gmUserObject * a_object, char* a_buffer, int a_bufferLen);
|
||||
#if GM_USE_INCGC
|
||||
static bool GM_CDECL GameObjCallback_GCTrace(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workRemaining, int& a_workDone);
|
||||
static void GM_CDECL GameObjCallback_GCDestruct(gmMachine * a_machine, gmUserObject * a_object);
|
||||
#else //GM_USE_INCGC
|
||||
static void GM_CDECL GameObjCallback_GCMark(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark);
|
||||
static void GM_CDECL GameObjCallback_GCCollect(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark);
|
||||
#endif //GM_USE_INCGC
|
||||
static void GM_CDECL GameObj_GetDot(gmThread * a_thread, gmVariable * a_operands);
|
||||
static void GM_CDECL GameObj_SetDot(gmThread * a_thread, gmVariable * a_operands);
|
||||
|
||||
GameObj* m_gameObj; ///< The game object owner of this interface
|
||||
gmUserObject* m_userObject; ///< The script object
|
||||
gmTableObject* m_tableObject; ///< Table functionality for script object members
|
||||
gmArraySimple<int> m_threads; ///< Threads associated with this game object
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
/// \brief Get 'this' as GameObj of TYPE
|
||||
/// Eg. Soldier* obj = GetThisGameObj<Soldier>(a_thread);
|
||||
template<class TYPE>
|
||||
TYPE* GetThisGameObj(gmThread* a_thread)
|
||||
{
|
||||
GM_ASSERT(a_thread->GetThis()->m_type == ScriptObj::GMTYPE_GAMEOBJ); //Paranoid check for type function
|
||||
|
||||
ScriptObj* scriptObj = (ScriptObj*)a_thread->ThisUser();
|
||||
|
||||
CHECK(scriptObj); //Check for null GameObj ptr
|
||||
|
||||
// You can check for valid derived type here
|
||||
|
||||
return static_cast<TYPE*>(scriptObj->GetGameObj());
|
||||
}
|
||||
|
||||
|
||||
/// \brief Get param as GameObj of TYPE
|
||||
/// Eg. Soldier* obj = GetGameObjParam<Soldier>(a_thread, 0);
|
||||
template<class TYPE>
|
||||
TYPE* GetGameObjParam(gmThread* a_thread, int a_paramIndex)
|
||||
{
|
||||
ScriptObj* scriptObj = (ScriptObj*)a_thread->ParamUserCheckType(a_paramIndex, ScriptObj::GMTYPE_GAMEOBJ);
|
||||
|
||||
CHECK(scriptObj); //Check for null GameObj ptr
|
||||
|
||||
// You can check for valid derived type here
|
||||
|
||||
return static_cast<TYPE*>(scriptObj->GetGameObj());
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/// \brief Get 'this' as GameObj of TYPE
|
||||
inline GameObj* GetThisGameObj(gmThread* a_thread)
|
||||
{
|
||||
GM_ASSERT(a_thread->GetThis()->m_type == ScriptObj::GMTYPE_GAMEOBJ); //Paranoid check for type function
|
||||
|
||||
ScriptObj* scriptObj = (ScriptObj*)a_thread->ThisUser();
|
||||
|
||||
if(!scriptObj)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return scriptObj->GetGameObj();
|
||||
}
|
||||
|
||||
|
||||
/// \brief Get param as GameObj of TYPE
|
||||
inline GameObj* GetGameObjParam(gmThread* a_thread, int a_paramIndex)
|
||||
{
|
||||
ScriptObj* scriptObj = (ScriptObj*)a_thread->ParamUserCheckType(a_paramIndex, ScriptObj::GMTYPE_GAMEOBJ);
|
||||
|
||||
if(!scriptObj)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return scriptObj->GetGameObj();
|
||||
}
|
||||
|
||||
|
||||
#endif //SCRIPTOBJ_H
|
||||
432
vscript/languages/gm/src/examples/GameObject/ScriptSys.cpp
Normal file
432
vscript/languages/gm/src/examples/GameObject/ScriptSys.cpp
Normal file
@@ -0,0 +1,432 @@
|
||||
//
|
||||
// ScriptSys.cpp
|
||||
//
|
||||
|
||||
#include "gmCall.h"
|
||||
#include "ScriptSys.h"
|
||||
#include "ScriptObj.h"
|
||||
#include "GameObj.h"
|
||||
|
||||
|
||||
// Init statics and constants
|
||||
ScriptSys* ScriptSys::s_instance = NULL;
|
||||
const int ScriptSys::DEBUGGER_DEFAULT_PORT = 49001;
|
||||
const char* ScriptSys::DEBUGGER_DEFAULT_IP = "127.0.0.1"; // localhost
|
||||
|
||||
|
||||
void ScriptSys::Init()
|
||||
{
|
||||
GM_ASSERT( !s_instance ); // Just have one instance for this example
|
||||
|
||||
s_instance = new ScriptSys;
|
||||
|
||||
// Register Game Object type and bindings
|
||||
ScriptObj::RegisterScriptBindings();
|
||||
GameObj::RegisterScriptBindings();
|
||||
}
|
||||
|
||||
|
||||
void ScriptSys::Destroy()
|
||||
{
|
||||
delete s_instance;
|
||||
s_instance = NULL;
|
||||
}
|
||||
|
||||
|
||||
void ScriptSys::DebuggerSendMessage(gmDebugSession * a_session, const void * a_command, int a_len)
|
||||
{
|
||||
nClient * client = (nClient *) a_session->m_user;
|
||||
client->SendMessage((const char *) a_command, a_len);
|
||||
}
|
||||
|
||||
|
||||
const void* ScriptSys::DebuggerPumpMessage(gmDebugSession * a_session, int &a_len)
|
||||
{
|
||||
nClient * client = (nClient *) a_session->m_user;
|
||||
return client->PumpMessage(a_len);
|
||||
}
|
||||
|
||||
|
||||
ScriptSys::ScriptSys()
|
||||
{
|
||||
m_machine = new gmMachine;
|
||||
|
||||
//Set machine callbacks
|
||||
gmMachine::s_machineCallback = ScriptSysCallback_Machine;
|
||||
gmMachine::s_printCallback = ScriptSysCallback_Print;
|
||||
|
||||
// Init debugger
|
||||
gmBindDebugLib(m_machine); // Register debugging library
|
||||
|
||||
m_debuggerIP = DEBUGGER_DEFAULT_IP;
|
||||
m_debuggerPort = DEBUGGER_DEFAULT_PORT;
|
||||
m_debugSession.m_sendMessage = DebuggerSendMessage;
|
||||
m_debugSession.m_pumpMessage = DebuggerPumpMessage;
|
||||
m_debugSession.m_user = &m_debugClient;
|
||||
|
||||
if(m_debugClient.Connect(m_debuggerIP, ((short) m_debuggerPort)))
|
||||
{
|
||||
m_debugSession.Open(m_machine);
|
||||
fprintf(stderr, "Debug session opened"GM_NL);
|
||||
}
|
||||
m_machine->SetDebugMode(true);
|
||||
}
|
||||
|
||||
|
||||
ScriptSys::~ScriptSys()
|
||||
{
|
||||
// End debugger session if any
|
||||
m_debugSession.Close();
|
||||
m_debugClient.Close();
|
||||
|
||||
// For debugging
|
||||
_gmDumpLeaks();
|
||||
|
||||
delete m_machine;
|
||||
}
|
||||
|
||||
|
||||
void __cdecl ScriptSys::LogError(const char *a_str, ...)
|
||||
{
|
||||
// WARNING This is not safe for longer strings, should use non-ansi vsprintnf, string type, or similar.
|
||||
const int MAX_CHARS = 512;
|
||||
char buffer[MAX_CHARS];
|
||||
|
||||
va_list args;
|
||||
va_start(args, a_str);
|
||||
|
||||
vsprintf(buffer, a_str, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
fprintf(stderr, "ERROR: %s", a_str);
|
||||
}
|
||||
|
||||
|
||||
GameObj* ScriptSys::GetGameObjFromThreadId(int a_threadId)
|
||||
{
|
||||
ScriptObj* scriptObj;
|
||||
|
||||
if(m_mapThreadGameObjs.GetAt(a_threadId, scriptObj))
|
||||
{
|
||||
return scriptObj->GetGameObj();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void ScriptSys::AssociateThreadIdWithGameObj(int a_threadId, GameObj& a_gameObj)
|
||||
{
|
||||
ScriptObj* scriptObj = a_gameObj.GetScriptObj();
|
||||
|
||||
scriptObj->AddThreadId(a_threadId);
|
||||
m_mapThreadGameObjs.SetAt(a_threadId, scriptObj);
|
||||
}
|
||||
|
||||
|
||||
void ScriptSys::DisassociateThreadIdWithGameObj(int a_threadId)
|
||||
{
|
||||
ScriptObj* scriptObj;
|
||||
|
||||
if(m_mapThreadGameObjs.RemoveAt(a_threadId, scriptObj))
|
||||
{
|
||||
scriptObj->RemoveThreadId(a_threadId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScriptSys::RemoveThreadIdButDontTouchGameObj(int a_threadId)
|
||||
{
|
||||
m_mapThreadGameObjs.RemoveAt(a_threadId);
|
||||
}
|
||||
|
||||
|
||||
void ScriptSys::SetTableNull(const char* a_memberName, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
gmTableObject* table = a_table;
|
||||
gmVariable newVar;
|
||||
newVar.Nullify();
|
||||
table->Set(m_machine, a_memberName, newVar);
|
||||
}
|
||||
|
||||
|
||||
void ScriptSys::SetTableInt(const char* a_memberName, int a_int, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
gmTableObject* table = a_table;
|
||||
gmVariable newVar;
|
||||
newVar.SetInt(a_int);
|
||||
table->Set(m_machine, a_memberName, newVar);
|
||||
}
|
||||
|
||||
|
||||
void ScriptSys::SetTableFloat(const char* a_memberName, float a_float, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
gmTableObject* table = a_table;
|
||||
gmVariable newVar;
|
||||
newVar.SetFloat(a_float);
|
||||
table->Set(m_machine, a_memberName, newVar);
|
||||
}
|
||||
|
||||
|
||||
void ScriptSys::SetTableString(const char* a_memberName, const char* a_string, int a_strLength, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
gmTableObject* table = a_table;
|
||||
gmVariable newVar;
|
||||
newVar.SetString(m_machine->AllocStringObject(a_string, a_strLength));
|
||||
table->Set(m_machine, a_memberName, newVar);
|
||||
}
|
||||
|
||||
void ScriptSys::SetTableGameObj(const char* a_memberName, GameObj* a_gameObj, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
GM_ASSERT(a_gameObj);
|
||||
gmTableObject* table = a_table;
|
||||
gmVariable newVar;
|
||||
newVar.SetUser(a_gameObj->GetScriptObj()->GetUserObject());
|
||||
table->Set(m_machine, a_memberName, newVar);
|
||||
}
|
||||
|
||||
|
||||
gmTableObject* ScriptSys::SetTableTable(const char* a_memberName, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
gmTableObject* table = a_table;
|
||||
gmMachine* machine = m_machine;
|
||||
gmVariable newVar;
|
||||
gmTableObject* newTable = machine->AllocTableObject();
|
||||
newVar.SetTable(newTable);
|
||||
table->Set(machine, a_memberName, newVar);
|
||||
return newTable; //Return the table so we can potentially put things in it
|
||||
}
|
||||
|
||||
|
||||
bool ScriptSys::GetTableInt(const char* a_memberName, int& a_int, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
gmTableObject* table = a_table;
|
||||
gmVariable stringName;
|
||||
gmVariable retVar;
|
||||
|
||||
stringName.SetString(m_machine->AllocStringObject(a_memberName));
|
||||
retVar = table->Get(stringName);
|
||||
if(retVar.m_type == GM_INT)
|
||||
{
|
||||
a_int = retVar.m_value.m_int;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ScriptSys::GetTableFloat(const char* a_memberName, float& a_float, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
gmTableObject* table = a_table;
|
||||
gmVariable stringName;
|
||||
gmVariable retVar;
|
||||
|
||||
stringName.SetString(m_machine->AllocStringObject(a_memberName));
|
||||
retVar = table->Get(stringName);
|
||||
if(retVar.m_type == GM_FLOAT)
|
||||
{
|
||||
a_float = retVar.m_value.m_float;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ScriptSys::GetTableString(const char* a_memberName, String& a_string, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
gmTableObject* table = a_table;
|
||||
gmVariable stringName;
|
||||
gmVariable retVar;
|
||||
|
||||
stringName.SetString(m_machine->AllocStringObject(a_memberName));
|
||||
retVar = table->Get(stringName);
|
||||
if(retVar.m_type == GM_STRING)
|
||||
{
|
||||
gmStringObject* stringObj = (gmStringObject*)GM_MOBJECT(m_machine, retVar.m_value.m_ref);
|
||||
a_string = stringObj->GetString();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ScriptSys::GetTableGameObj(const char* a_memberName, GameObj*& a_gameObj, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
gmTableObject* table = a_table;
|
||||
gmVariable stringName;
|
||||
gmVariable retVar;
|
||||
|
||||
stringName.SetString(m_machine->AllocStringObject(a_memberName));
|
||||
retVar = table->Get(stringName);
|
||||
if(retVar.m_type == ScriptObj::GMTYPE_GAMEOBJ)
|
||||
{
|
||||
gmUserObject* userObj = (gmUserObject*)GM_MOBJECT(m_machine, retVar.m_value.m_ref);
|
||||
a_gameObj = ((ScriptObj*)userObj->m_user)->GetGameObj();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ScriptSys::GetTableTable(const char* a_memberName, gmTableObject*& a_retTable, gmTableObject* a_table)
|
||||
{
|
||||
GM_ASSERT(a_table);
|
||||
gmTableObject* table = a_table;
|
||||
gmVariable stringName;
|
||||
gmVariable retVar;
|
||||
|
||||
stringName.SetString(m_machine->AllocStringObject(a_memberName));
|
||||
retVar = table->Get(stringName);
|
||||
if(retVar.m_type == GM_TABLE)
|
||||
{
|
||||
a_retTable = (gmTableObject*)GM_MOBJECT(m_machine, retVar.m_value.m_ref);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void GM_CDECL ScriptSys::ScriptSysCallback_Print(gmMachine* a_machine, const char* a_string)
|
||||
{
|
||||
printf("%s\n", a_string);
|
||||
}
|
||||
|
||||
|
||||
bool GM_CDECL ScriptSys::ScriptSysCallback_Machine(gmMachine* a_machine, gmMachineCommand a_command, const void* a_context)
|
||||
{
|
||||
switch(a_command)
|
||||
{
|
||||
case MC_THREAD_EXCEPTION:
|
||||
{
|
||||
ScriptSys::Get()->LogAnyMachineErrorMessages();
|
||||
break;
|
||||
}
|
||||
case MC_COLLECT_GARBAGE:
|
||||
{
|
||||
/* // Old code
|
||||
#if GM_USE_INCGC
|
||||
gmGarbageCollector* gc = a_machine->GetGC();
|
||||
|
||||
for(unsigned int objIndex = 0; objIndex<ScriptSys::Get()->m_allScriptObjs.Count(); ++objIndex)
|
||||
{
|
||||
ScriptObj* scriptObj = ScriptSys::Get()->m_allScriptObjs[objIndex];
|
||||
gc->GetNextObject(scriptObj->GetTableObject());
|
||||
}
|
||||
#else //GM_USE_INCGC
|
||||
gmuint32 mark = *(gmuint32*)a_context;
|
||||
|
||||
for(unsigned int objIndex = 0; objIndex<ScriptSys::Get()->m_allScriptObjs.Count(); ++objIndex)
|
||||
{
|
||||
ScriptObj* scriptObj = ScriptSys::Get()->m_allScriptObjs[objIndex];
|
||||
|
||||
if(scriptObj->GetTableObject()->NeedsMark(mark))
|
||||
{
|
||||
scriptObj->GetTableObject()->Mark(a_machine, mark);
|
||||
}
|
||||
}
|
||||
#endif //GM_USE_INCGC
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case MC_THREAD_CREATE: // Called when a thread is created. a_context is the thread.
|
||||
{
|
||||
break;
|
||||
}
|
||||
case MC_THREAD_DESTROY: // Called when a thread is destroyed. a_context is the thread that is about to die
|
||||
{
|
||||
gmThread* thread = (gmThread*)a_context;
|
||||
ScriptSys::Get()->DisassociateThreadIdWithGameObj(thread->GetId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ScriptSys::ExecuteFile(const char* a_fileName)
|
||||
{
|
||||
FILE* scriptFile = NULL;
|
||||
char* fileString = NULL;
|
||||
int fileSize = 0;
|
||||
|
||||
GM_ASSERT(m_machine);
|
||||
|
||||
if( !(scriptFile = fopen(a_fileName, "rb")) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(scriptFile, 0, SEEK_END);
|
||||
fileSize = ftell(scriptFile);
|
||||
fseek(scriptFile, 0, SEEK_SET);
|
||||
fileString = new char [fileSize+1];
|
||||
fread(fileString, fileSize, 1, scriptFile);
|
||||
fileString[fileSize] = 0; // Terminating null
|
||||
fclose(scriptFile);
|
||||
|
||||
int threadId = GM_INVALID_THREAD;
|
||||
int errors = m_machine->ExecuteString(fileString, &threadId, true, a_fileName);
|
||||
if(errors)
|
||||
{
|
||||
LogAnyMachineErrorMessages();
|
||||
}
|
||||
|
||||
delete [] fileString;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ScriptSys::ExecuteString(const char* a_string)
|
||||
{
|
||||
GM_ASSERT(m_machine);
|
||||
|
||||
int threadId = GM_INVALID_THREAD;
|
||||
int errors = m_machine->ExecuteString(a_string, &threadId, true);
|
||||
if (errors)
|
||||
{
|
||||
LogAnyMachineErrorMessages();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int ScriptSys::Execute(unsigned int a_deltaTimeMS)
|
||||
{
|
||||
int numThreads = m_machine->Execute(a_deltaTimeMS);
|
||||
|
||||
if(m_debugClient.IsConnected())
|
||||
{
|
||||
m_debugSession.Update();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_debugSession.Close();
|
||||
}
|
||||
|
||||
return numThreads;
|
||||
}
|
||||
|
||||
|
||||
void ScriptSys::LogAnyMachineErrorMessages()
|
||||
{
|
||||
bool first = true;
|
||||
const char * message;
|
||||
while((message = m_machine->GetLog().GetEntry(first)))
|
||||
{
|
||||
LogError("%s"GM_NL, message);
|
||||
}
|
||||
m_machine->GetLog().Reset();
|
||||
}
|
||||
107
vscript/languages/gm/src/examples/GameObject/ScriptSys.h
Normal file
107
vscript/languages/gm/src/examples/GameObject/ScriptSys.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#ifndef SCRIPTSYS_H
|
||||
#define SCRIPTSYS_H
|
||||
|
||||
//
|
||||
// ScriptSys.h
|
||||
//
|
||||
// Example script system to support scriptable game objects
|
||||
//
|
||||
|
||||
#include "gmThread.h"
|
||||
#include "gmDebug.h"
|
||||
#include "gmArraySimple.h"
|
||||
#include "StdStuff.h"
|
||||
#include "NetClient.h"
|
||||
|
||||
// Fwd decls
|
||||
class GameObj;
|
||||
class ScriptObj;
|
||||
|
||||
// Script system to support and control the virtual machine
|
||||
class ScriptSys
|
||||
{
|
||||
public:
|
||||
|
||||
/// Access this system from anywhere once it has been initialized for convenience
|
||||
static ScriptSys* Get() { return s_instance; }
|
||||
|
||||
ScriptSys();
|
||||
virtual ~ScriptSys();
|
||||
|
||||
/// Get the GM machine
|
||||
gmMachine* GetMachine() { return m_machine; }
|
||||
|
||||
/// Set bindings and Init constant strings
|
||||
static void Init();
|
||||
/// Clean out this structure
|
||||
static void Destroy();
|
||||
|
||||
|
||||
/// Log an error message
|
||||
void __cdecl LogError(const char *a_str, ...);
|
||||
|
||||
/// Log any machine error messages that may be waiting
|
||||
void LogAnyMachineErrorMessages();
|
||||
|
||||
/// Get GameObj that was associated with a thread Id.
|
||||
GameObj* GetGameObjFromThreadId(int a_threadId);
|
||||
|
||||
/// Associate a threadId with a GameObj, logically as a primary thread.
|
||||
void AssociateThreadIdWithGameObj(int a_threadId, GameObj& a_gameObj);
|
||||
/// Disassociate a threadId with a GameObj.
|
||||
void DisassociateThreadIdWithGameObj(int a_threadId);
|
||||
/// Remove the thread Id association, but don't modify the GameObj.
|
||||
/// This can be used internally by GameObj to perform iteration and removal.
|
||||
void RemoveThreadIdButDontTouchGameObj(int a_threadId);
|
||||
|
||||
/// Run a script file
|
||||
bool ExecuteFile(const char* a_fileName);
|
||||
/// Executes a string.
|
||||
bool ExecuteString(const char* a_str);
|
||||
|
||||
/// Update the virtual machine.
|
||||
int Execute(unsigned int a_deltaTimeMS);
|
||||
|
||||
void SetTableNull(const char* a_memberName, gmTableObject* a_table);
|
||||
void SetTableInt(const char* a_memberName, int a_int, gmTableObject* a_table);
|
||||
void SetTableFloat(const char* a_memberName, float a_float, gmTableObject* a_table);
|
||||
void SetTableString(const char* a_memberName, const char* a_string, int a_strLength, gmTableObject* a_table);
|
||||
void SetTableGameObj(const char* a_memberName, GameObj* a_gameObj, gmTableObject* a_table);
|
||||
gmTableObject* SetTableTable(const char* a_memberName, gmTableObject* a_table);
|
||||
|
||||
bool GetTableInt(const char* a_memberName, int& a_int, gmTableObject* a_table);
|
||||
bool GetTableFloat(const char* a_memberName, float& a_float, gmTableObject* a_table);
|
||||
bool GetTableString(const char* a_memberName, String& a_string, gmTableObject* a_table);
|
||||
bool GetTableGameObj(const char* a_memberName, GameObj*& a_gameObj, gmTableObject* a_table);
|
||||
bool GetTableTable(const char* a_memberName, gmTableObject*& a_retTable, gmTableObject* a_table);
|
||||
|
||||
protected:
|
||||
|
||||
/// Machine 'print' binding callback
|
||||
static void GM_CDECL ScriptSysCallback_Print(gmMachine* a_machine, const char* a_string);
|
||||
/// Machine general and exception callback
|
||||
static bool GM_CDECL ScriptSysCallback_Machine(gmMachine* a_machine, gmMachineCommand a_command, const void* a_context);
|
||||
|
||||
/// Debugging support Send a message
|
||||
static void DebuggerSendMessage(gmDebugSession * a_session, const void * a_command, int a_len);
|
||||
/// Debugging support Pump a message
|
||||
static const void* DebuggerPumpMessage(gmDebugSession * a_session, int &a_len);
|
||||
|
||||
gmMachine* m_machine; ///< GM machine instance
|
||||
Map<int, ScriptObj*> m_mapThreadGameObjs; ///< Map script threadId to game object
|
||||
/* // Old code
|
||||
gmArraySimple<ScriptObj*> m_allScriptObjs; ///< All the script objects, for garbage collection handling
|
||||
*/
|
||||
|
||||
nClient m_debugClient; ///< Debugger network client
|
||||
gmDebugSession m_debugSession; ///< Debugger session
|
||||
const char* m_debuggerIP; ///< Debugger IP
|
||||
int m_debuggerPort; ///< Debugger port
|
||||
|
||||
static const int DEBUGGER_DEFAULT_PORT; ///< Debugger port number
|
||||
static const char* DEBUGGER_DEFAULT_IP; ///< Debugger port number
|
||||
static ScriptSys* s_instance; ///< Static instance for convenience
|
||||
};
|
||||
|
||||
|
||||
#endif //SCRIPTSYS_H
|
||||
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// StdStuff.cpp
|
||||
//
|
||||
|
||||
#include "StdStuff.h"
|
||||
194
vscript/languages/gm/src/examples/GameObject/StdStuff.h
Normal file
194
vscript/languages/gm/src/examples/GameObject/StdStuff.h
Normal file
@@ -0,0 +1,194 @@
|
||||
#ifndef STDSTUFF_H
|
||||
#define STDSTUFF_H
|
||||
|
||||
//
|
||||
// StdStuff.h
|
||||
//
|
||||
// Merely some containers and standard things you would
|
||||
// find in MFC, STL, or your favourite library/engine.
|
||||
// These were implemented as quickly and minimally as possible
|
||||
// rather than introduce more code or external libraries.
|
||||
//
|
||||
|
||||
#include "gmHash.h"
|
||||
|
||||
// Quick n dirty Map using the available hash table
|
||||
template<class KEY, class VALUE>
|
||||
class Map
|
||||
{
|
||||
public:
|
||||
|
||||
Map()
|
||||
: m_hashTable(1024) // Just an arbitrary number at present
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Map()
|
||||
{
|
||||
m_hashTable.RemoveAndDeleteAll();
|
||||
}
|
||||
|
||||
/// \brief Insert a element associated with a key.
|
||||
/// \param a_key Key to identify data.
|
||||
/// \param a_value Data associated with Key.
|
||||
void SetAt(const KEY& a_key, const VALUE& a_value)
|
||||
{
|
||||
HashNode* node = m_hashTable.Find(a_key);
|
||||
if(node)
|
||||
{
|
||||
node->m_value = a_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = new HashNode;
|
||||
node->m_key = a_key;
|
||||
node->m_value = a_value;
|
||||
m_hashTable.Insert(node);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Find a node in the map.
|
||||
/// \param a_key Key to identiy element.
|
||||
/// \param a_value Found element returned here.
|
||||
/// \return TRUE if found, FALSE if not in map.
|
||||
bool GetAt(const KEY& a_key, VALUE& a_value)
|
||||
{
|
||||
HashNode* node = m_hashTable.Find(a_key);
|
||||
if(node)
|
||||
{
|
||||
a_value = node->m_value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Remove a node from the map.
|
||||
/// \param a_key Key to identiy element.
|
||||
/// \param a_removedData Found element returned here.
|
||||
/// \return TRUE if found, FALSE if not in map.
|
||||
bool RemoveAt(const KEY& a_key, VALUE& a_removedData)
|
||||
{
|
||||
HashNode* node = m_hashTable.Find(a_key);
|
||||
if(node)
|
||||
{
|
||||
a_removedData = node->m_value;
|
||||
|
||||
m_hashTable.Remove(node);
|
||||
delete node;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Remove a node from the map.
|
||||
/// \param a_key Key to identiy element.
|
||||
/// \return TRUE if found, FALSE if not in map.
|
||||
bool RemoveAt(const KEY& a_key)
|
||||
{
|
||||
HashNode* node = m_hashTable.Find(a_key);
|
||||
if(node)
|
||||
{
|
||||
m_hashTable.Remove(node);
|
||||
delete node;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
struct HashNode : public gmHashNode<KEY, HashNode, HashNode>
|
||||
{
|
||||
VALUE m_value;
|
||||
KEY m_key;
|
||||
|
||||
virtual const KEY& GetKey() const { return m_key; }
|
||||
|
||||
static inline gmuint Hash(const KEY& a_key)
|
||||
{
|
||||
return (unsigned int)a_key;
|
||||
}
|
||||
|
||||
static inline int Compare(const KEY& a_keyA, const KEY& a_keyB)
|
||||
{
|
||||
if(a_keyA < a_keyB)
|
||||
{ return -1; }
|
||||
if(a_keyA > a_keyB)
|
||||
{ return 1; }
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Blocking
|
||||
gmHash<KEY, HashNode, HashNode> m_hashTable;
|
||||
|
||||
};
|
||||
|
||||
|
||||
// The most crap string implementation ever
|
||||
class String
|
||||
{
|
||||
public:
|
||||
|
||||
String()
|
||||
{
|
||||
m_buffer = NULL;
|
||||
SetBuffer("");
|
||||
}
|
||||
|
||||
String(const char* a_newString)
|
||||
{
|
||||
m_buffer = NULL;
|
||||
SetBuffer(a_newString);
|
||||
}
|
||||
|
||||
String(const char* a_newString, const int a_newStringLength)
|
||||
{
|
||||
m_buffer = NULL;
|
||||
SetBuffer(a_newString, a_newStringLength);
|
||||
}
|
||||
|
||||
~String()
|
||||
{
|
||||
delete [] m_buffer;
|
||||
}
|
||||
|
||||
operator const char* () const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
const char* operator = (const char* a_newString)
|
||||
{
|
||||
SetBuffer(a_newString);
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
const char* operator = (const String& a_newString)
|
||||
{
|
||||
SetBuffer(a_newString);
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void SetBuffer(const char* a_newString, const int a_newStringLength)
|
||||
{
|
||||
delete [] m_buffer;
|
||||
m_buffer = new char [a_newStringLength + 1];
|
||||
memcpy(m_buffer, a_newString, a_newStringLength);
|
||||
m_buffer[a_newStringLength] = 0;
|
||||
}
|
||||
|
||||
void SetBuffer(const char* a_newString)
|
||||
{
|
||||
int newStringLength = strlen(a_newString);
|
||||
SetBuffer(a_newString, newStringLength);
|
||||
}
|
||||
|
||||
char * m_buffer;
|
||||
};
|
||||
|
||||
#endif //STDSTUFF_H
|
||||
44
vscript/languages/gm/src/examples/GameObject/TestGameObj.gm
Normal file
44
vscript/languages/gm/src/examples/GameObject/TestGameObj.gm
Normal file
@@ -0,0 +1,44 @@
|
||||
// Just a bunch of tests that don't mean anything at present.
|
||||
|
||||
global WhatsMyName = function()
|
||||
{
|
||||
print("m_name = ", .m_name);
|
||||
print("this = ", this);
|
||||
|
||||
if(.IsValid())
|
||||
{
|
||||
.SetPos(23.0f, 56.0f);
|
||||
print("position(", .GetPosX(), ",", .GetPosY(), ")");
|
||||
|
||||
global g_globalObj = this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
global RunGlobalObject = function ()
|
||||
{
|
||||
g_globalObj:WhatsMyName();
|
||||
};
|
||||
|
||||
|
||||
global ThreadYieldTest = function()
|
||||
{
|
||||
count = 0;
|
||||
while(count < 30)
|
||||
{
|
||||
sleep(0.5f);
|
||||
count += 1;
|
||||
print("count=",count);
|
||||
yield();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Console.SetColor(COLOR_RED, COLOR_BLACK);
|
||||
Console.Print("Red");
|
||||
Console.SetColor(COLOR_GREEN, COLOR_BLACK);
|
||||
Console.Print("Green");
|
||||
Console.Print("\n");
|
||||
Console.SetColor(COLOR_WHITE, COLOR_BLACK);
|
||||
|
||||
print("Script compiled and executed. \n");
|
||||
30
vscript/languages/gm/src/examples/GameObject/main.cpp
Normal file
30
vscript/languages/gm/src/examples/GameObject/main.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <windows.h>
|
||||
#include "StdStuff.h"
|
||||
#include "App.h"
|
||||
|
||||
// Entry point for Win32 app
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
String test1("hello");
|
||||
test1 = "world";
|
||||
const char* huh = test1;
|
||||
|
||||
App app;
|
||||
|
||||
if(!app.Init())
|
||||
{
|
||||
fprintf(stderr,"Failed App::Init()");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while(app.Update())
|
||||
{
|
||||
}
|
||||
|
||||
app.Destroy();
|
||||
|
||||
printf("App finished, press ENTER to exit.");
|
||||
getchar(); // Wait for key press
|
||||
|
||||
return 0;
|
||||
}
|
||||
362
vscript/languages/gm/src/examples/Minimal/Minimal.dsp
Normal file
362
vscript/languages/gm/src/examples/Minimal/Minimal.dsp
Normal file
@@ -0,0 +1,362 @@
|
||||
# Microsoft Developer Studio Project File - Name="Minimal" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=Minimal - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "Minimal.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "Minimal.mak" CFG="Minimal - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "Minimal - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "Minimal - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "Perforce Project"
|
||||
# PROP Scc_LocalPath "..\.."
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "Minimal - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /W3 /GX /O2 /I "..\..\gm" /I "..\..\platform\win32msvc" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD BASE RSC /l 0xc09 /d "NDEBUG"
|
||||
# ADD RSC /l 0xc09 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
|
||||
!ELSEIF "$(CFG)" == "Minimal - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\gm" /I "..\..\platform\win32msvc" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0xc09 /d "_DEBUG"
|
||||
# ADD RSC /l 0xc09 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "Minimal - Win32 Release"
|
||||
# Name "Minimal - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\main.cpp
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "gm"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmArraySimple.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmArraySimple.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmByteCode.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmByteCode.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmByteCodeGen.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmByteCodeGen.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeGen.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeGen.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeGenHooks.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeGenHooks.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeTree.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCodeTree.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmConfig.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCrc.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmCrc.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmDebug.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmDebug.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmFunctionObject.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmFunctionObject.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmHash.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmHash.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmIncGC.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmIncGC.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmIterator.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmLibHooks.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmLibHooks.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmListDouble.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmListDouble.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmLog.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmLog.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMachine.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMachine.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMachineLib.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMachineLib.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMem.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMem.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemChain.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemChain.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemFixed.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemFixed.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemFixedSet.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmMemFixedSet.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmOperators.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmOperators.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmParser.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmParser.cpp.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmScanner.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmScanner.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStream.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStream.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStreamBuffer.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStreamBuffer.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStringObject.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmStringObject.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmTableObject.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmTableObject.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmThread.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmThread.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmUserObject.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmUserObject.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmUtil.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmUtil.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmVariable.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\gm\gmVariable.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "win32"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\platform\win32msvc\gmConfig_p.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
||||
33
vscript/languages/gm/src/examples/Minimal/Minimal.dsw
Normal file
33
vscript/languages/gm/src/examples/Minimal/Minimal.dsw
Normal file
@@ -0,0 +1,33 @@
|
||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "Minimal"=.\Minimal.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
Perforce Project
|
||||
..\..
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
1059
vscript/languages/gm/src/examples/Minimal/Minimal.vcproj
Normal file
1059
vscript/languages/gm/src/examples/Minimal/Minimal.vcproj
Normal file
File diff suppressed because it is too large
Load Diff
78
vscript/languages/gm/src/examples/Minimal/main.cpp
Normal file
78
vscript/languages/gm/src/examples/Minimal/main.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#if 0 // This is trully the smallest app
|
||||
|
||||
#include "gmThread.h" // game monkey script
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
gmMachine machine;
|
||||
machine.ExecuteString("print(`Hello world`);");
|
||||
getchar(); // Keypress before exit
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // This is a tiny app
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h> // multimedia timer (may need winmm.lib)
|
||||
#include "gmThread.h" // game monkey script
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// Create virtual machine
|
||||
gmMachine* machine = new gmMachine;
|
||||
|
||||
// Get a script from stdin. Some examples:
|
||||
// print("Hello world");
|
||||
// for( i = 0; i < 10; i=i+1 ) { print("i=",i); sleep(1.0); }
|
||||
fprintf(stdout,"Please enter one line of script\n>");
|
||||
const int MAX_SCRIPT_SIZE = 4096;
|
||||
char script[MAX_SCRIPT_SIZE];
|
||||
fgets(script, MAX_SCRIPT_SIZE-1, stdin);
|
||||
|
||||
// Compile the script, but don't run it for now
|
||||
int errors = machine->ExecuteString(script, NULL, false, NULL);
|
||||
// Dump compile time errors to output
|
||||
if(errors)
|
||||
{
|
||||
bool first = true;
|
||||
const char * message;
|
||||
|
||||
while((message = machine->GetLog().GetEntry(first)))
|
||||
{
|
||||
fprintf(stderr, "%s"GM_NL, message);
|
||||
}
|
||||
machine->GetLog().Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
int deltaTime = 0;
|
||||
int lastTime = timeGetTime();
|
||||
|
||||
// Keep executing script while threads persist
|
||||
while(machine->Execute(deltaTime))
|
||||
{
|
||||
// Update delta time
|
||||
int curTime = timeGetTime();
|
||||
deltaTime = curTime - lastTime;
|
||||
lastTime = curTime;
|
||||
|
||||
// Dump run time errors to output
|
||||
bool first = true;
|
||||
const char * message;
|
||||
while((message = machine->GetLog().GetEntry(first)))
|
||||
{
|
||||
fprintf(stderr, "%s"GM_NL, message);
|
||||
}
|
||||
machine->GetLog().Reset();
|
||||
}
|
||||
}
|
||||
|
||||
delete machine; // Finished with VM
|
||||
|
||||
fprintf(stdout,"Script complete. Press a key to exit.");
|
||||
getchar(); // Keypress before exit
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // Minimal build type
|
||||
334
vscript/languages/gm/src/gm/bison.hairy
Normal file
334
vscript/languages/gm/src/gm/bison.hairy
Normal file
@@ -0,0 +1,334 @@
|
||||
|
||||
extern int timeclock;
|
||||
|
||||
|
||||
int yyerror; /* Yyerror and yycost are set by guards. */
|
||||
int yycost; /* If yyerror is set to a nonzero value by a */
|
||||
/* guard, the reduction with which the guard */
|
||||
/* is associated is not performed, and the */
|
||||
/* error recovery mechanism is invoked. */
|
||||
/* Yycost indicates the cost of performing */
|
||||
/* the reduction given the attributes of the */
|
||||
/* symbols. */
|
||||
|
||||
|
||||
/* YYMAXDEPTH indicates the size of the parser's state and value */
|
||||
/* stacks. */
|
||||
|
||||
#ifndef YYMAXDEPTH
|
||||
#define YYMAXDEPTH 500
|
||||
#endif
|
||||
|
||||
/* YYMAXRULES must be at least as large as the number of rules that */
|
||||
/* could be placed in the rule queue. That number could be determined */
|
||||
/* from the grammar and the size of the stack, but, as yet, it is not. */
|
||||
|
||||
#ifndef YYMAXRULES
|
||||
#define YYMAXRULES 100
|
||||
#endif
|
||||
|
||||
#ifndef YYMAXBACKUP
|
||||
#define YYMAXBACKUP 100
|
||||
#endif
|
||||
|
||||
|
||||
short yyss[YYMAXDEPTH]; /* the state stack */
|
||||
YYSTYPE yyvs[YYMAXDEPTH]; /* the semantic value stack */
|
||||
YYLTYPE yyls[YYMAXDEPTH]; /* the location stack */
|
||||
short yyrq[YYMAXRULES]; /* the rule queue */
|
||||
int yychar; /* the lookahead symbol */
|
||||
|
||||
YYSTYPE yylval; /* the semantic value of the */
|
||||
/* lookahead symbol */
|
||||
|
||||
YYSTYPE yytval; /* the semantic value for the state */
|
||||
/* at the top of the state stack. */
|
||||
|
||||
YYSTYPE yyval; /* the variable used to return */
|
||||
/* semantic values from the action */
|
||||
/* routines */
|
||||
|
||||
YYLTYPE yylloc; /* location data for the lookahead */
|
||||
/* symbol */
|
||||
|
||||
YYLTYPE yytloc; /* location data for the state at the */
|
||||
/* top of the state stack */
|
||||
|
||||
|
||||
int yynunlexed;
|
||||
short yyunchar[YYMAXBACKUP];
|
||||
YYSTYPE yyunval[YYMAXBACKUP];
|
||||
YYLTYPE yyunloc[YYMAXBACKUP];
|
||||
|
||||
short *yygssp; /* a pointer to the top of the state */
|
||||
/* stack; only set during error */
|
||||
/* recovery. */
|
||||
|
||||
YYSTYPE *yygvsp; /* a pointer to the top of the value */
|
||||
/* stack; only set during error */
|
||||
/* recovery. */
|
||||
|
||||
YYLTYPE *yyglsp; /* a pointer to the top of the */
|
||||
/* location stack; only set during */
|
||||
/* error recovery. */
|
||||
|
||||
|
||||
/* Yyget is an interface between the parser and the lexical analyzer. */
|
||||
/* It is costly to provide such an interface, but it avoids requiring */
|
||||
/* the lexical analyzer to be able to back up the scan. */
|
||||
|
||||
yyget()
|
||||
{
|
||||
if (yynunlexed > 0)
|
||||
{
|
||||
yynunlexed--;
|
||||
yychar = yyunchar[yynunlexed];
|
||||
yylval = yyunval[yynunlexed];
|
||||
yylloc = yyunloc[yynunlexed];
|
||||
}
|
||||
else if (yychar <= 0)
|
||||
yychar = 0;
|
||||
else
|
||||
{
|
||||
yychar = yylex();
|
||||
if (yychar < 0)
|
||||
yychar = 0;
|
||||
else yychar = YYTRANSLATE(yychar);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
yyunlex(chr, val, loc)
|
||||
int chr;
|
||||
YYSTYPE val;
|
||||
YYLTYPE loc;
|
||||
{
|
||||
yyunchar[yynunlexed] = chr;
|
||||
yyunval[yynunlexed] = val;
|
||||
yyunloc[yynunlexed] = loc;
|
||||
yynunlexed++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
yyrestore(first, last)
|
||||
register short *first;
|
||||
register short *last;
|
||||
{
|
||||
register short *ssp;
|
||||
register short *rp;
|
||||
register int symbol;
|
||||
register int state;
|
||||
register int tvalsaved;
|
||||
|
||||
ssp = yygssp;
|
||||
yyunlex(yychar, yylval, yylloc);
|
||||
|
||||
tvalsaved = 0;
|
||||
while (first != last)
|
||||
{
|
||||
symbol = yystos[*ssp];
|
||||
if (symbol < YYNTBASE)
|
||||
{
|
||||
yyunlex(symbol, yytval, yytloc);
|
||||
tvalsaved = 1;
|
||||
ssp--;
|
||||
}
|
||||
|
||||
ssp--;
|
||||
|
||||
if (first == yyrq)
|
||||
first = yyrq + YYMAXRULES;
|
||||
|
||||
first--;
|
||||
|
||||
for (rp = yyrhs + yyprhs[*first]; symbol = *rp; rp++)
|
||||
{
|
||||
if (symbol < YYNTBASE)
|
||||
state = yytable[yypact[*ssp] + symbol];
|
||||
else
|
||||
{
|
||||
state = yypgoto[symbol - YYNTBASE] + *ssp;
|
||||
|
||||
if (state >= 0 && state <= YYLAST && yycheck[state] == *ssp)
|
||||
state = yytable[state];
|
||||
else
|
||||
state = yydefgoto[symbol - YYNTBASE];
|
||||
}
|
||||
|
||||
*++ssp = state;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! tvalsaved && ssp > yyss)
|
||||
{
|
||||
yyunlex(yystos[*ssp], yytval, yytloc);
|
||||
ssp--;
|
||||
}
|
||||
|
||||
yygssp = ssp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
yyparse()
|
||||
{
|
||||
register int yystate;
|
||||
register int yyn;
|
||||
register short *yyssp;
|
||||
register short *yyrq0;
|
||||
register short *yyptr;
|
||||
register YYSTYPE *yyvsp;
|
||||
|
||||
int yylen;
|
||||
YYLTYPE *yylsp;
|
||||
short *yyrq1;
|
||||
short *yyrq2;
|
||||
|
||||
yystate = 0;
|
||||
yyssp = yyss - 1;
|
||||
yyvsp = yyvs - 1;
|
||||
yylsp = yyls - 1;
|
||||
yyrq0 = yyrq;
|
||||
yyrq1 = yyrq0;
|
||||
yyrq2 = yyrq0;
|
||||
|
||||
yychar = yylex();
|
||||
if (yychar < 0)
|
||||
yychar = 0;
|
||||
else yychar = YYTRANSLATE(yychar);
|
||||
|
||||
yynewstate:
|
||||
|
||||
if (yyssp >= yyss + YYMAXDEPTH - 1)
|
||||
{
|
||||
yyabort("Parser Stack Overflow");
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
*++yyssp = yystate;
|
||||
|
||||
yyresume:
|
||||
|
||||
yyn = yypact[yystate];
|
||||
if (yyn == YYFLAG)
|
||||
goto yydefault;
|
||||
|
||||
yyn += yychar;
|
||||
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar)
|
||||
goto yydefault;
|
||||
|
||||
yyn = yytable[yyn];
|
||||
if (yyn < 0)
|
||||
{
|
||||
yyn = -yyn;
|
||||
goto yyreduce;
|
||||
}
|
||||
else if (yyn == 0)
|
||||
goto yyerrlab;
|
||||
|
||||
yystate = yyn;
|
||||
|
||||
yyptr = yyrq2;
|
||||
while (yyptr != yyrq1)
|
||||
{
|
||||
yyn = *yyptr++;
|
||||
yylen = yyr2[yyn];
|
||||
yyvsp -= yylen;
|
||||
yylsp -= yylen;
|
||||
|
||||
yyguard(yyn, yyvsp, yylsp);
|
||||
if (yyerror)
|
||||
goto yysemerr;
|
||||
|
||||
yyaction(yyn, yyvsp, yylsp);
|
||||
*++yyvsp = yyval;
|
||||
|
||||
yylsp++;
|
||||
if (yylen == 0)
|
||||
{
|
||||
yylsp->timestamp = timeclock;
|
||||
yylsp->first_line = yytloc.first_line;
|
||||
yylsp->first_column = yytloc.first_column;
|
||||
yylsp->last_line = (yylsp-1)->last_line;
|
||||
yylsp->last_column = (yylsp-1)->last_column;
|
||||
yylsp->text = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
yylsp->last_line = (yylsp+yylen-1)->last_line;
|
||||
yylsp->last_column = (yylsp+yylen-1)->last_column;
|
||||
}
|
||||
|
||||
if (yyptr == yyrq + YYMAXRULES)
|
||||
yyptr = yyrq;
|
||||
}
|
||||
|
||||
if (yystate == YYFINAL)
|
||||
YYACCEPT;
|
||||
|
||||
yyrq2 = yyptr;
|
||||
yyrq1 = yyrq0;
|
||||
|
||||
*++yyvsp = yytval;
|
||||
*++yylsp = yytloc;
|
||||
yytval = yylval;
|
||||
yytloc = yylloc;
|
||||
yyget();
|
||||
|
||||
goto yynewstate;
|
||||
|
||||
yydefault:
|
||||
|
||||
yyn = yydefact[yystate];
|
||||
if (yyn == 0)
|
||||
goto yyerrlab;
|
||||
|
||||
yyreduce:
|
||||
|
||||
*yyrq0++ = yyn;
|
||||
|
||||
if (yyrq0 == yyrq + YYMAXRULES)
|
||||
yyrq0 = yyrq;
|
||||
|
||||
if (yyrq0 == yyrq2)
|
||||
{
|
||||
yyabort("Parser Rule Queue Overflow");
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
yyssp -= yyr2[yyn];
|
||||
yyn = yyr1[yyn];
|
||||
|
||||
yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
|
||||
if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
|
||||
yystate = yytable[yystate];
|
||||
else
|
||||
yystate = yydefgoto[yyn - YYNTBASE];
|
||||
|
||||
goto yynewstate;
|
||||
|
||||
yysemerr:
|
||||
*--yyptr = yyn;
|
||||
yyrq2 = yyptr;
|
||||
yyvsp += yyr2[yyn];
|
||||
|
||||
yyerrlab:
|
||||
|
||||
yygssp = yyssp;
|
||||
yygvsp = yyvsp;
|
||||
yyglsp = yylsp;
|
||||
yyrestore(yyrq0, yyrq2);
|
||||
yyrecover();
|
||||
yystate = *yygssp;
|
||||
yyssp = yygssp;
|
||||
yyvsp = yygvsp;
|
||||
yyrq0 = yyrq;
|
||||
yyrq1 = yyrq0;
|
||||
yyrq2 = yyrq0;
|
||||
goto yyresume;
|
||||
}
|
||||
|
||||
$
|
||||
688
vscript/languages/gm/src/gm/bison.simple
Normal file
688
vscript/languages/gm/src/gm/bison.simple
Normal file
@@ -0,0 +1,688 @@
|
||||
/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
|
||||
#line 3 "bison.simple"
|
||||
|
||||
/* Skeleton output parser for bison,
|
||||
Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* As a special exception, when this file is copied by Bison into a
|
||||
Bison output file, you may use that output file without restriction.
|
||||
This special exception was added by the Free Software Foundation
|
||||
in version 1.24 of Bison. */
|
||||
|
||||
#ifndef alloca
|
||||
#ifdef __GNUC__
|
||||
#define alloca __builtin_alloca
|
||||
#else /* not GNU C. */
|
||||
#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
|
||||
#include <alloca.h>
|
||||
#else /* not sparc */
|
||||
#if defined (MSDOS) && !defined (__TURBOC__)
|
||||
#include <malloc.h>
|
||||
#else /* not MSDOS, or __TURBOC__ */
|
||||
#if defined(_AIX)
|
||||
#include <malloc.h>
|
||||
#pragma alloca
|
||||
#else /* not MSDOS, __TURBOC__, or _AIX */
|
||||
#ifdef __hpux
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
void *alloca (unsigned int);
|
||||
};
|
||||
#else /* not __cplusplus */
|
||||
void *alloca ();
|
||||
#endif /* not __cplusplus */
|
||||
#endif /* __hpux */
|
||||
#endif /* not _AIX */
|
||||
#endif /* not MSDOS, or __TURBOC__ */
|
||||
#endif /* not sparc. */
|
||||
#endif /* not GNU C. */
|
||||
#endif /* alloca not defined. */
|
||||
|
||||
/* This is the parser code that is written into each bison parser
|
||||
when the %semantic_parser declaration is not specified in the grammar.
|
||||
It was written by Richard Stallman by simplifying the hairy parser
|
||||
used when %semantic_parser is specified. */
|
||||
|
||||
/* Note: there must be only one dollar sign in this file.
|
||||
It is replaced by the list of actions, each action
|
||||
as one case of the switch. */
|
||||
|
||||
#define yyerrok (yyerrstatus = 0)
|
||||
#define yyclearin (yychar = YYEMPTY)
|
||||
#define YYEMPTY -2
|
||||
#define YYEOF 0
|
||||
#define YYACCEPT return(0)
|
||||
#define YYABORT return(1)
|
||||
#define YYERROR goto yyerrlab1
|
||||
/* Like YYERROR except do call yyerror.
|
||||
This remains here temporarily to ease the
|
||||
transition to the new meaning of YYERROR, for GCC.
|
||||
Once GCC version 2 has supplanted version 1, this can go. */
|
||||
#define YYFAIL goto yyerrlab
|
||||
#define YYRECOVERING() (!!yyerrstatus)
|
||||
#define YYBACKUP(token, value) \
|
||||
do \
|
||||
if (yychar == YYEMPTY && yylen == 1) \
|
||||
{ yychar = (token), yylval = (value); \
|
||||
yychar1 = YYTRANSLATE (yychar); \
|
||||
YYPOPSTACK; \
|
||||
goto yybackup; \
|
||||
} \
|
||||
else \
|
||||
{ yyerror ("syntax error: cannot back up"); YYERROR; } \
|
||||
while (0)
|
||||
|
||||
#define YYTERROR 1
|
||||
#define YYERRCODE 256
|
||||
|
||||
#ifndef YYPURE
|
||||
#define YYLEX yylex()
|
||||
#endif
|
||||
|
||||
#ifdef YYPURE
|
||||
#ifdef YYLSP_NEEDED
|
||||
#ifdef YYLEX_PARAM
|
||||
#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
|
||||
#else
|
||||
#define YYLEX yylex(&yylval, &yylloc)
|
||||
#endif
|
||||
#else /* not YYLSP_NEEDED */
|
||||
#ifdef YYLEX_PARAM
|
||||
#define YYLEX yylex(&yylval, YYLEX_PARAM)
|
||||
#else
|
||||
#define YYLEX yylex(&yylval)
|
||||
#endif
|
||||
#endif /* not YYLSP_NEEDED */
|
||||
#endif
|
||||
|
||||
/* If nonreentrant, generate the variables here */
|
||||
|
||||
#ifndef YYPURE
|
||||
|
||||
int yychar; /* the lookahead symbol */
|
||||
YYSTYPE yylval; /* the semantic value of the */
|
||||
/* lookahead symbol */
|
||||
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE yylloc; /* location data for the lookahead */
|
||||
/* symbol */
|
||||
#endif
|
||||
|
||||
int yynerrs; /* number of parse errors so far */
|
||||
#endif /* not YYPURE */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
int yydebug; /* nonzero means print parse trace */
|
||||
/* Since this is uninitialized, it does not stop multiple parsers
|
||||
from coexisting. */
|
||||
#endif
|
||||
|
||||
/* YYINITDEPTH indicates the initial size of the parser's stacks */
|
||||
|
||||
#ifndef YYINITDEPTH
|
||||
#define YYINITDEPTH 200
|
||||
#endif
|
||||
|
||||
/* YYMAXDEPTH is the maximum size the stacks can grow to
|
||||
(effective only if the built-in stack extension method is used). */
|
||||
|
||||
#if YYMAXDEPTH == 0
|
||||
#undef YYMAXDEPTH
|
||||
#endif
|
||||
|
||||
#ifndef YYMAXDEPTH
|
||||
#define YYMAXDEPTH 10000
|
||||
#endif
|
||||
|
||||
/* Prevent warning if -Wstrict-prototypes. */
|
||||
#ifdef __GNUC__
|
||||
int yyparse (void);
|
||||
#endif
|
||||
|
||||
#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
|
||||
#define __yy_memcpy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
|
||||
#else /* not GNU C or C++ */
|
||||
#ifndef __cplusplus
|
||||
|
||||
/* This is the most reliable way to avoid incompatibilities
|
||||
in available built-in functions on various systems. */
|
||||
static void
|
||||
__yy_memcpy (from, to, count)
|
||||
char *from;
|
||||
char *to;
|
||||
int count;
|
||||
{
|
||||
register char *f = from;
|
||||
register char *t = to;
|
||||
register int i = count;
|
||||
|
||||
while (i-- > 0)
|
||||
*t++ = *f++;
|
||||
}
|
||||
|
||||
#else /* __cplusplus */
|
||||
|
||||
/* This is the most reliable way to avoid incompatibilities
|
||||
in available built-in functions on various systems. */
|
||||
static void
|
||||
__yy_memcpy (char *from, char *to, int count)
|
||||
{
|
||||
register char *f = from;
|
||||
register char *t = to;
|
||||
register int i = count;
|
||||
|
||||
while (i-- > 0)
|
||||
*t++ = *f++;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#line 192 "bison.simple"
|
||||
|
||||
/* The user can define YYPARSE_PARAM as the name of an argument to be passed
|
||||
into yyparse. The argument should have type void *.
|
||||
It should actually point to an object.
|
||||
Grammar actions can access the variable by casting it
|
||||
to the proper pointer type. */
|
||||
|
||||
#ifdef YYPARSE_PARAM
|
||||
#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
|
||||
#else
|
||||
#define YYPARSE_PARAM
|
||||
#define YYPARSE_PARAM_DECL
|
||||
#endif
|
||||
|
||||
int
|
||||
yyparse(YYPARSE_PARAM)
|
||||
YYPARSE_PARAM_DECL
|
||||
{
|
||||
register int yystate;
|
||||
register int yyn;
|
||||
register short *yyssp;
|
||||
register YYSTYPE *yyvsp;
|
||||
int yyerrstatus; /* number of tokens to shift before error messages enabled */
|
||||
int yychar1 = 0; /* lookahead token as an internal (translated) token number */
|
||||
|
||||
short yyssa[YYINITDEPTH]; /* the state stack */
|
||||
YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
|
||||
|
||||
short *yyss = yyssa; /* refer to the stacks thru separate pointers */
|
||||
YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
|
||||
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
|
||||
YYLTYPE *yyls = yylsa;
|
||||
YYLTYPE *yylsp;
|
||||
|
||||
#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
|
||||
#else
|
||||
#define YYPOPSTACK (yyvsp--, yyssp--)
|
||||
#endif
|
||||
|
||||
int yystacksize = YYINITDEPTH;
|
||||
|
||||
#ifdef YYPURE
|
||||
int yychar;
|
||||
YYSTYPE yylval;
|
||||
int yynerrs;
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE yylloc;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
YYSTYPE yyval; /* the variable used to return */
|
||||
/* semantic values from the action */
|
||||
/* routines */
|
||||
|
||||
int yylen;
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Starting parse\n");
|
||||
#endif
|
||||
|
||||
yystate = 0;
|
||||
yyerrstatus = 0;
|
||||
yynerrs = 0;
|
||||
yychar = YYEMPTY; /* Cause a token to be read. */
|
||||
|
||||
/* Initialize stack pointers.
|
||||
Waste one element of value and location stack
|
||||
so that they stay on the same level as the state stack.
|
||||
The wasted elements are never initialized. */
|
||||
|
||||
yyssp = yyss - 1;
|
||||
yyvsp = yyvs;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp = yyls;
|
||||
#endif
|
||||
|
||||
/* Push a new state, which is found in yystate . */
|
||||
/* In all cases, when you get here, the value and location stacks
|
||||
have just been pushed. so pushing a state here evens the stacks. */
|
||||
yynewstate:
|
||||
|
||||
*++yyssp = (short) yystate;
|
||||
|
||||
if (yyssp >= yyss + yystacksize - 1)
|
||||
{
|
||||
/* Give user a chance to reallocate the stack */
|
||||
/* Use copies of these so that the &'s don't force the real ones into memory. */
|
||||
YYSTYPE *yyvs1 = yyvs;
|
||||
short *yyss1 = yyss;
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE *yyls1 = yyls;
|
||||
#endif
|
||||
|
||||
/* Get the current used size of the three stacks, in elements. */
|
||||
int size = yyssp - yyss + 1;
|
||||
|
||||
#ifdef yyoverflow
|
||||
/* Each stack pointer address is followed by the size of
|
||||
the data in use in that stack, in bytes. */
|
||||
#ifdef YYLSP_NEEDED
|
||||
/* This used to be a conditional around just the two extra args,
|
||||
but that might be undefined if yyoverflow is a macro. */
|
||||
yyoverflow("parser stack overflow",
|
||||
&yyss1, size * sizeof (*yyssp),
|
||||
&yyvs1, size * sizeof (*yyvsp),
|
||||
&yyls1, size * sizeof (*yylsp),
|
||||
&yystacksize);
|
||||
#else
|
||||
yyoverflow("parser stack overflow",
|
||||
&yyss1, size * sizeof (*yyssp),
|
||||
&yyvs1, size * sizeof (*yyvsp),
|
||||
&yystacksize);
|
||||
#endif
|
||||
|
||||
yyss = yyss1; yyvs = yyvs1;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yyls = yyls1;
|
||||
#endif
|
||||
#else /* no yyoverflow */
|
||||
/* Extend the stack our own way. */
|
||||
if (yystacksize >= YYMAXDEPTH)
|
||||
{
|
||||
yyerror("parser stack overflow");
|
||||
return 2;
|
||||
}
|
||||
yystacksize *= 2;
|
||||
if (yystacksize > YYMAXDEPTH)
|
||||
yystacksize = YYMAXDEPTH;
|
||||
yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
|
||||
__yy_memcpy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
|
||||
yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
|
||||
__yy_memcpy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
|
||||
#ifdef YYLSP_NEEDED
|
||||
yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
|
||||
__yy_memcpy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
|
||||
#endif
|
||||
#endif /* no yyoverflow */
|
||||
|
||||
yyssp = yyss + size - 1;
|
||||
yyvsp = yyvs + size - 1;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp = yyls + size - 1;
|
||||
#endif
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Stack size increased to %d\n", yystacksize);
|
||||
#endif
|
||||
|
||||
if (yyssp >= yyss + yystacksize - 1)
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Entering state %d\n", yystate);
|
||||
#endif
|
||||
|
||||
goto yybackup;
|
||||
yybackup:
|
||||
|
||||
/* Do appropriate processing given the current state. */
|
||||
/* Read a lookahead token if we need one and don't already have one. */
|
||||
/* yyresume: */
|
||||
|
||||
/* First try to decide what to do without reference to lookahead token. */
|
||||
|
||||
yyn = yypact[yystate];
|
||||
if (yyn == YYFLAG)
|
||||
goto yydefault;
|
||||
|
||||
/* Not known => get a lookahead token if don't already have one. */
|
||||
|
||||
/* yychar is either YYEMPTY or YYEOF
|
||||
or a valid token in external form. */
|
||||
|
||||
if (yychar == YYEMPTY)
|
||||
{
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Reading a token: ");
|
||||
#endif
|
||||
yychar = YYLEX;
|
||||
}
|
||||
|
||||
/* Convert token to internal form (in yychar1) for indexing tables with */
|
||||
|
||||
if (yychar <= 0) /* This means end of input. */
|
||||
{
|
||||
yychar1 = 0;
|
||||
yychar = YYEOF; /* Don't call YYLEX any more */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Now at end of input.\n");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
yychar1 = YYTRANSLATE(yychar);
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
|
||||
/* Give the individual parser a way to print the precise meaning
|
||||
of a token, for further debugging info. */
|
||||
#ifdef YYPRINT
|
||||
YYPRINT (stderr, yychar, yylval);
|
||||
#endif
|
||||
fprintf (stderr, ")\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
yyn += yychar1;
|
||||
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
|
||||
goto yydefault;
|
||||
|
||||
yyn = yytable[yyn];
|
||||
|
||||
/* yyn is what to do for this token type in this state.
|
||||
Negative => reduce, -yyn is rule number.
|
||||
Positive => shift, yyn is new state.
|
||||
New state is final state => don't bother to shift,
|
||||
just return success.
|
||||
0, or most negative number => error. */
|
||||
|
||||
if (yyn < 0)
|
||||
{
|
||||
if (yyn == YYFLAG)
|
||||
goto yyerrlab;
|
||||
yyn = -yyn;
|
||||
goto yyreduce;
|
||||
}
|
||||
else if (yyn == 0)
|
||||
goto yyerrlab;
|
||||
|
||||
if (yyn == YYFINAL)
|
||||
YYACCEPT;
|
||||
|
||||
/* Shift the lookahead token. */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
|
||||
#endif
|
||||
|
||||
/* Discard the token being shifted unless it is eof. */
|
||||
if (yychar != YYEOF)
|
||||
yychar = YYEMPTY;
|
||||
|
||||
*++yyvsp = yylval;
|
||||
#ifdef YYLSP_NEEDED
|
||||
*++yylsp = yylloc;
|
||||
#endif
|
||||
|
||||
/* count tokens shifted since error; after three, turn off error status. */
|
||||
if (yyerrstatus) yyerrstatus--;
|
||||
|
||||
yystate = yyn;
|
||||
goto yynewstate;
|
||||
|
||||
/* Do the default action for the current state. */
|
||||
yydefault:
|
||||
|
||||
yyn = yydefact[yystate];
|
||||
if (yyn == 0)
|
||||
goto yyerrlab;
|
||||
|
||||
/* Do a reduction. yyn is the number of a rule to reduce with. */
|
||||
yyreduce:
|
||||
yylen = yyr2[yyn];
|
||||
if (yylen > 0)
|
||||
yyval = yyvsp[1-yylen]; /* implement default value of the action */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf (stderr, "Reducing via rule %d (line %d), ",
|
||||
yyn, yyrline[yyn]);
|
||||
|
||||
/* Print the symbols being reduced, and their result. */
|
||||
for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
|
||||
fprintf (stderr, "%s ", yytname[yyrhs[i]]);
|
||||
fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
|
||||
}
|
||||
#endif
|
||||
|
||||
$ /* the action file gets copied in in place of this dollarsign */
|
||||
#line 487 "bison.simple"
|
||||
|
||||
yyvsp -= yylen;
|
||||
yyssp -= yylen;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp -= yylen;
|
||||
#endif
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
short *ssp1 = yyss - 1;
|
||||
fprintf (stderr, "state stack now");
|
||||
while (ssp1 != yyssp)
|
||||
fprintf (stderr, " %d", *++ssp1);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
*++yyvsp = yyval;
|
||||
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp++;
|
||||
if (yylen == 0)
|
||||
{
|
||||
yylsp->first_line = yylloc.first_line;
|
||||
yylsp->first_column = yylloc.first_column;
|
||||
yylsp->last_line = (yylsp-1)->last_line;
|
||||
yylsp->last_column = (yylsp-1)->last_column;
|
||||
yylsp->text = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
yylsp->last_line = (yylsp+yylen-1)->last_line;
|
||||
yylsp->last_column = (yylsp+yylen-1)->last_column;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Now "shift" the result of the reduction.
|
||||
Determine what state that goes to,
|
||||
based on the state we popped back to
|
||||
and the rule number reduced by. */
|
||||
|
||||
yyn = yyr1[yyn];
|
||||
|
||||
yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
|
||||
if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
|
||||
yystate = yytable[yystate];
|
||||
else
|
||||
yystate = yydefgoto[yyn - YYNTBASE];
|
||||
|
||||
goto yynewstate;
|
||||
|
||||
yyerrlab: /* here on detecting error */
|
||||
|
||||
if (! yyerrstatus)
|
||||
/* If not already recovering from an error, report this error. */
|
||||
{
|
||||
++yynerrs;
|
||||
|
||||
#ifdef YYERROR_VERBOSE
|
||||
yyn = yypact[yystate];
|
||||
|
||||
if (yyn > YYFLAG && yyn < YYLAST)
|
||||
{
|
||||
int size = 0;
|
||||
char *msg;
|
||||
int x, count;
|
||||
|
||||
count = 0;
|
||||
/* Start X at -yyn if nec to avoid negative indexes in yycheck. */
|
||||
for (x = (yyn < 0 ? -yyn : 0);
|
||||
x < (int)(sizeof(yytname) / sizeof(char *)); x++) //_GD_
|
||||
if (yycheck[x + yyn] == x)
|
||||
size += strlen(yytname[x]) + 15, count++;
|
||||
//_GD_ msg = (char *) malloc(size + 15);
|
||||
msg = GM_NEW( char [size + 15] );
|
||||
if (msg != 0)
|
||||
{
|
||||
strcpy(msg, "parse error");
|
||||
|
||||
if (count < 5)
|
||||
{
|
||||
count = 0;
|
||||
for (x = (yyn < 0 ? -yyn : 0);
|
||||
x < (sizeof(yytname) / sizeof(char *)); x++)
|
||||
if (yycheck[x + yyn] == x)
|
||||
{
|
||||
strcat(msg, count == 0 ? ", expecting `" : " or `");
|
||||
strcat(msg, yytname[x]);
|
||||
strcat(msg, "'");
|
||||
count++;
|
||||
}
|
||||
}
|
||||
yyerror(msg);
|
||||
//_GD_ free(msg);
|
||||
delete [] msg;
|
||||
}
|
||||
else
|
||||
yyerror ("parse error; also virtual memory exceeded");
|
||||
}
|
||||
else
|
||||
#endif /* YYERROR_VERBOSE */
|
||||
yyerror("parse error");
|
||||
}
|
||||
|
||||
goto yyerrlab1;
|
||||
yyerrlab1: /* here on error raised explicitly by an action */
|
||||
|
||||
if (yyerrstatus == 3)
|
||||
{
|
||||
/* if just tried and failed to reuse lookahead token after an error, discard it. */
|
||||
|
||||
/* return failure if at end of input */
|
||||
if (yychar == YYEOF)
|
||||
YYABORT;
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
|
||||
#endif
|
||||
|
||||
yychar = YYEMPTY;
|
||||
}
|
||||
|
||||
/* Else will try to reuse lookahead token
|
||||
after shifting the error token. */
|
||||
|
||||
yyerrstatus = 3; /* Each real token shifted decrements this */
|
||||
|
||||
goto yyerrhandle;
|
||||
|
||||
yyerrdefault: /* current state does not do anything special for the error token. */
|
||||
|
||||
#if 0
|
||||
/* This is wrong; only states that explicitly want error tokens
|
||||
should shift them. */
|
||||
yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
|
||||
if (yyn) goto yydefault;
|
||||
#endif
|
||||
|
||||
yyerrpop: /* pop the current state because it cannot handle the error token */
|
||||
|
||||
if (yyssp == yyss) YYABORT;
|
||||
yyvsp--;
|
||||
yystate = *--yyssp;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp--;
|
||||
#endif
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
short *ssp1 = yyss - 1;
|
||||
fprintf (stderr, "Error: state stack now");
|
||||
while (ssp1 != yyssp)
|
||||
fprintf (stderr, " %d", *++ssp1);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
yyerrhandle:
|
||||
|
||||
yyn = yypact[yystate];
|
||||
if (yyn == YYFLAG)
|
||||
goto yyerrdefault;
|
||||
|
||||
yyn += YYTERROR;
|
||||
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
|
||||
goto yyerrdefault;
|
||||
|
||||
yyn = yytable[yyn];
|
||||
if (yyn < 0)
|
||||
{
|
||||
if (yyn == YYFLAG)
|
||||
goto yyerrpop;
|
||||
yyn = -yyn;
|
||||
goto yyreduce;
|
||||
}
|
||||
else if (yyn == 0)
|
||||
goto yyerrpop;
|
||||
|
||||
if (yyn == YYFINAL)
|
||||
YYACCEPT;
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Shifting error token, ");
|
||||
#endif
|
||||
|
||||
*++yyvsp = yylval;
|
||||
#ifdef YYLSP_NEEDED
|
||||
*++yylsp = yylloc;
|
||||
#endif
|
||||
|
||||
yystate = yyn;
|
||||
goto yynewstate;
|
||||
}
|
||||
1512
vscript/languages/gm/src/gm/flex.skl
Normal file
1512
vscript/languages/gm/src/gm/flex.skl
Normal file
File diff suppressed because it is too large
Load Diff
18
vscript/languages/gm/src/gm/gmArraySimple.cpp
Normal file
18
vscript/languages/gm/src/gm/gmArraySimple.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmArraySimple.h"
|
||||
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
333
vscript/languages/gm/src/gm/gmArraySimple.h
Normal file
333
vscript/languages/gm/src/gm/gmArraySimple.h
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMARRAYSIMPLE_H_
|
||||
#define _GMARRAYSIMPLE_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmUtil.h"
|
||||
|
||||
#define TMPL template <class T>
|
||||
#define QUAL gmArraySimple<T>
|
||||
|
||||
/// \class gmArraySimple
|
||||
/// \brief templated array class for simple types with power of 2 auto size option.
|
||||
/// Elements may be moved around in memory using memcpy() during resize operations.
|
||||
TMPL class gmArraySimple
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
NULL_INDEX = ~0
|
||||
};
|
||||
|
||||
gmArraySimple(void);
|
||||
gmArraySimple(const QUAL &a_array);
|
||||
~gmArraySimple(void);
|
||||
|
||||
template <class J>
|
||||
inline void InsertLast(const J &a_elem)
|
||||
{
|
||||
if(m_count >= m_size)
|
||||
{
|
||||
Resize(m_count + 1);
|
||||
}
|
||||
m_elem[m_count++] = a_elem;
|
||||
}
|
||||
|
||||
/// \brief SetBlockSize() will set the hysteresis memory grow by in elements.
|
||||
/// \param a_blockSize as 0 will set automatic power of 2.
|
||||
inline void SetBlockSize(gmuint a_blockSize) { m_blockSize = a_blockSize; }
|
||||
|
||||
inline bool InsertLastIfUnique(const T &a_elem);
|
||||
inline T& InsertLast(void);
|
||||
inline void InsertBefore(gmuint a_index, const T &a_elem);
|
||||
inline void Remove(gmuint a_index);
|
||||
inline void RemoveSwapLast(gmuint a_index);
|
||||
inline void RemoveLast(void);
|
||||
|
||||
inline T &operator[](gmuint a_index);
|
||||
inline const T &operator[](gmuint a_index) const;
|
||||
|
||||
inline gmuint Count(void) const { return m_count; }
|
||||
inline bool IsEmpty(void) const { return (m_count == 0); }
|
||||
|
||||
inline void Reset(void) { SetCount(0); }
|
||||
inline void ResetAndFreeMemory(void);
|
||||
|
||||
inline void SetCount(gmuint a_count);
|
||||
inline void SetCountAndFreeMemory(gmuint a_count);
|
||||
inline void Touch(gmuint a_element);
|
||||
|
||||
inline T* GetData(void) { return m_elem; }
|
||||
inline const T* GetData(void) const { return m_elem; }
|
||||
inline gmuint GetSize(void) { return m_size; }
|
||||
bool IsValid(const T* a_elem) const;
|
||||
|
||||
inline QUAL &operator=(const QUAL &a_array);
|
||||
|
||||
inline bool FindRemove(const T &a_elem);
|
||||
|
||||
template <class Q>
|
||||
inline gmuint FindIndex(const Q &a_elem) const
|
||||
{
|
||||
// iterate backwards, better chance of finding a_elem, given InsertLast() is
|
||||
// used commonly which presents possible element coherence
|
||||
if(m_count == 0) return NULL_INDEX;
|
||||
gmuint i = m_count - 1;
|
||||
do
|
||||
{
|
||||
if (m_elem[i] == a_elem) // used commonly which presents possible element coherence
|
||||
return i;
|
||||
} while(i-- > 0);
|
||||
return NULL_INDEX;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
T *m_elem;
|
||||
gmuint m_count, m_size;
|
||||
gmuint m_blockSize; //!< 0 and will be power of 2 sizing.
|
||||
|
||||
/// \brief Resize() will resize the array.
|
||||
/// \param a_size is the required size.
|
||||
void Resize(gmuint a_size, bool a_shrinkIfPossible = false);
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// implementation
|
||||
//
|
||||
|
||||
|
||||
TMPL
|
||||
inline QUAL::gmArraySimple(void)
|
||||
{
|
||||
m_elem = NULL;
|
||||
m_count = 0;
|
||||
m_size = 0;
|
||||
m_blockSize = 0; // power of 2 auto
|
||||
}
|
||||
|
||||
|
||||
TMPL inline QUAL::gmArraySimple(const QUAL &a_array)
|
||||
{
|
||||
m_elem = NULL;
|
||||
m_count = 0;
|
||||
m_size = 0;
|
||||
m_blockSize = 0; // power of 2 auto
|
||||
operator=(a_array);
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline QUAL::~gmArraySimple(void)
|
||||
{
|
||||
if(m_elem)
|
||||
{
|
||||
delete[] (char *) m_elem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TMPL bool QUAL::InsertLastIfUnique(const T &a_elem)
|
||||
{
|
||||
if(FindIndex(a_elem) == NULL_INDEX)
|
||||
{
|
||||
InsertLast(a_elem);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
TMPL inline T& QUAL::InsertLast(void)
|
||||
{
|
||||
if(m_count >= m_size)
|
||||
{
|
||||
Resize(m_count + 1);
|
||||
}
|
||||
return m_elem[m_count++];
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline void QUAL::InsertBefore(gmuint a_index, const T &a_elem)
|
||||
{
|
||||
if(a_index >= m_count)
|
||||
{
|
||||
InsertLast(a_elem);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_count >= m_size)
|
||||
{
|
||||
Resize(m_count + 1);
|
||||
}
|
||||
memmove(&m_elem[a_index+1], &m_elem[a_index], (m_count - a_index) * sizeof(T));
|
||||
m_elem[a_index] = a_elem;
|
||||
++m_count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline void QUAL::Remove(gmuint a_index)
|
||||
{
|
||||
if(a_index >= m_count) return;
|
||||
memmove(&m_elem[a_index], &m_elem[a_index+1], (m_count - (a_index + 1)) * sizeof(T));
|
||||
--m_count;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline void QUAL::RemoveSwapLast(gmuint a_index)
|
||||
{
|
||||
if (a_index >= m_count) return;
|
||||
if(--m_count != a_index)
|
||||
{
|
||||
m_elem[a_index] = m_elem[m_count];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline void QUAL::RemoveLast(void)
|
||||
{
|
||||
GM_ASSERT(m_count > 0);
|
||||
--m_count;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline T &QUAL::operator[](gmuint a_index)
|
||||
{
|
||||
GM_ASSERT(a_index >= 0 && a_index < m_count);
|
||||
return m_elem[a_index];
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline const T &QUAL::operator[](gmuint a_index) const
|
||||
{
|
||||
GM_ASSERT(a_index >= 0 && a_index < m_count);
|
||||
return m_elem[a_index];
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline void QUAL::ResetAndFreeMemory(void)
|
||||
{
|
||||
if(m_elem)
|
||||
{
|
||||
delete[] (char *) m_elem;
|
||||
m_elem = NULL;
|
||||
}
|
||||
m_count = m_size = 0;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline void QUAL::SetCount(gmuint a_count)
|
||||
{
|
||||
if(a_count > m_size)
|
||||
{
|
||||
Resize(a_count);
|
||||
}
|
||||
m_count = a_count;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline void QUAL::SetCountAndFreeMemory(gmuint a_count)
|
||||
{
|
||||
Resize(a_count, true);
|
||||
m_count = a_count;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline void QUAL::Touch(gmuint a_element)
|
||||
{
|
||||
if(a_element >= m_count)
|
||||
{
|
||||
SetCount(a_element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TMPL bool QUAL::IsValid(const T* a_elem) const
|
||||
{
|
||||
gmuint index = (a_elem - m_elem);
|
||||
return (index < m_count);
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
inline QUAL &QUAL::operator=(const QUAL &a_array)
|
||||
{
|
||||
SetCount(a_array.m_count);
|
||||
memcpy((char*)m_elem, (const char*)a_array.m_elem, m_count * sizeof(T));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
TMPL bool QUAL::FindRemove(const T &a_elem)
|
||||
{
|
||||
gmuint index = FindIndex(a_elem);
|
||||
if(index != NULL_INDEX)
|
||||
{
|
||||
Remove(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
void QUAL::Resize(gmuint a_size, bool a_shrinkIfPossible)
|
||||
{
|
||||
if(m_size >= a_size)
|
||||
{
|
||||
// TODO: handle a_shrinkIfPossible.
|
||||
return;
|
||||
}
|
||||
|
||||
// we need to grow, figure out a new size.
|
||||
gmuint size = 0;
|
||||
if(m_blockSize > 0)
|
||||
{
|
||||
size = ((a_size / m_blockSize) + 1) * m_blockSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
size = gmLog2ge(gmMax<gmuint>(4, a_size + 1));
|
||||
}
|
||||
|
||||
// alloc, copy, free
|
||||
{
|
||||
T * t = (T*) GM_NEW(char[sizeof(T) * size]);
|
||||
if(m_elem)
|
||||
{
|
||||
memcpy(t, m_elem, m_count * sizeof(T));
|
||||
delete[] (char *) m_elem;
|
||||
}
|
||||
m_elem = t;
|
||||
}
|
||||
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
#undef QUAL
|
||||
#undef TMPL
|
||||
|
||||
#endif
|
||||
134
vscript/languages/gm/src/gm/gmByteCode.cpp
Normal file
134
vscript/languages/gm/src/gm/gmByteCode.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmByteCode.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
#if GM_COMPILE_DEBUG
|
||||
|
||||
void gmByteCodePrint(FILE * a_fp, const void * a_byteCode, int a_byteCodeLength)
|
||||
{
|
||||
union
|
||||
{
|
||||
const gmuint32 * instruction32;
|
||||
const gmuint8 * instruction;
|
||||
};
|
||||
|
||||
instruction = (const gmuint8 *) a_byteCode;
|
||||
const gmuint8 * end = instruction + a_byteCodeLength;
|
||||
const gmuint8 * start = instruction;
|
||||
const char * cp;
|
||||
bool opiptr, opf32;
|
||||
|
||||
while(instruction < end)
|
||||
{
|
||||
opiptr = false;
|
||||
opf32 = false;
|
||||
|
||||
int addr = instruction - start;
|
||||
|
||||
switch(*instruction)
|
||||
{
|
||||
case BC_NOP : cp = "nop"; break;
|
||||
case BC_LINE : cp = "line"; break;
|
||||
|
||||
case BC_GETDOT : cp = "get dot"; opiptr = true; break;
|
||||
case BC_SETDOT : cp = "set dot"; opiptr = true; break;
|
||||
case BC_GETIND : cp = "get index"; break;
|
||||
case BC_SETIND : cp = "set index"; break;
|
||||
|
||||
case BC_BRA : cp = "bra"; opiptr = true; break;
|
||||
case BC_BRZ : cp = "brz"; opiptr = true; break;
|
||||
case BC_BRNZ : cp = "brnz"; opiptr = true; break;
|
||||
case BC_BRZK : cp = "brzk"; opiptr = true; break;
|
||||
case BC_BRNZK : cp = "brnzk"; opiptr = true; break;
|
||||
case BC_CALL : cp = "call"; opiptr = true; break;
|
||||
case BC_RET : cp = "ret"; break;
|
||||
case BC_RETV : cp = "retv"; break;
|
||||
case BC_FOREACH : cp = "foreach"; opiptr = true; break;
|
||||
|
||||
case BC_POP : cp = "pop"; break;
|
||||
case BC_POP2 : cp = "pop2"; break;
|
||||
case BC_DUP : cp = "dup"; break;
|
||||
case BC_DUP2 : cp = "dup2"; break;
|
||||
case BC_SWAP : cp = "swap"; break;
|
||||
case BC_PUSHNULL : cp = "push null"; break;
|
||||
case BC_PUSHINT : cp = "push int"; opiptr = true; break;
|
||||
case BC_PUSHINT0 : cp = "push int 0"; break;
|
||||
case BC_PUSHINT1 : cp = "push int 1"; break;
|
||||
case BC_PUSHFP : cp = "push fp"; opf32 = true; break;
|
||||
case BC_PUSHSTR : cp = "push str"; opiptr = true; break;
|
||||
case BC_PUSHTBL : cp = "push tbl"; break;
|
||||
case BC_PUSHFN : cp = "push fn"; opiptr = true; break;
|
||||
case BC_PUSHTHIS : cp = "push this"; break;
|
||||
|
||||
case BC_GETLOCAL : cp = "get local"; opiptr = true; break;
|
||||
case BC_SETLOCAL : cp = "set local"; opiptr = true; break;
|
||||
case BC_GETGLOBAL : cp = "get global"; opiptr = true; break;
|
||||
case BC_SETGLOBAL : cp = "set global"; opiptr = true; break;
|
||||
case BC_GETTHIS : cp = "get this"; opiptr = true; break;
|
||||
case BC_SETTHIS : cp = "set this"; opiptr = true; break;
|
||||
|
||||
case BC_OP_ADD : cp = "add"; break;
|
||||
case BC_OP_SUB : cp = "sub"; break;
|
||||
case BC_OP_MUL : cp = "mul"; break;
|
||||
case BC_OP_DIV : cp = "div"; break;
|
||||
case BC_OP_REM : cp = "rem"; break;
|
||||
|
||||
case BC_BIT_OR : cp = "bor"; break;
|
||||
case BC_BIT_XOR : cp = "bxor"; break;
|
||||
case BC_BIT_AND : cp = "band"; break;
|
||||
case BC_BIT_INV : cp = "binv"; break;
|
||||
case BC_BIT_SHL : cp = "bshl"; break;
|
||||
case BC_BIT_SHR : cp = "bshr"; break;
|
||||
|
||||
case BC_OP_NEG : cp = "neg"; break;
|
||||
case BC_OP_POS : cp = "pos"; break;
|
||||
case BC_OP_NOT : cp = "not"; break;
|
||||
|
||||
case BC_OP_LT : cp = "lt"; break;
|
||||
case BC_OP_GT : cp = "gt"; break;
|
||||
case BC_OP_LTE : cp = "lte"; break;
|
||||
case BC_OP_GTE : cp = "gte"; break;
|
||||
case BC_OP_EQ : cp = "eq"; break;
|
||||
case BC_OP_NEQ : cp = "neq"; break;
|
||||
|
||||
default : cp = "ERROR"; break;
|
||||
}
|
||||
|
||||
++instruction32;
|
||||
|
||||
if(opf32)
|
||||
{
|
||||
float fval = *((float *) instruction);
|
||||
instruction += sizeof(gmint32);
|
||||
fprintf(a_fp, " %04d %s %f"GM_NL, addr, cp, fval);
|
||||
}
|
||||
else if (opiptr)
|
||||
{
|
||||
gmptr ival = *((gmptr *) instruction);
|
||||
instruction += sizeof(gmptr);
|
||||
fprintf(a_fp, " %04d %s %d"GM_NL, addr, cp, ival);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(a_fp, " %04d %s"GM_NL, addr, cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // GM_COMPILE_DEBUG
|
||||
|
||||
102
vscript/languages/gm/src/gm/gmByteCode.h
Normal file
102
vscript/languages/gm/src/gm/gmByteCode.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMBYTECODE_H_
|
||||
#define _GMBYTECODE_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
|
||||
/// \enum gmByteCode
|
||||
/// \brief gmByteCode are the op codes for the game monkey scripting. The first byte codes MUST match the gmOperator
|
||||
/// enum.
|
||||
enum gmByteCode
|
||||
{
|
||||
// BC_GETDOT to BC_NOP MUST MATCH ENUM GMOPERATOR
|
||||
|
||||
BC_GETDOT = 0, // tos '.' opptr, push result
|
||||
BC_SETDOT, // tos-1 '.' opptr = tos, tos -= 2
|
||||
BC_GETIND, // tos-1 = tos-1 [tos], --tos
|
||||
BC_SETIND, // tos-2 [tos-1] = tos, tos -= 3
|
||||
|
||||
// math
|
||||
BC_OP_ADD,
|
||||
BC_OP_SUB,
|
||||
BC_OP_MUL,
|
||||
BC_OP_DIV,
|
||||
BC_OP_REM,
|
||||
|
||||
// bit
|
||||
BC_BIT_OR,
|
||||
BC_BIT_XOR,
|
||||
BC_BIT_AND,
|
||||
BC_BIT_SHL,
|
||||
BC_BIT_SHR,
|
||||
BC_BIT_INV,
|
||||
|
||||
// compare
|
||||
BC_OP_LT,
|
||||
BC_OP_GT,
|
||||
BC_OP_LTE,
|
||||
BC_OP_GTE,
|
||||
BC_OP_EQ,
|
||||
BC_OP_NEQ,
|
||||
|
||||
// unary
|
||||
BC_OP_NEG,
|
||||
BC_OP_POS,
|
||||
BC_OP_NOT,
|
||||
|
||||
BC_NOP,
|
||||
BC_LINE, // indicates instruction is on a new code line to the last executed instruction. used in debug mode
|
||||
|
||||
// branch
|
||||
BC_BRA, // branch always
|
||||
BC_BRZ, // branch tos equal to zero, --tos
|
||||
BC_BRNZ, // branch tos not equal to zero, --tos
|
||||
BC_BRZK, // branch tos equal to zero keep value on stack
|
||||
BC_BRNZK, // branch tos not equal to zero keep value on stack
|
||||
BC_CALL, // call op16 num parameters
|
||||
BC_RET, // return null, ++tos
|
||||
BC_RETV, // return tos
|
||||
BC_FOREACH, // op16 op16, table, iterator, leave loop complete bool on stack.
|
||||
|
||||
// stack
|
||||
BC_POP, // --tos
|
||||
BC_POP2, // tos -=2
|
||||
BC_DUP, // tos + 1 = tos, ++tos
|
||||
BC_DUP2, // tos + 1 = tos -1, tos + 2 = tos, tos += 2
|
||||
BC_SWAP, //
|
||||
BC_PUSHNULL, // push null,
|
||||
BC_PUSHINT, // push int opptr
|
||||
BC_PUSHINT0, // push 0
|
||||
BC_PUSHINT1, // push 1
|
||||
BC_PUSHFP, // push floating point op32
|
||||
BC_PUSHSTR, // push string opptr
|
||||
BC_PUSHTBL, // push table
|
||||
BC_PUSHFN, // push function opptr
|
||||
BC_PUSHTHIS, // push this
|
||||
|
||||
// get set
|
||||
BC_GETLOCAL, // get local op16 (stack offset) ++tos
|
||||
BC_SETLOCAL, // set local op16 (stack offset) --tos
|
||||
BC_GETGLOBAL, // get global opptr (symbol id) ++tos
|
||||
BC_SETGLOBAL, // set global opptr (symbol id) --tos
|
||||
BC_GETTHIS, // get this opptr (symbol id) ++tos
|
||||
BC_SETTHIS, // set this opptr (symbol id) --tos
|
||||
};
|
||||
|
||||
#if GM_COMPILE_DEBUG
|
||||
|
||||
void gmByteCodePrint(FILE * a_fp, const void * a_byteCode, int a_byteCodeLength);
|
||||
|
||||
#endif // GM_COMPILE_DEBUG
|
||||
|
||||
#endif
|
||||
158
vscript/languages/gm/src/gm/gmByteCodeGen.cpp
Normal file
158
vscript/languages/gm/src/gm/gmByteCodeGen.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmByteCodeGen.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
|
||||
gmByteCodeGen::gmByteCodeGen(void * a_context)
|
||||
{
|
||||
m_tos = 0;
|
||||
m_maxTos = 0;
|
||||
m_emitCallback = NULL;
|
||||
m_context = a_context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void gmByteCodeGen::Reset(void * a_context)
|
||||
{
|
||||
gmStreamBufferDynamic::Reset();
|
||||
m_tos = 0;
|
||||
m_maxTos = 0;
|
||||
m_emitCallback = NULL;
|
||||
m_context = a_context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool gmByteCodeGen::Emit(gmByteCode a_instruction)
|
||||
{
|
||||
if(m_emitCallback) m_emitCallback(Tell(), m_context);
|
||||
AdjustStack(a_instruction);
|
||||
*this << (gmuint32) a_instruction;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool gmByteCodeGen::Emit(gmByteCode a_instruction, gmuint32 a_operand32)
|
||||
{
|
||||
if(m_emitCallback) m_emitCallback(Tell(), m_context);
|
||||
AdjustStack(a_instruction);
|
||||
*this << (gmuint32) a_instruction;
|
||||
*this << a_operand32;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool gmByteCodeGen::EmitPtr(gmByteCode a_instruction, gmptr a_operand)
|
||||
{
|
||||
if(m_emitCallback) m_emitCallback(Tell(), m_context);
|
||||
AdjustStack(a_instruction);
|
||||
*this << ((gmuint32) a_instruction);
|
||||
*this << a_operand;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
unsigned int gmByteCodeGen::Skip(unsigned int p_n, unsigned char p_value)
|
||||
{
|
||||
unsigned int oldPos = Tell();
|
||||
if(p_n)
|
||||
{
|
||||
char * fill = (char *) alloca(p_n);
|
||||
memset(fill, p_value, p_n);
|
||||
Write(fill, p_n);
|
||||
}
|
||||
return oldPos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void gmByteCodeGen::AdjustStack(gmByteCode a_instruction)
|
||||
{
|
||||
switch(a_instruction)
|
||||
{
|
||||
case BC_NOP : break;
|
||||
case BC_LINE : break;
|
||||
|
||||
case BC_GETDOT : m_tos += 0; break;
|
||||
case BC_SETDOT : m_tos -= 2; break;
|
||||
case BC_GETIND : --m_tos; break;
|
||||
case BC_SETIND : m_tos -= 3; break;
|
||||
|
||||
case BC_BRA : break;
|
||||
case BC_BRZ : --m_tos; break;
|
||||
case BC_BRNZ : --m_tos; break;
|
||||
case BC_BRZK : break;
|
||||
case BC_BRNZK : break;
|
||||
case BC_CALL : break;
|
||||
case BC_RET : break;
|
||||
case BC_RETV : break;
|
||||
case BC_FOREACH : ++m_tos; break;
|
||||
|
||||
case BC_POP : --m_tos; break;
|
||||
case BC_POP2 : m_tos -= 2; break;
|
||||
case BC_DUP : ++m_tos; break;
|
||||
case BC_DUP2 : m_tos += 2; break;
|
||||
case BC_SWAP : break;
|
||||
case BC_PUSHNULL : ++m_tos; break;
|
||||
case BC_PUSHINT : ++m_tos; break;
|
||||
case BC_PUSHINT0 : ++m_tos; break;
|
||||
case BC_PUSHINT1 : ++m_tos; break;
|
||||
case BC_PUSHFP : ++m_tos; break;
|
||||
case BC_PUSHSTR : ++m_tos; break;
|
||||
case BC_PUSHTBL : ++m_tos; break;
|
||||
case BC_PUSHFN : ++m_tos; break;
|
||||
case BC_PUSHTHIS : ++m_tos; break;
|
||||
|
||||
case BC_GETLOCAL : ++m_tos; break;
|
||||
case BC_SETLOCAL : --m_tos; break;
|
||||
case BC_GETGLOBAL : ++m_tos; break;
|
||||
case BC_SETGLOBAL : --m_tos; break;
|
||||
case BC_GETTHIS : ++m_tos; break;
|
||||
case BC_SETTHIS : --m_tos; break;
|
||||
|
||||
case BC_OP_ADD : --m_tos; break;
|
||||
case BC_OP_SUB : --m_tos; break;
|
||||
case BC_OP_MUL : --m_tos; break;
|
||||
case BC_OP_DIV : --m_tos; break;
|
||||
case BC_OP_REM : --m_tos; break;
|
||||
|
||||
case BC_BIT_OR : --m_tos; break;
|
||||
case BC_BIT_XOR : --m_tos; break;
|
||||
case BC_BIT_AND : --m_tos; break;
|
||||
case BC_BIT_INV : --m_tos; break;
|
||||
case BC_BIT_SHL : --m_tos; break;
|
||||
case BC_BIT_SHR : --m_tos; break;
|
||||
|
||||
case BC_OP_NEG : break;
|
||||
case BC_OP_POS : break;
|
||||
case BC_OP_NOT : break;
|
||||
|
||||
case BC_OP_LT : --m_tos; break;
|
||||
case BC_OP_GT : --m_tos; break;
|
||||
case BC_OP_LTE : --m_tos; break;
|
||||
case BC_OP_GTE : --m_tos; break;
|
||||
case BC_OP_EQ : --m_tos; break;
|
||||
case BC_OP_NEQ : --m_tos; break;
|
||||
}
|
||||
|
||||
if(m_tos > m_maxTos) m_maxTos = m_tos;
|
||||
}
|
||||
|
||||
|
||||
49
vscript/languages/gm/src/gm/gmByteCodeGen.h
Normal file
49
vscript/languages/gm/src/gm/gmByteCodeGen.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
*/
|
||||
|
||||
#ifndef _GMBYTECODEGEN_H_
|
||||
#define _GMBYTECODEGEN_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmStreamBuffer.h"
|
||||
#include "gmByteCode.h"
|
||||
|
||||
/// \class gmByteCodeBuffer
|
||||
class gmByteCodeGen : public gmStreamBufferDynamic
|
||||
{
|
||||
public:
|
||||
gmByteCodeGen(void * a_context = NULL);
|
||||
virtual ~gmByteCodeGen() {}
|
||||
|
||||
void Reset(void * a_context = NULL);
|
||||
|
||||
bool Emit(gmByteCode a_instruction);
|
||||
bool Emit(gmByteCode a_instruction, gmuint32 a_operand32);
|
||||
bool EmitPtr(gmByteCode a_instruction, gmptr a_operand);
|
||||
|
||||
unsigned int Skip(unsigned int p_n, unsigned char p_value = 0);
|
||||
|
||||
/// \brief m_emitCallback will be called whenever code is emitted
|
||||
void (GM_CDECL *m_emitCallback)(int a_address, void * a_context);
|
||||
|
||||
inline int GetMaxTos() const { return m_maxTos; }
|
||||
inline int GetTos() const { return m_tos; }
|
||||
inline void SetTos(int a_tos) { m_tos = a_tos; }
|
||||
|
||||
protected:
|
||||
|
||||
void AdjustStack(gmByteCode a_instruction);
|
||||
|
||||
int m_tos;
|
||||
int m_maxTos;
|
||||
void * m_context;
|
||||
};
|
||||
|
||||
#endif // _GMBYTECODEGEN_H_
|
||||
1549
vscript/languages/gm/src/gm/gmCodeGen.cpp
Normal file
1549
vscript/languages/gm/src/gm/gmCodeGen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
49
vscript/languages/gm/src/gm/gmCodeGen.h
Normal file
49
vscript/languages/gm/src/gm/gmCodeGen.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMCODEGEN_H_
|
||||
#define _GMCODEGEN_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmLog.h"
|
||||
#include "gmCodeGenHooks.h"
|
||||
|
||||
// fwd decl
|
||||
struct gmCodeTreeNode;
|
||||
|
||||
/// \class gmCodeGen
|
||||
/// \brief gmCodeGen will create byte code for a given code tree. after parsing script into a code tree using gmCodeTree,
|
||||
/// turn it into byte code using this class. After the code gen has been run, the gmCodeTree may be unlocked.
|
||||
/// Note that the code tree is parsed into a set of functions authored using a gmCodeGenHooks implementation.
|
||||
class gmCodeGen
|
||||
{
|
||||
public:
|
||||
|
||||
/// \brief Get() will return the singleton code generator.
|
||||
static gmCodeGen& Get();
|
||||
|
||||
/// \brief FreeMemory() will free all memory allocated by the code tree. must be unlocked
|
||||
virtual void FreeMemory() = 0;
|
||||
|
||||
/// \brief Lock() will create the byte code for the given gode tree.
|
||||
/// \param a_codeTree is the code tree.
|
||||
/// \param a_hooks is the byte code authoring object.
|
||||
/// \param a_debug is true if debug info is required.
|
||||
/// \param a_log is the compile log.
|
||||
/// \return the number of errors encounted
|
||||
virtual int Lock(const gmCodeTreeNode * a_codeTree, gmCodeGenHooks * a_hooks, bool a_debug, gmLog * a_log) = 0;
|
||||
|
||||
/// \brief Unlock() will reset the code generator.
|
||||
virtual int Unlock() = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // _GMCODEGEN_H_
|
||||
17
vscript/languages/gm/src/gm/gmCodeGenHooks.cpp
Normal file
17
vscript/languages/gm/src/gm/gmCodeGenHooks.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmCodeGenHooks.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
101
vscript/languages/gm/src/gm/gmCodeGenHooks.h
Normal file
101
vscript/languages/gm/src/gm/gmCodeGenHooks.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMCODEGENHOOKS_H_
|
||||
#define _GMCODEGENHOOKS_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
|
||||
/// \struct gmLineInfo
|
||||
/// \brief gmLineInfo describes the debug info required for line number debugging
|
||||
struct gmLineInfo
|
||||
{
|
||||
int m_address; //!< byte code address
|
||||
int m_lineNumber; //!< code line number
|
||||
};
|
||||
|
||||
/// \struct gmFunctionInfo
|
||||
/// \brief gmFunctionInfo
|
||||
struct gmFunctionInfo
|
||||
{
|
||||
gmptr m_id; //!< unique id of the function (as used in BC_PUSHFN)
|
||||
bool m_root; //!< is this function the root function '__main'
|
||||
|
||||
const void * m_byteCode; //!< byte code
|
||||
int m_byteCodeLength; //!< byte code length in bytes
|
||||
int m_numParams; //!< parameter count
|
||||
int m_numLocals; //!< local variable count (includes registers)
|
||||
int m_maxStackSize; //!< required temporary storage
|
||||
|
||||
const char * m_debugName; //!< name of variable function was assigned to... may be NULL
|
||||
const char ** m_symbols; //!< param and local variable names, sizeof m_numParams + m_numLocals; (indexed by stack offset)
|
||||
int m_lineInfoCount; //!< number of entries in the line info array
|
||||
const gmLineInfo * m_lineInfo; //!< line - instruction address mapping for debugging purposes.
|
||||
};
|
||||
|
||||
/// \class gmCodeGenHooks
|
||||
/// \brief gmCodeGenHooks is an interface that is fed to the compiler. basically the code gen hooks class allows you
|
||||
/// to compile script directly into the runtime vm, or into a libary.
|
||||
class gmCodeGenHooks
|
||||
{
|
||||
public:
|
||||
gmCodeGenHooks() {}
|
||||
virtual ~gmCodeGenHooks() {}
|
||||
|
||||
/// \brief Begin() will be called by gmCodeGen at the start of compilation.
|
||||
/// \param a_debug is true if this is a debug build.
|
||||
virtual bool Begin(bool a_debug) = 0;
|
||||
|
||||
/// \brief AddFunction() is called each time the byte code for a function has been created. The memory passed in
|
||||
/// the info structure is not valid after AddFunction returns.
|
||||
/// \return true on success
|
||||
virtual bool AddFunction(gmFunctionInfo &a_functionInfo) = 0;
|
||||
|
||||
/// \brief End() is called by gmCodeGen at the end of a compilation.
|
||||
/// \param a_errors is the number of compilation errors.
|
||||
virtual bool End(int a_errors) = 0;
|
||||
|
||||
/// \brief GetFunctionId() is called for the creation of unique function ids.
|
||||
/// \return a unique id.
|
||||
virtual gmptr GetFunctionId() = 0;
|
||||
|
||||
/// \brief GetSymbolId() is called by the compiler to get a unique symbol id. this sybol id is a machine size int
|
||||
/// id written into the byte code.
|
||||
/// \return a unique id for each unique a_symbol.
|
||||
virtual gmptr GetSymbolId(const char * a_symbol) = 0;
|
||||
|
||||
/// \brief GetStringId() is called by the compiler to get a constant string id. the returned value is written into
|
||||
/// the byte code for string lookups.
|
||||
/// \return a unique id for each unique string.
|
||||
virtual gmptr GetStringId(const char * a_string) = 0;
|
||||
|
||||
/// \brief SwapEndian() returns true if the byte code is being compiled for a machine of differing endian
|
||||
virtual bool SwapEndian() const { return false; }
|
||||
};
|
||||
|
||||
|
||||
/// \class gmCodeGenHooksNull
|
||||
/// \brief used for syntax checking etc.
|
||||
class gmCodeGenHooksNull : public gmCodeGenHooks
|
||||
{
|
||||
public:
|
||||
gmCodeGenHooksNull() {}
|
||||
virtual ~gmCodeGenHooksNull() {}
|
||||
|
||||
virtual bool Begin(bool a_debug) { return true; }
|
||||
virtual bool AddFunction(gmFunctionInfo &a_functionInfo) { return true; }
|
||||
virtual bool End(int a_errors) { return true; }
|
||||
virtual gmptr GetFunctionId() { return 0; }
|
||||
virtual gmptr GetSymbolId(const char * a_symbol) { return 0; }
|
||||
virtual gmptr GetStringId(const char * a_string) { return 0; }
|
||||
};
|
||||
|
||||
#endif // _GMCODEGENHOOKS_H_
|
||||
555
vscript/languages/gm/src/gm/gmCodeTree.cpp
Normal file
555
vscript/languages/gm/src/gm/gmCodeTree.cpp
Normal file
@@ -0,0 +1,555 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmCodeTree.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
gmCodeTreeNode * g_codeTree = NULL;
|
||||
|
||||
|
||||
|
||||
gmCodeTree::gmCodeTree() :
|
||||
m_mem(1, GMCODETREE_CHAINSIZE)
|
||||
{
|
||||
g_codeTree = NULL;
|
||||
m_locked = false;
|
||||
m_errors = 0;
|
||||
m_log = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gmCodeTree::~gmCodeTree()
|
||||
{
|
||||
Unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
gmCodeTree &gmCodeTree::Get()
|
||||
{
|
||||
static gmCodeTree codeTree;
|
||||
return codeTree;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void gmCodeTree::FreeMemory()
|
||||
{
|
||||
if(m_locked == false)
|
||||
{
|
||||
m_mem.ResetAndFreeMemory();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int gmCodeTree::Lock(const char * a_script, gmLog * a_log)
|
||||
{
|
||||
if(m_locked == true) return 1;
|
||||
|
||||
m_errors = 0;
|
||||
m_locked = true;
|
||||
m_log = a_log;
|
||||
g_codeTree = NULL;
|
||||
//gmdebug = 1;
|
||||
gmlineno = 1;
|
||||
|
||||
// create a scan buffer
|
||||
YY_BUFFER_STATE buffer = gm_scan_string(a_script);
|
||||
if(buffer)
|
||||
{
|
||||
m_errors = gmparse();
|
||||
gm_delete_buffer(buffer);
|
||||
}
|
||||
return m_errors;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int gmCodeTree::Unlock()
|
||||
{
|
||||
m_mem.Reset();
|
||||
g_codeTree = NULL;
|
||||
m_locked = false;
|
||||
m_errors = 0;
|
||||
m_log = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const gmCodeTreeNode * gmCodeTree::GetCodeTree() const
|
||||
{
|
||||
return g_codeTree;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void * gmCodeTree::Alloc(int a_size, int a_align)
|
||||
{
|
||||
return m_mem.AllocBytes(a_size, a_align);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if GM_COMPILE_DEBUG
|
||||
|
||||
const char * gmGetOperatorTypeName(gmCodeTreeNodeOperationType a_type)
|
||||
{
|
||||
switch(a_type)
|
||||
{
|
||||
case CTNOT_INVALID : return "CTNOT_INVALID";
|
||||
case CTNOT_DOT : return "CTNOT_DOT";
|
||||
case CTNOT_UNARY_PLUS : return "CTNOT_UNARY_PLUS";
|
||||
case CTNOT_UNARY_MINUS : return "CTNOT_UNARY_MINUS";
|
||||
case CTNOT_UNARY_NOT : return "CTNOT_UNARY_NOT";
|
||||
case CTNOT_UNARY_COMPLEMENT : return "CTNOT_UNARY_COMPLEMENT";
|
||||
case CTNOT_ARRAY_INDEX : return "CTNOT_ARRAY_INDEX";
|
||||
case CTNOT_TIMES : return "CTNOT_TIMES";
|
||||
case CTNOT_DIVIDE : return "CTNOT_DIVIDE";
|
||||
case CTNOT_REM : return "CTNOT_REM";
|
||||
case CTNOT_ADD : return "CTNOT_ADD";
|
||||
case CTNOT_MINUS : return "CTNOT_MINUS";
|
||||
case CTNOT_LT : return "CTNOT_LT";
|
||||
case CTNOT_GT : return "CTNOT_GT";
|
||||
case CTNOT_LTE : return "CTNOT_LTE";
|
||||
case CTNOT_GTE : return "CTNOT_GTE";
|
||||
case CTNOT_EQ : return "CTNOT_EQ";
|
||||
case CTNOT_NEQ : return "CTNOT_NEQ";
|
||||
case CTNOT_AND : return "CTNOT_AND";
|
||||
case CTNOT_OR : return "CTNOT_OR";
|
||||
case CTNOT_BIT_OR : return "CTNOT_BIT_OR";
|
||||
case CTNOT_BIT_XOR : return "CTNOT_BIT_XOR";
|
||||
case CTNOT_BIT_AND : return "CTNOT_BIT_AND";
|
||||
case CTNOT_SHIFT_LEFT : return "CTNOT_SHIFT_LEFT";
|
||||
case CTNOT_SHIFT_RIGHT : return "CTNOT_SHIFT_RIGHT";
|
||||
case CTNOT_ASSIGN : return "CTNOT_ASSIGN";
|
||||
case CTNOT_ASSIGN_FIELD : return "CTNOT_ASSIGN_FIELD";
|
||||
default : break;
|
||||
}
|
||||
return "UNKNOWN OPERATOR TYPE";
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void PrintRecursive(const gmCodeTreeNode * a_node, FILE * a_fp, bool a_firstCall)
|
||||
{
|
||||
if(a_node)
|
||||
{
|
||||
static int indent;
|
||||
int i;
|
||||
|
||||
if(a_firstCall)
|
||||
{
|
||||
indent = 0;
|
||||
}
|
||||
|
||||
indent += 2;
|
||||
|
||||
while(a_node != NULL)
|
||||
{
|
||||
for(i = 0; i < indent; ++i)
|
||||
fprintf(a_fp, " ");
|
||||
|
||||
if(a_node->m_type == CTNT_DECLARATION)
|
||||
{
|
||||
//
|
||||
// DECLARATIONS
|
||||
//
|
||||
switch(a_node->m_subType)
|
||||
{
|
||||
case CTNDT_PARAMETER : fprintf(a_fp, "CTNDT_PARAMETER:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNDT_VARIABLE : fprintf(a_fp, "CTNDT_VARIABLE:%04d, type %d"GM_NL, a_node->m_lineNumber, a_node->m_subTypeType); break;
|
||||
default : fprintf(a_fp, "UNKNOWN DECLARATION:"GM_NL); break;
|
||||
}
|
||||
}
|
||||
else if(a_node->m_type == CTNT_STATEMENT)
|
||||
{
|
||||
//
|
||||
// STATEMENTS
|
||||
//
|
||||
switch(a_node->m_subType)
|
||||
{
|
||||
case CTNST_RETURN : fprintf(a_fp, "CTNST_RETURN:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNST_BREAK : fprintf(a_fp, "CTNST_BREAK:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNST_CONTINUE : fprintf(a_fp, "CTNST_CONTINUE:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNST_FOR : fprintf(a_fp, "CTNST_FOR:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNST_FOREACH : fprintf(a_fp, "CTNST_FOREACH:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNST_WHILE : fprintf(a_fp, "CTNST_WHILE:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNST_DOWHILE : fprintf(a_fp, "CTNST_DOWHILE:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNST_IF : fprintf(a_fp, "CTNST_IF:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNST_COMPOUND : fprintf(a_fp, "CTNST_COMPOUND:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
default : fprintf(a_fp, "UNKNOWN STATEMENT:"GM_NL); break;
|
||||
}
|
||||
}
|
||||
else if(a_node->m_type == CTNT_EXPRESSION)
|
||||
{
|
||||
//
|
||||
// EXPRESSIONS
|
||||
//
|
||||
switch(a_node->m_subType)
|
||||
{
|
||||
case CTNET_OPERATION :
|
||||
{
|
||||
if(a_node->m_subTypeType < CTNOT_MAX)
|
||||
{
|
||||
fprintf(a_fp, "CTNET_OPERATION:%04d : %s"GM_NL, a_node->m_lineNumber, gmGetOperatorTypeName((gmCodeTreeNodeOperationType) a_node->m_subTypeType));
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(a_fp, "UNKNOWN CTNET_OPERATION"GM_NL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CTNET_CONSTANT :
|
||||
{
|
||||
switch(a_node->m_subTypeType)
|
||||
{
|
||||
case CTNCT_INT : fprintf(a_fp, "CTNCT_INT:%04d : %d"GM_NL, a_node->m_lineNumber, a_node->m_data.m_iValue); break;
|
||||
case CTNCT_FLOAT : fprintf(a_fp, "CTNCT_FLOAT:%04d : %f"GM_NL, a_node->m_lineNumber, a_node->m_data.m_fValue); break;
|
||||
case CTNCT_STRING : fprintf(a_fp, "CTNCT_STRING:%04d : %s"GM_NL, a_node->m_lineNumber, a_node->m_data.m_string); break;
|
||||
case CTNCT_NULL : fprintf(a_fp, "CTNCT_NULL:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
default: fprintf(a_fp, "UNKNOWN CTNET_CONSTANT"GM_NL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CTNET_IDENTIFIER : fprintf(a_fp, "CTNET_IDENTIFIER:%04d : %s"GM_NL, a_node->m_lineNumber, a_node->m_data.m_string); break;
|
||||
case CTNET_THIS : fprintf(a_fp, "CTNET_THIS:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNET_CALL : fprintf(a_fp, "CTNET_CALL:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNET_FUNCTION : fprintf(a_fp, "CTNET_FUNCTION:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
case CTNET_TABLE : fprintf(a_fp, "CTNET_TABLE:%04d"GM_NL, a_node->m_lineNumber); break;
|
||||
default : fprintf(a_fp, "UNKNOWN EXPRESSION:"GM_NL); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(a_fp, "UNKNOWN NODE TYPE"GM_NL);
|
||||
}
|
||||
|
||||
// print the child nodes
|
||||
for(i = 0; i < GMCODETREE_NUMCHILDREN; ++i)
|
||||
{
|
||||
if(a_node->m_children[i])
|
||||
{
|
||||
PrintRecursive(a_node->m_children[i], a_fp, false);
|
||||
}
|
||||
}
|
||||
a_node = a_node->m_sibling;
|
||||
|
||||
} // while(a_node != NULL)
|
||||
|
||||
indent -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gmCodeTree::Print(FILE * a_fp)
|
||||
{
|
||||
if(m_locked)
|
||||
{
|
||||
PrintRecursive(g_codeTree, a_fp, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // GM_COMPILE_DEBUG
|
||||
|
||||
|
||||
|
||||
gmCodeTreeNode * gmCodeTreeNode::Create(gmCodeTreeNodeType a_type, int a_subType, int a_lineNumber, int a_subTypeType)
|
||||
{
|
||||
gmCodeTreeNode * node = (gmCodeTreeNode *) gmCodeTree::Get().Alloc(sizeof(gmCodeTreeNode), GM_DEFAULT_ALLOC_ALIGNMENT);
|
||||
GM_ASSERT(node != NULL);
|
||||
memset(node, 0, sizeof(gmCodeTreeNode));
|
||||
node->m_type = a_type;
|
||||
node->m_subType = a_subType;
|
||||
node->m_lineNumber = a_lineNumber;
|
||||
node->m_subTypeType = a_subTypeType;
|
||||
node->m_flags = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void gmCodeTreeNode::SetChild(int a_index, gmCodeTreeNode * a_node)
|
||||
{
|
||||
GM_ASSERT(a_index >= 0 && a_index < GMCODETREE_NUMCHILDREN);
|
||||
|
||||
m_children[a_index] = a_node;
|
||||
if(a_node != NULL)
|
||||
{
|
||||
a_node->m_parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool gmFold(float &a_r, float a_a, int a_op)
|
||||
{
|
||||
switch(a_op)
|
||||
{
|
||||
case CTNOT_UNARY_PLUS : a_r = a_a; break;
|
||||
case CTNOT_UNARY_MINUS : a_r = -a_a; break;
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool gmFold(int &a_r, int a_a, int a_op)
|
||||
{
|
||||
switch(a_op)
|
||||
{
|
||||
case CTNOT_UNARY_PLUS : a_r = a_a; break;
|
||||
case CTNOT_UNARY_MINUS : a_r = -a_a; break;
|
||||
case CTNOT_UNARY_NOT : a_r = !a_a; break;
|
||||
case CTNOT_UNARY_COMPLEMENT : a_r = ~a_a; break;
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#include <math.h>
|
||||
static bool gmFold(float &a_r, float a_a, float a_b, int a_op)
|
||||
{
|
||||
switch(a_op)
|
||||
{
|
||||
case CTNOT_TIMES : a_r = a_a * a_b; break;
|
||||
case CTNOT_DIVIDE : if(a_b == 0) return false; a_r = a_a / a_b; break;
|
||||
case CTNOT_REM : a_r = fmodf(a_a, a_b); break;
|
||||
case CTNOT_ADD : a_r = a_a + a_b; break;
|
||||
case CTNOT_MINUS : a_r = a_a - a_b; break;
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool gmFold(int &a_r, int a_a, int a_b, int a_op)
|
||||
{
|
||||
switch(a_op)
|
||||
{
|
||||
case CTNOT_TIMES : a_r = a_a * a_b; break;
|
||||
case CTNOT_DIVIDE : if(a_b == 0) return false; a_r = a_a / a_b; break;
|
||||
case CTNOT_REM : a_r = a_a % a_b; break;
|
||||
case CTNOT_ADD : a_r = a_a + a_b; break;
|
||||
case CTNOT_MINUS : a_r = a_a - a_b; break;
|
||||
case CTNOT_BIT_OR : a_r = a_a | a_b; break;
|
||||
case CTNOT_BIT_XOR : a_r = a_a ^ a_b; break;
|
||||
case CTNOT_BIT_AND : a_r = a_a & a_b; break;
|
||||
case CTNOT_SHIFT_LEFT : a_r = a_a << a_b; break;
|
||||
case CTNOT_SHIFT_RIGHT : a_r = a_a >> a_b; break;
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool gmCodeTreeNode::ConstantFold()
|
||||
{
|
||||
if(m_type == CTNT_EXPRESSION && m_subType == CTNET_OPERATION)
|
||||
{
|
||||
bool possibleUnaryFold = false;
|
||||
bool possibleFold = false;
|
||||
bool intOnly = false;
|
||||
|
||||
switch(m_subTypeType)
|
||||
{
|
||||
case CTNOT_UNARY_PLUS :
|
||||
case CTNOT_UNARY_MINUS :
|
||||
possibleUnaryFold = true;
|
||||
break;
|
||||
case CTNOT_UNARY_NOT :
|
||||
case CTNOT_UNARY_COMPLEMENT :
|
||||
possibleUnaryFold = true;
|
||||
intOnly = true;
|
||||
break;
|
||||
case CTNOT_TIMES :
|
||||
case CTNOT_DIVIDE :
|
||||
case CTNOT_REM :
|
||||
case CTNOT_ADD :
|
||||
case CTNOT_MINUS :
|
||||
possibleFold = true;
|
||||
break;
|
||||
case CTNOT_BIT_OR :
|
||||
case CTNOT_BIT_XOR :
|
||||
case CTNOT_BIT_AND :
|
||||
case CTNOT_SHIFT_LEFT :
|
||||
case CTNOT_SHIFT_RIGHT :
|
||||
possibleFold = true;
|
||||
intOnly = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(possibleUnaryFold)
|
||||
{
|
||||
gmCodeTreeNode * l = m_children[0];
|
||||
if(l && l->m_type == CTNT_EXPRESSION && l->m_subType == CTNET_CONSTANT)
|
||||
{
|
||||
if(l->m_subTypeType == CTNCT_INT || (l->m_subTypeType == CTNCT_FLOAT && !intOnly))
|
||||
{
|
||||
// we can fold....
|
||||
m_children[0] = NULL;
|
||||
m_subType = CTNET_CONSTANT;
|
||||
|
||||
if(l->m_subTypeType == CTNCT_INT)
|
||||
{
|
||||
gmFold(m_data.m_iValue, l->m_data.m_iValue, m_subTypeType);
|
||||
m_subTypeType = CTNCT_INT;
|
||||
}
|
||||
else if(l->m_subTypeType == CTNCT_FLOAT)
|
||||
{
|
||||
gmFold(m_data.m_fValue, l->m_data.m_fValue, m_subTypeType);
|
||||
m_subTypeType = CTNCT_FLOAT;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(possibleFold)
|
||||
{
|
||||
gmCodeTreeNode * l = m_children[0], * r = m_children[1];
|
||||
if((l && l->m_type == CTNT_EXPRESSION && l->m_subType == CTNET_CONSTANT) &&
|
||||
(r && r->m_type == CTNT_EXPRESSION && r->m_subType == CTNET_CONSTANT))
|
||||
{
|
||||
if((l->m_subTypeType == CTNCT_INT || (l->m_subTypeType == CTNCT_FLOAT && !intOnly)) &&
|
||||
(r->m_subTypeType == CTNCT_INT || (r->m_subTypeType == CTNCT_FLOAT && !intOnly)))
|
||||
{
|
||||
// we can fold....
|
||||
m_children[0] = NULL; m_children[1] = NULL;
|
||||
m_subType = CTNET_CONSTANT;
|
||||
if(l->m_subTypeType == CTNCT_INT && r->m_subTypeType == CTNCT_INT)
|
||||
{
|
||||
gmFold(m_data.m_iValue, l->m_data.m_iValue, r->m_data.m_iValue, m_subTypeType);
|
||||
m_subTypeType = CTNCT_INT;
|
||||
}
|
||||
else if(l->m_subTypeType == CTNCT_FLOAT && r->m_subTypeType == CTNCT_FLOAT)
|
||||
{
|
||||
gmFold(m_data.m_fValue, l->m_data.m_fValue, r->m_data.m_fValue, m_subTypeType);
|
||||
m_subTypeType = CTNCT_FLOAT;
|
||||
}
|
||||
else if(l->m_subTypeType == CTNCT_INT && r->m_subTypeType == CTNCT_FLOAT)
|
||||
{
|
||||
gmFold(m_data.m_fValue, (float) l->m_data.m_iValue, r->m_data.m_fValue, m_subTypeType);
|
||||
m_subTypeType = CTNCT_FLOAT;
|
||||
}
|
||||
else if(l->m_subTypeType == CTNCT_FLOAT && r->m_subTypeType == CTNCT_INT)
|
||||
{
|
||||
gmFold(m_data.m_fValue, l->m_data.m_fValue, (float) r->m_data.m_iValue, m_subTypeType);
|
||||
m_subTypeType = CTNCT_FLOAT;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void gmProcessSingleQuoteString(char * a_string)
|
||||
{
|
||||
char * c = a_string;
|
||||
char * r = a_string;
|
||||
|
||||
while(*c)
|
||||
{
|
||||
if(c[0] == '`' && c[1] == '`' && c[2])
|
||||
{
|
||||
*(r++) = *c;
|
||||
c += 2;
|
||||
continue;
|
||||
}
|
||||
else if(c[0] == '`')
|
||||
{
|
||||
++c;
|
||||
continue;
|
||||
}
|
||||
|
||||
*(r++) = *(c++);
|
||||
}
|
||||
*r = '\0';
|
||||
}
|
||||
|
||||
|
||||
|
||||
void gmProcessDoubleQuoteString(char * a_string)
|
||||
{
|
||||
char * c = a_string;
|
||||
char * r = a_string;
|
||||
|
||||
while(*c)
|
||||
{
|
||||
if(c[0] == '\"')
|
||||
{
|
||||
++c;
|
||||
continue;
|
||||
}
|
||||
else if(c[0] == '\\')
|
||||
{
|
||||
switch(c[1])
|
||||
{
|
||||
case '0' : case '1' : case '2' : case '3' : case '4' : case '5' :
|
||||
case '6' : case '7' : case '8' : case '9' :
|
||||
{
|
||||
char buffer[4]; int i = 0;
|
||||
while(i < 3 && isdigit(c[i+1]))
|
||||
{
|
||||
buffer[i] = c[i+1];
|
||||
++i;
|
||||
}
|
||||
buffer[i] = '\0';
|
||||
*r = (char) (atoi(buffer) & 0xff);
|
||||
c += (i - 1);
|
||||
break;
|
||||
}
|
||||
case 'a' : *r = '\a'; break;
|
||||
case 'b' : *r = '\b'; break;
|
||||
case 'f' : *r = '\f'; break;
|
||||
case 'n' : *r = '\n'; break;
|
||||
case 'r' : *r = '\r'; break;
|
||||
case 't' : *r = '\t'; break;
|
||||
case 'v' : *r = '\v'; break;
|
||||
case '\'' : *r = '\''; break;
|
||||
case '\"' : *r = '\"'; break;
|
||||
case '\\' : *r = '\\'; break;
|
||||
default: *r = c[1];
|
||||
}
|
||||
++r;
|
||||
c += 2;
|
||||
continue;
|
||||
}
|
||||
*(r++) = *(c++);
|
||||
}
|
||||
*r = '\0';
|
||||
}
|
||||
|
||||
|
||||
|
||||
int gmerror(char * a_message)
|
||||
{
|
||||
gmCodeTree & ct = gmCodeTree::Get();
|
||||
if(ct.GetLog())
|
||||
{
|
||||
ct.GetLog()->LogEntry("error (%d) %s", gmlineno, a_message);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
254
vscript/languages/gm/src/gm/gmCodeTree.h
Normal file
254
vscript/languages/gm/src/gm/gmCodeTree.h
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMCODETREE_H_
|
||||
#define _GMCODETREE_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmMem.h"
|
||||
#include "gmMemChain.h"
|
||||
#include "gmLog.h"
|
||||
#include "gmScanner.h"
|
||||
|
||||
#define GMCODETREE_NUMCHILDREN 4
|
||||
|
||||
// fwd decl
|
||||
struct gmCodeTreeNode;
|
||||
|
||||
/// \class gmCodeTree
|
||||
/// \brief gmCodeTree is a singleton class for creating code trees.
|
||||
class gmCodeTree
|
||||
{
|
||||
protected:
|
||||
gmCodeTree();
|
||||
|
||||
public:
|
||||
~gmCodeTree();
|
||||
|
||||
/// \brief Get() will return the singlton parser.
|
||||
static gmCodeTree &Get();
|
||||
|
||||
/// \brief FreeMemory() will free all memory allocated by the code tree. must be unlocked
|
||||
void FreeMemory();
|
||||
|
||||
/// \brief Lock() will create a code tree for the passed script. Note that the code tree is valid until
|
||||
/// Unlock() is called.
|
||||
/// \param a_script is a null terminated script string.
|
||||
/// \return the number of errors encounted when parsing.
|
||||
/// \sa Unlock()
|
||||
int Lock(const char * a_script, gmLog * a_log = NULL);
|
||||
|
||||
/// \brief Unlock() will unlock the singleton code tree such that it may be used again.
|
||||
/// \return 0 on success
|
||||
/// \sa Lock()
|
||||
int Unlock();
|
||||
|
||||
/// \brief GetCodeTree() will return the code tree resulting from a Lock() operation.
|
||||
/// \return NULL on failure
|
||||
/// \sa Lock()
|
||||
const gmCodeTreeNode * GetCodeTree() const;
|
||||
|
||||
inline gmLog * GetLog() const { return m_log; }
|
||||
|
||||
/// \brief Alloc() will return memory from the code tree memory pool. This method is used by the
|
||||
/// parser when building the code tree.
|
||||
/// \return NULL on failure
|
||||
void * Alloc(int a_size, int a_align = GM_DEFAULT_ALLOC_ALIGNMENT);
|
||||
|
||||
#if GM_COMPILE_DEBUG
|
||||
|
||||
/// \brief Print() will write the tree to the given file. this is purely for debugging.
|
||||
/// \param a_fp is an open file for writing.
|
||||
void Print(FILE * a_fp);
|
||||
|
||||
#endif // GM_COMPILE_DEBUG
|
||||
|
||||
private:
|
||||
|
||||
bool m_locked;
|
||||
int m_errors;
|
||||
gmLog * m_log;
|
||||
gmMemChain m_mem;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \enum gmCodeTreeNodeType
|
||||
/// \brief gmCodeTreeNodeType indicates the type of a gmCodeTreeNode.
|
||||
enum gmCodeTreeNodeType
|
||||
{
|
||||
CTNT_INVALID = 0,
|
||||
CTNT_DECLARATION,
|
||||
CTNT_STATEMENT,
|
||||
CTNT_EXPRESSION,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \enum gmCodeTreeNodeDeclarationType
|
||||
/// \brief if a tree node is of type CTNT_DECLARATION, gmCodeTreeNodeDeclarationType are the sub types
|
||||
enum gmCodeTreeNodeDeclarationType
|
||||
{
|
||||
CTNDT_PARAMETER = 0,
|
||||
CTNDT_VARIABLE,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \enum gmCodeTreeVariableType
|
||||
/// \brief if a treenode is CTNT_DECLARATION, CTNDT_VARIABLE, this is the type of variable decl.
|
||||
enum gmCodeTreeVariableType
|
||||
{
|
||||
CTVT_LOCAL = 0,
|
||||
CTVT_GLOBAL,
|
||||
CTVT_MEMBER,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \enum gmCodeTreeNodeStatementType
|
||||
/// \brief if a tree node is of type CTNT_STATEMENT, gmCodeTreeNodeStatementType are the sub types
|
||||
enum gmCodeTreeNodeStatementType
|
||||
{
|
||||
CTNST_INVALID = 0,
|
||||
CTNST_RETURN,
|
||||
CTNST_BREAK,
|
||||
CTNST_CONTINUE,
|
||||
CTNST_FOR,
|
||||
CTNST_FOREACH,
|
||||
CTNST_WHILE,
|
||||
CTNST_DOWHILE,
|
||||
CTNST_IF,
|
||||
CTNST_COMPOUND,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \enum gmCodeTreeNodeExpressionType
|
||||
/// \brief
|
||||
enum gmCodeTreeNodeExpressionType
|
||||
{
|
||||
CTNET_INVALID = 0,
|
||||
CTNET_OPERATION,
|
||||
CTNET_CONSTANT,
|
||||
CTNET_IDENTIFIER,
|
||||
CTNET_THIS,
|
||||
CTNET_CALL,
|
||||
CTNET_FUNCTION,
|
||||
CTNET_TABLE,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \enum gmCodeTreeNodeOperationType
|
||||
/// \brief
|
||||
enum gmCodeTreeNodeOperationType
|
||||
{
|
||||
CTNOT_INVALID = 0,
|
||||
CTNOT_DOT,
|
||||
CTNOT_UNARY_PLUS,
|
||||
CTNOT_UNARY_MINUS,
|
||||
CTNOT_UNARY_COMPLEMENT,
|
||||
CTNOT_UNARY_NOT,
|
||||
CTNOT_ARRAY_INDEX,
|
||||
CTNOT_TIMES,
|
||||
CTNOT_DIVIDE,
|
||||
CTNOT_REM,
|
||||
CTNOT_ADD,
|
||||
CTNOT_MINUS,
|
||||
CTNOT_LT,
|
||||
CTNOT_GT,
|
||||
CTNOT_LTE,
|
||||
CTNOT_GTE,
|
||||
CTNOT_EQ,
|
||||
CTNOT_NEQ,
|
||||
CTNOT_AND,
|
||||
CTNOT_OR,
|
||||
CTNOT_BIT_OR,
|
||||
CTNOT_BIT_XOR,
|
||||
CTNOT_BIT_AND,
|
||||
CTNOT_SHIFT_LEFT,
|
||||
CTNOT_SHIFT_RIGHT,
|
||||
CTNOT_ASSIGN,
|
||||
CTNOT_ASSIGN_FIELD,
|
||||
CTNOT_MAX,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \enum gmCodeTreeNodeConstantType
|
||||
enum gmCodeTreeNodeConstantType
|
||||
{
|
||||
CTNCT_INVALID = 0,
|
||||
CTNCT_INT,
|
||||
CTNCT_FLOAT,
|
||||
CTNCT_STRING,
|
||||
CTNCT_NULL,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \union gmCodeTreeNodeUnion
|
||||
/// \brief
|
||||
union gmCodeTreeNodeData
|
||||
{
|
||||
char * m_string;
|
||||
int m_iValue;
|
||||
float m_fValue;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \struct gmCodeTreeNode
|
||||
/// \brief gmCodeTreeNode is the tree node structure used to represent the game monkey script syntax tree.
|
||||
struct gmCodeTreeNode
|
||||
{
|
||||
// flags
|
||||
enum
|
||||
{
|
||||
CTN_POP = (1 << 0),
|
||||
CTN_MEMBER = (1 << 1),
|
||||
};
|
||||
|
||||
/// \brief Create() will create a tree node. the singleton gmCodeTree must be locked.
|
||||
/// \return a tree node
|
||||
static gmCodeTreeNode * Create(gmCodeTreeNodeType a_type, int a_subType, int a_lineNumber, int a_subTypeType = 0);
|
||||
|
||||
/// \brief SetChild() will set the child at the given index.
|
||||
/// \param a_node is the child node, whose parent pointer will be assigned to this.
|
||||
void SetChild(int a_index, gmCodeTreeNode * a_node);
|
||||
|
||||
/// \brief ConstantFold() will pull child nodes into this node, and make this node a constant if possible
|
||||
bool ConstantFold();
|
||||
|
||||
gmCodeTreeNodeType m_type;
|
||||
int m_subType;
|
||||
int m_subTypeType;
|
||||
int m_flags;
|
||||
|
||||
gmCodeTreeNode * m_children[GMCODETREE_NUMCHILDREN];
|
||||
gmCodeTreeNode * m_sibling;
|
||||
gmCodeTreeNode * m_parent;
|
||||
|
||||
int m_lineNumber;
|
||||
gmCodeTreeNodeData m_data;
|
||||
};
|
||||
|
||||
//
|
||||
// misc lexing and parsing functions.
|
||||
//
|
||||
|
||||
void gmProcessSingleQuoteString(char * a_string);
|
||||
void gmProcessDoubleQuoteString(char * a_string);
|
||||
int gmerror(char * a_message);
|
||||
int gmparse(void);
|
||||
|
||||
#endif // _GMCODETREE_H_
|
||||
106
vscript/languages/gm/src/gm/gmConfig.h
Normal file
106
vscript/languages/gm/src/gm/gmConfig.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMCONFIG_H_
|
||||
#define _GMCONFIG_H_
|
||||
|
||||
// Include the platform config.
|
||||
// All platform configuration exists in gmconfig_p.h
|
||||
#include "gmConfig_p.h"
|
||||
|
||||
#include <stdlib.h> // atoi, strtoul (binds: rand, srand)
|
||||
#include <stdio.h> // fprintf, sprintf, _snprintf, _vnsprintf
|
||||
#include <string.h> // stricmp, strcmp, strcpy, strlen, strcat, memset, memcpy (binds: strlwr, wtrupr, strspn, strcspn, strchr, strstr)
|
||||
#include <stdarg.h> // va_start, va_end
|
||||
#include <ctype.h> // isdigit
|
||||
#include <math.h> // floorf, fmodf
|
||||
|
||||
|
||||
/// \enum gmEndian Endian byte order
|
||||
enum gmEndian
|
||||
{
|
||||
GM_ENDIAN_BIG = 0, //!< MOTOROLA (MAC), NINTENDO GC
|
||||
GM_ENDIAN_LITTLE = 1 //!< x86, XBOX, PS2
|
||||
};
|
||||
|
||||
//
|
||||
// Game Monkey Configuration
|
||||
//
|
||||
|
||||
// COMPILE
|
||||
|
||||
#define GM_COMPILE_DEBUG 1 // define for compile debugging code, ie, printing code trees, byte code etc.
|
||||
|
||||
// COMPILE LOG
|
||||
|
||||
#define GMLOG_CHAINSIZE 2048 // memory chunk resolution for the compile log
|
||||
|
||||
// COMPILE PARSER
|
||||
|
||||
#define GMCODETREE_CHAINSIZE 4096 // memory chunk resolution for compiler code tree nodes.
|
||||
|
||||
// COMPILER CODE GENERATOR
|
||||
|
||||
#define GM_COMPILE_PASS_THIS_ALWAYS 0 // set to 1 to pass current this to each function call
|
||||
|
||||
// RUNTIME THREAD
|
||||
|
||||
#define GMTHREAD_INITIALBYTESIZE 512 // initial stack byte size for a single thread
|
||||
#define GMTHREAD_MAXBYTESIZE 128000 //1024 // max stack byte size for a single thread (Sample scripts like it big)
|
||||
|
||||
// MACHINE
|
||||
|
||||
#define GMMACHINE_REMOVECOMPILER 0 // Remove compiler code, will only be able to run precompiled libs
|
||||
#define GMMACHINE_GMCHECKDIVBYZERO 0 // Let GM operator check for divide by zero and possibly cause GM run time exception (rather than OS exception)
|
||||
#define GMMACHINE_NULL_VAR_CTOR 0 // Nullify gmVariable in constructor. Not recommended for real-time / time critical applications.
|
||||
#define GMMACHINE_USERTYPEGROWBY 16 // allocate user types in chunks of this size
|
||||
#define GMMACHINE_OBJECTCHUNKSIZE 32 // default object chunk allocation size
|
||||
#define GMMACHINE_TBLCHUNKSIZE 32 // table object chunk allocation size
|
||||
#define GMMACHINE_STRINGCHUNKSIZE 128 // default object chunk allocation size
|
||||
#define GMMACHINE_STACKFCHUNKSIZE 128 // stack frame chunk size
|
||||
#define GMMACHINE_AUTOMEM true // automatically decide garbage collection limit
|
||||
#define GMMACHINE_AUTOMEMMULTIPY 2.5f // after gc cycle, set limit = current * GMMACHINE_AUTOMEMMULTIPY (This is for atomic GC)
|
||||
#define GMMACHINE_AUTOMEMALLOWSHRINK 0 // Allow memory liimits to shrink, otherwise memory will grow when needed only
|
||||
#define GMMACHINE_INITIALGCHARDLIMIT 128*1024 // default gc hard memory limit.
|
||||
#define GMMACHINE_INITIALGCSOFTLIMIT (GMMACHINE_INITIALGCHARDLIMIT * 9 / 10) // default gc soft memory limit
|
||||
#define GMMACHINE_STRINGHASHSIZE 8192 // this will be dynamic... todo
|
||||
#define GMMACHINE_MAXKILLEDTHREADS 16 // max size of the free thread list (don't make too large, ie, < 32)
|
||||
#define GMMACHINE_GCEVERYALLOC 0 // define this to check garbage collection every allocate.
|
||||
#define GMMACHINE_SUPERPARANOIDGC 0 // validate references (only for debugging purposes)
|
||||
#define GMMACHINE_THREEPASSGC 0 // 1 for safe gc of persisting objects that reference other objects,
|
||||
// ie, persisting tables. if you only have persisting simple objects, ie
|
||||
// strings, set to 0 for faster garbage collection.
|
||||
|
||||
// Auto GC Calibration values
|
||||
#define GMMACHINE_GC_HARD_MEM_INC_FRAC_OF_USED 1.5f // what to set hard limit to above used mem when growing hard limit
|
||||
#define GMMACHINE_GC_HARD_MEM_DEC_FRAC_OF_USED 1.5f // what to set hard limit to above used mem when shinking hard limit
|
||||
#define GMMACHINE_GC_SOFT_MEM_DEFAULT_FRAC_OF_HARD (9.0f/10.0f) // what to set soft limit as frac of hard limit by default
|
||||
#define GMMACHINE_GC_HARD_MEM_SHRINK_THRESH 0.5f // threshold at which hard limit should shrink
|
||||
#define GMMACHINE_GC_SOFT_MEM_MIN_FRAC 0.25f // minimum soft limit as frac of hard limit to shrink soft limit
|
||||
#define GMMACHINE_GC_SOFT_MEM_DEC_FRAC 0.1f // amount to shrink soft limit as frac of soft/hard
|
||||
#define GMMACHINE_GC_MIN_FRAMES_SINCE_RESTART 100 // if gc is restarting within this many frames/calls, it is probably configured bad
|
||||
#define GM_GC_DEFAULT_WORK_INCREMENT 200 // Desired number of objects to trace per frame
|
||||
#define GM_GC_DEFAULT_DESTRUCT_INCREMENT 200 // Desired number of old objects to free per frame
|
||||
|
||||
#define GMMACHINE_CPPOWNEDGMOBJHASHSIZE 1024 // default hash table size for objects owned by cpp code, necessary for GC.
|
||||
|
||||
// DEBUGGING
|
||||
|
||||
#define GMDEBUG_SUPPORT 1 // allow use with the gm debugger
|
||||
|
||||
|
||||
// GARBAGE COLLECTOR
|
||||
#define GM_USE_INCGC 1 // use incremental garbage collector
|
||||
|
||||
|
||||
#define GM_BOOL_OP 1 // Spport for a bool operator on user types for use in if statements. For full effect, users will want to implement operators [bool, ==, !=, !]
|
||||
|
||||
#endif // _GMCONFIG_H_
|
||||
84
vscript/languages/gm/src/gm/gmCrc.cpp
Normal file
84
vscript/languages/gm/src/gm/gmCrc.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmCrc.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
#define _gmUPDC32(A,C) (s_gmCrc32Table[((C) ^ ((gmuint8) A)) & 0xff] ^ ((C) >> 8))
|
||||
|
||||
// CRC polynomial 0xedb88320
|
||||
static gmuint32 s_gmCrc32Table[] =
|
||||
{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
|
||||
|
||||
gmuint32 gmCrc32String(const char *p_string)
|
||||
{
|
||||
register gmuint32 crc32;
|
||||
|
||||
crc32 = 0xffffffff;
|
||||
|
||||
for (; *p_string; ++p_string)
|
||||
{
|
||||
crc32 = _gmUPDC32(*p_string, crc32);
|
||||
}
|
||||
|
||||
return ~crc32;
|
||||
}
|
||||
|
||||
19
vscript/languages/gm/src/gm/gmCrc.h
Normal file
19
vscript/languages/gm/src/gm/gmCrc.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMCRC_H_
|
||||
#define _GMCRC_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
|
||||
gmuint32 gmCrc32String(const char *p_string);
|
||||
|
||||
#endif // _GMCRC_H_
|
||||
741
vscript/languages/gm/src/gm/gmDebug.cpp
Normal file
741
vscript/languages/gm/src/gm/gmDebug.cpp
Normal file
@@ -0,0 +1,741 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmDebug.h"
|
||||
#include "gmConfig.h"
|
||||
#include "gmMachine.h"
|
||||
#include "gmThread.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
#if GMDEBUG_SUPPORT
|
||||
|
||||
|
||||
#define ID_mrun GM_MAKE_ID32('m','r','u','n')
|
||||
#define ID_msin GM_MAKE_ID32('m','s','i','n')
|
||||
#define ID_msou GM_MAKE_ID32('m','s','o','u')
|
||||
#define ID_msov GM_MAKE_ID32('m','s','o','v')
|
||||
#define ID_mgct GM_MAKE_ID32('m','g','c','t')
|
||||
#define ID_mgsr GM_MAKE_ID32('m','g','s','r')
|
||||
#define ID_mgsi GM_MAKE_ID32('m','g','s','i')
|
||||
#define ID_mgti GM_MAKE_ID32('m','g','t','i')
|
||||
#define ID_mgvi GM_MAKE_ID32('m','g','v','i')
|
||||
#define ID_msbp GM_MAKE_ID32('m','s','b','p')
|
||||
#define ID_mbrk GM_MAKE_ID32('m','b','r','k')
|
||||
#define ID_mend GM_MAKE_ID32('m','e','n','d')
|
||||
|
||||
#define ID_dbrk GM_MAKE_ID32('d','b','r','k')
|
||||
#define ID_dexc GM_MAKE_ID32('d','e','x','c')
|
||||
#define ID_drun GM_MAKE_ID32('d','r','u','n')
|
||||
#define ID_dstp GM_MAKE_ID32('d','s','t','p')
|
||||
#define ID_dsrc GM_MAKE_ID32('d','s','r','c')
|
||||
#define ID_dctx GM_MAKE_ID32('d','c','t','x')
|
||||
#define ID_call GM_MAKE_ID32('c','a','l','l')
|
||||
#define ID_vari GM_MAKE_ID32('v','a','r','i')
|
||||
#define ID_done GM_MAKE_ID32('d','o','n','e')
|
||||
#define ID_dsri GM_MAKE_ID32('d','s','r','i')
|
||||
#define ID_srci GM_MAKE_ID32('s','r','c','i')
|
||||
#define ID_done GM_MAKE_ID32('d','o','n','e')
|
||||
#define ID_dthi GM_MAKE_ID32('d','t','h','i')
|
||||
#define ID_thri GM_MAKE_ID32('t','h','r','i')
|
||||
#define ID_done GM_MAKE_ID32('d','o','n','e')
|
||||
#define ID_derr GM_MAKE_ID32('d','e','r','r')
|
||||
#define ID_dmsg GM_MAKE_ID32('d','m','s','g')
|
||||
#define ID_dack GM_MAKE_ID32('d','a','c','k')
|
||||
#define ID_dend GM_MAKE_ID32('d','e','n','d')
|
||||
|
||||
|
||||
//
|
||||
// functions to handle incomming commands from a debugger
|
||||
//
|
||||
|
||||
void gmMachineRun(gmDebugSession * a_session, int a_threadId);
|
||||
void gmMachineStepInto(gmDebugSession * a_session, int a_threadId);
|
||||
void gmMachineStepOver(gmDebugSession * a_session, int a_threadId);
|
||||
void gmMachineStepOut(gmDebugSession * a_session, int a_threadId);
|
||||
void gmMachineGetContext(gmDebugSession * a_session, int a_threadId, int a_callframe);
|
||||
void gmMachineGetSource(gmDebugSession * a_session, int a_sourceId);
|
||||
void gmMachineGetSourceInfo(gmDebugSession * a_session);
|
||||
void gmMachineGetThreadInfo(gmDebugSession * a_session);
|
||||
void gmMachineGetVariableInfo(gmDebugSession * a_session, int a_variableId);
|
||||
void gmMachineSetBreakPoint(gmDebugSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled);
|
||||
void gmMachineBreak(gmDebugSession * a_session, int a_threadId);
|
||||
void gmMachineQuit(gmDebugSession * a_session);
|
||||
|
||||
//
|
||||
// functions to package outgoing messages to a debugger
|
||||
//
|
||||
|
||||
void gmDebuggerBreak(gmDebugSession * a_session, int a_threadId, int a_sourceId, int a_lineNumber);
|
||||
void gmDebuggerRun(gmDebugSession * a_session, int a_threadId);
|
||||
void gmDebuggerStop(gmDebugSession * a_session, int a_threadId);
|
||||
void gmDebuggerSource(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName, const char * a_source);
|
||||
void gmDebuggerException(gmDebugSession * a_session, int a_threadId);
|
||||
|
||||
void gmDebuggerBeginContext(gmDebugSession * a_session, int a_threadId, int a_callFrame);
|
||||
void gmDebuggerContextCallFrame(gmDebugSession * a_session, int a_callFrame, const char * a_functionName, int a_sourceId, int a_lineNumber, const char * a_thisSymbol, const char * a_thisValue, int a_thisId);
|
||||
void gmDebuggerContextVariable(gmDebugSession * a_session, const char * a_varSymbol, const char * a_varValue, int a_varId);
|
||||
void gmDebuggerEndContext(gmDebugSession * a_session);
|
||||
|
||||
void gmDebuggerBeginSourceInfo(gmDebugSession * a_session);
|
||||
void gmDebuggerSourceInfo(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName);
|
||||
void gmDebuggerEndSourceInfo(gmDebugSession * a_session);
|
||||
|
||||
void gmDebuggerBeginThreadInfo(gmDebugSession * a_session);
|
||||
void gmDebuggerThreadInfo(gmDebugSession * a_session, int a_threadId, int a_threadState);
|
||||
void gmDebuggerEndThreadInfo(gmDebugSession * a_session);
|
||||
|
||||
void gmDebuggerError(gmDebugSession * a_session, const char * a_error);
|
||||
void gmDebuggerMessage(gmDebugSession * a_session, const char * a_message);
|
||||
void gmDebuggerAck(gmDebugSession * a_session, int a_response, int a_posNeg);
|
||||
void gmDebuggerQuit(gmDebugSession * a_session);
|
||||
|
||||
//
|
||||
// debug machine callback
|
||||
//
|
||||
|
||||
enum gmdThreadFlags
|
||||
{
|
||||
TF_STEPOVER = (1 << 0),
|
||||
TF_STEPINTO = (1 << 1),
|
||||
TF_STEPOUT = (1 << 2),
|
||||
TF_BREAK = (1 << 3),
|
||||
};
|
||||
|
||||
// the following callbacks return true if the thread is to yield after completion of the callback.
|
||||
static bool LineCallback(gmThread * a_thread)
|
||||
{
|
||||
gmDebugSession * session = (gmDebugSession *) a_thread->GetMachine()->m_debugUser;
|
||||
GM_ASSERT(session);
|
||||
|
||||
if(!(a_thread->m_debugFlags & TF_STEPOVER) ||
|
||||
(a_thread->m_debugUser != ((a_thread->GetFrame()) ? a_thread->GetFrame()->m_returnBase : 0)))
|
||||
{
|
||||
int * bp = session->FindBreakPoint((void *) a_thread->GetInstruction());
|
||||
if(bp == NULL)
|
||||
return false;
|
||||
|
||||
if(*bp && *bp != a_thread->GetId())
|
||||
return false;
|
||||
}
|
||||
|
||||
a_thread->m_debugFlags = TF_BREAK;
|
||||
const gmFunctionObject * fn = a_thread->GetFunctionObject();
|
||||
gmDebuggerBreak(session, a_thread->GetId(), fn->GetSourceId(), fn->GetLine(a_thread->GetInstruction()));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CallCallback(gmThread * a_thread)
|
||||
{
|
||||
gmDebugSession * session = (gmDebugSession *) a_thread->GetMachine()->m_debugUser;
|
||||
GM_ASSERT(session);
|
||||
if(a_thread->m_debugFlags & TF_STEPINTO)
|
||||
{
|
||||
a_thread->m_debugFlags = TF_BREAK;
|
||||
const gmFunctionObject * fn = a_thread->GetFunctionObject();
|
||||
gmDebuggerBreak(session, a_thread->GetId(), fn->GetSourceId(), fn->GetLine(a_thread->GetInstruction()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool RetCallback(gmThread * a_thread)
|
||||
{
|
||||
gmDebugSession * session = (gmDebugSession *) a_thread->GetMachine()->m_debugUser;
|
||||
GM_ASSERT(session);
|
||||
if(((a_thread->m_debugFlags & TF_STEPOUT) && (a_thread->m_debugUser == a_thread->GetIntBase())) ||
|
||||
((a_thread->m_debugFlags & TF_STEPOVER) && (a_thread->m_debugUser == a_thread->GetIntBase())))
|
||||
{
|
||||
a_thread->m_debugFlags = TF_BREAK;
|
||||
const gmFunctionObject * fn = a_thread->GetFunctionObject();
|
||||
gmDebuggerBreak(session, a_thread->GetId(), fn->GetSourceId(), fn->GetLine(a_thread->GetInstruction()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsBrokenCallback(gmThread * a_thread)
|
||||
{
|
||||
return (a_thread->m_debugFlags & TF_BREAK) > 0;
|
||||
}
|
||||
|
||||
static gmMachineCallback s_prevMachineCallback = NULL;
|
||||
bool GM_CDECL gmdMachineCallback(gmMachine * a_machine, gmMachineCommand a_command, const void * a_context)
|
||||
{
|
||||
gmDebugSession * session = (gmDebugSession *) a_machine->m_debugUser;
|
||||
const gmThread * thread = (const gmThread *) a_context;
|
||||
|
||||
// chain callback
|
||||
if(s_prevMachineCallback) s_prevMachineCallback(a_machine, a_command, a_context);
|
||||
|
||||
// do we have a debug session?
|
||||
if(session == NULL) return false;
|
||||
|
||||
// command
|
||||
switch(a_command)
|
||||
{
|
||||
case MC_THREAD_EXCEPTION :
|
||||
{
|
||||
// send thread exception message
|
||||
gmDebuggerException(session, thread->GetId());
|
||||
a_machine->GetLog();
|
||||
bool first = true;
|
||||
const char * entry;
|
||||
while((entry = a_machine->GetLog().GetEntry(first)))
|
||||
{
|
||||
gmDebuggerError(session, entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case MC_THREAD_CREATE :
|
||||
{
|
||||
gmDebuggerRun(session, thread->GetId());
|
||||
break;
|
||||
}
|
||||
case MC_THREAD_DESTROY :
|
||||
{
|
||||
gmDebuggerStop(session, thread->GetId());
|
||||
break;
|
||||
}
|
||||
default : break;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// debug session
|
||||
//
|
||||
|
||||
gmDebugSession::gmDebugSession() :
|
||||
m_breaks(32)
|
||||
{
|
||||
m_machine = NULL;
|
||||
}
|
||||
|
||||
|
||||
gmDebugSession::~gmDebugSession()
|
||||
{
|
||||
m_breaks.RemoveAndDeleteAll();
|
||||
}
|
||||
|
||||
|
||||
void gmDebugSession::Update()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
int len;
|
||||
const void * msg = m_pumpMessage(this, len);
|
||||
if(msg == NULL)
|
||||
break;
|
||||
|
||||
m_in.Open(msg, len);
|
||||
|
||||
// parse the message
|
||||
int id, pa, pb, pc, pd;
|
||||
Unpack(id);
|
||||
switch(id)
|
||||
{
|
||||
case ID_mrun :
|
||||
Unpack(id);
|
||||
gmMachineRun(this, id);
|
||||
break;
|
||||
case ID_msin :
|
||||
Unpack(id);
|
||||
gmMachineStepInto(this, id);
|
||||
break;
|
||||
case ID_msou :
|
||||
Unpack(id);
|
||||
gmMachineStepOut(this, id);
|
||||
break;
|
||||
case ID_msov :
|
||||
Unpack(id);
|
||||
gmMachineStepOver(this, id);
|
||||
break;
|
||||
case ID_mgct :
|
||||
Unpack(id).Unpack(pa);
|
||||
gmMachineGetContext(this, id, pa);
|
||||
break;
|
||||
case ID_mgsr :
|
||||
Unpack(id);
|
||||
gmMachineGetSource(this, id);
|
||||
break;
|
||||
case ID_mgsi :
|
||||
gmMachineGetSourceInfo(this);
|
||||
break;
|
||||
case ID_mgti :
|
||||
gmMachineGetThreadInfo(this);
|
||||
break;
|
||||
case ID_mgvi :
|
||||
Unpack(id);
|
||||
gmMachineGetVariableInfo(this, id);
|
||||
break;
|
||||
case ID_msbp :
|
||||
Unpack(pa).Unpack(pb).Unpack(pc).Unpack(id).Unpack(pd);
|
||||
gmMachineSetBreakPoint(this, pa, pb, pc, id, pd);
|
||||
break;
|
||||
case ID_mbrk :
|
||||
Unpack(id);
|
||||
gmMachineBreak(this, id);
|
||||
break;
|
||||
case ID_mend :
|
||||
gmMachineQuit(this);
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool gmDebugSession::Open(gmMachine * a_machine)
|
||||
{
|
||||
Close();
|
||||
m_machine = a_machine;
|
||||
m_machine->m_debugUser = this;
|
||||
m_machine->m_line = LineCallback;
|
||||
m_machine->m_call = CallCallback;
|
||||
m_machine->m_isBroken = IsBrokenCallback;
|
||||
m_machine->m_return = RetCallback;
|
||||
s_prevMachineCallback = a_machine->s_machineCallback;
|
||||
a_machine->s_machineCallback = gmdMachineCallback;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool threadIterClose(gmThread * a_thread, void * a_context)
|
||||
{
|
||||
a_thread->m_debugFlags = 0;
|
||||
a_thread->m_debugUser = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool gmDebugSession::Close()
|
||||
{
|
||||
if(m_machine && m_machine->m_debugUser == this)
|
||||
{
|
||||
gmDebuggerQuit(this);
|
||||
|
||||
m_machine->m_debugUser = NULL;
|
||||
m_machine->s_machineCallback = s_prevMachineCallback;
|
||||
|
||||
m_machine->m_line = NULL;
|
||||
m_machine->m_call = NULL;
|
||||
m_machine->m_return = NULL;
|
||||
m_machine->m_isBroken = NULL;
|
||||
|
||||
m_machine->KillExceptionThreads();
|
||||
m_machine->ForEachThread(threadIterClose, NULL);
|
||||
m_machine = NULL;
|
||||
|
||||
m_breaks.RemoveAndDeleteAll();
|
||||
m_out.ResetAndFreeMemory();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
m_breaks.RemoveAndDeleteAll();
|
||||
m_out.ResetAndFreeMemory();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
gmDebugSession &gmDebugSession::Pack(int a_val)
|
||||
{
|
||||
m_out << a_val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
gmDebugSession &gmDebugSession::Pack(const char * a_val)
|
||||
{
|
||||
if(a_val)
|
||||
m_out.Write(a_val, strlen(a_val) + 1);
|
||||
else
|
||||
m_out.Write("", 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void gmDebugSession::Send()
|
||||
{
|
||||
m_sendMessage(this, m_out.GetData(), m_out.GetSize());
|
||||
m_out.Reset();
|
||||
}
|
||||
|
||||
|
||||
gmDebugSession &gmDebugSession::Unpack(int &a_val)
|
||||
{
|
||||
if(m_in.Read(&a_val, 4) != 4) a_val = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
gmDebugSession &gmDebugSession::Unpack(const char * &a_val)
|
||||
{
|
||||
// this is dangerous!!!
|
||||
a_val = &m_in.GetData()[m_in.Tell()];
|
||||
int len = strlen(a_val);
|
||||
m_in.Seek(m_in.Tell() + len + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool gmDebugSession::AddBreakPoint(const void * a_bp, int a_threadId)
|
||||
{
|
||||
BreakPoint * bp = m_breaks.Find((void *const&)a_bp);
|
||||
if(bp) return false;
|
||||
bp = GM_NEW( BreakPoint() );
|
||||
bp->m_bp = a_bp;
|
||||
bp->m_threadId = a_threadId;
|
||||
m_breaks.Insert(bp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int * gmDebugSession::FindBreakPoint(const void * a_bp)
|
||||
{
|
||||
BreakPoint * bp = m_breaks.Find((void *const&)a_bp);
|
||||
if(bp)
|
||||
{
|
||||
return &bp->m_threadId;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool gmDebugSession::RemoveBreakPoint(const void * a_bp)
|
||||
{
|
||||
BreakPoint * bp = m_breaks.Find((void *const&)a_bp);
|
||||
if(bp)
|
||||
{
|
||||
m_breaks.Remove(bp);
|
||||
delete bp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// implementation
|
||||
//
|
||||
|
||||
void gmMachineRun(gmDebugSession * a_session, int a_threadId)
|
||||
{
|
||||
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
|
||||
if(thread)
|
||||
{
|
||||
thread->m_debugFlags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void gmMachineStepInto(gmDebugSession * a_session, int a_threadId)
|
||||
{
|
||||
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
|
||||
if(thread)
|
||||
{
|
||||
thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
|
||||
thread->m_debugFlags = TF_STEPINTO | TF_STEPOVER;
|
||||
}
|
||||
}
|
||||
|
||||
void gmMachineStepOver(gmDebugSession * a_session, int a_threadId)
|
||||
{
|
||||
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
|
||||
if(thread)
|
||||
{
|
||||
thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
|
||||
thread->m_debugFlags = TF_STEPOVER;
|
||||
}
|
||||
}
|
||||
|
||||
void gmMachineStepOut(gmDebugSession * a_session, int a_threadId)
|
||||
{
|
||||
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
|
||||
if(thread)
|
||||
{
|
||||
thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
|
||||
thread->m_debugFlags = TF_STEPOUT;
|
||||
}
|
||||
}
|
||||
|
||||
void gmMachineGetContext(gmDebugSession * a_session, int a_threadId, int a_callframe)
|
||||
{
|
||||
const int buffSize = 256;
|
||||
char buff[buffSize]; // buff is used for AsString
|
||||
|
||||
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
|
||||
if(thread)
|
||||
{
|
||||
// count the number of frames on the thread
|
||||
int numFrames = 0;
|
||||
const gmStackFrame * frame = thread->GetFrame();
|
||||
while(frame)
|
||||
{
|
||||
++numFrames;
|
||||
frame = frame->m_prev;
|
||||
}
|
||||
|
||||
// if a valid frame was requested, fill out a context.
|
||||
if(a_callframe >= 0 && a_callframe <= numFrames)
|
||||
{
|
||||
gmDebuggerBeginContext(a_session, a_threadId, a_callframe);
|
||||
|
||||
// pack frames
|
||||
frame = thread->GetFrame();
|
||||
numFrames = 0;
|
||||
|
||||
gmVariable * base = thread->GetBase();
|
||||
const gmuint8 * ip = thread->GetInstruction();
|
||||
|
||||
while(frame)
|
||||
{
|
||||
// get the function object
|
||||
gmVariable * fnVar = base - 1;
|
||||
if(fnVar->m_type == GM_FUNCTION)
|
||||
{
|
||||
gmFunctionObject * fn = (gmFunctionObject *) GM_MOBJECT(thread->GetMachine(), fnVar->m_value.m_ref);
|
||||
|
||||
// this
|
||||
base[-2].AsStringWithType(thread->GetMachine(), buff, buffSize);
|
||||
gmDebuggerContextCallFrame(a_session, numFrames, fn->GetDebugName(), fn->GetSourceId(), fn->GetLine(ip), "this", buff, (base[-2].IsReference()) ? base[-2].m_value.m_ref : 0);
|
||||
|
||||
if(numFrames == a_callframe)
|
||||
{
|
||||
// this is the active frame, fill out the variables
|
||||
int i;
|
||||
for(i = 0; i < fn->GetNumParamsLocals(); ++i)
|
||||
{
|
||||
base[i].AsStringWithType(thread->GetMachine(), buff, buffSize);
|
||||
gmDebuggerContextVariable(a_session, fn->GetSymbol(i), buff, (base[i].IsReference()) ? base[i].m_value.m_ref : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base[-2].AsStringWithType(thread->GetMachine(), buff, buffSize);
|
||||
gmDebuggerContextCallFrame(a_session, numFrames, "unknown", 0, 0, "this", buff, (base[-2].IsReference()) ? base[-2].m_value.m_ref : 0);
|
||||
}
|
||||
|
||||
// next call frame
|
||||
++numFrames;
|
||||
base = thread->GetBottom() + frame->m_returnBase;
|
||||
ip = frame->m_returnAddress;
|
||||
frame = frame->m_prev;
|
||||
}
|
||||
|
||||
gmDebuggerEndContext(a_session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gmMachineGetSource(gmDebugSession * a_session, int a_sourceId)
|
||||
{
|
||||
const char * source;
|
||||
const char * filename;
|
||||
if(a_session->GetMachine()->GetSourceCode(a_sourceId, source, filename))
|
||||
{
|
||||
gmDebuggerSource(a_session, a_sourceId, filename, source);
|
||||
}
|
||||
}
|
||||
|
||||
void gmMachineGetSourceInfo(gmDebugSession * a_session)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
|
||||
static bool threadIter(gmThread * a_thread, void * a_context)
|
||||
{
|
||||
gmDebugSession * session = (gmDebugSession *) a_context;
|
||||
int state = 0; // 0 - running, 1 - blocked, 2 - sleeping, 3 - exception, 4 - debug
|
||||
if(a_thread->m_debugFlags)
|
||||
state = 4;
|
||||
else if(a_thread->GetState() == gmThread::EXCEPTION)
|
||||
state = 3;
|
||||
else if(a_thread->GetState() == gmThread::RUNNING)
|
||||
state = 0;
|
||||
else if(a_thread->GetState() == gmThread::BLOCKED)
|
||||
state = 1;
|
||||
else if(a_thread->GetState() == gmThread::SLEEPING)
|
||||
state = 2;
|
||||
else
|
||||
state = 3;
|
||||
gmDebuggerThreadInfo(session, a_thread->GetId(), state);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void gmMachineGetThreadInfo(gmDebugSession * a_session)
|
||||
{
|
||||
gmDebuggerBeginThreadInfo(a_session);
|
||||
a_session->GetMachine()->ForEachThread(threadIter, a_session);
|
||||
gmDebuggerEndThreadInfo(a_session);
|
||||
}
|
||||
|
||||
void gmMachineGetVariableInfo(gmDebugSession * a_session, int a_variableId)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
void gmMachineSetBreakPoint(gmDebugSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled)
|
||||
{
|
||||
bool sendAck = false;
|
||||
|
||||
// get break point
|
||||
const void * bp = (const void *) a_session->GetMachine()->GetInstructionAtBreakPoint(a_sourceId, a_lineNumber);
|
||||
if(bp)
|
||||
{
|
||||
// get to next instruction
|
||||
bp = (const void *) (((const char *) bp) + 4);
|
||||
|
||||
int * id = a_session->FindBreakPoint(bp);
|
||||
if(id)
|
||||
{
|
||||
if(!a_enabled)
|
||||
{
|
||||
a_session->RemoveBreakPoint(bp);
|
||||
sendAck = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(a_session->AddBreakPoint(bp, a_threadId))
|
||||
{
|
||||
sendAck = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sendAck)
|
||||
gmDebuggerAck(a_session, a_responseId, 1);
|
||||
else
|
||||
gmDebuggerAck(a_session, a_responseId, 0);
|
||||
}
|
||||
|
||||
void gmMachineBreak(gmDebugSession * a_session, int a_threadId)
|
||||
{
|
||||
gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
|
||||
if(thread)
|
||||
{
|
||||
thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
|
||||
thread->m_debugFlags = TF_STEPINTO | TF_STEPOVER;
|
||||
}
|
||||
}
|
||||
|
||||
void gmMachineQuit(gmDebugSession * a_session)
|
||||
{
|
||||
a_session->Close();
|
||||
}
|
||||
|
||||
void gmDebuggerBreak(gmDebugSession * a_session, int a_threadId, int a_sourceId, int a_lineNumber) {
|
||||
a_session->Pack(ID_dbrk).Pack(a_threadId).Pack(a_sourceId).Pack(a_lineNumber).Send();
|
||||
}
|
||||
void gmDebuggerException(gmDebugSession * a_session, int a_threadId) {
|
||||
a_session->Pack(ID_dexc).Pack(a_threadId).Send();
|
||||
}
|
||||
void gmDebuggerRun(gmDebugSession * a_session, int a_threadId) {
|
||||
a_session->Pack(ID_drun).Pack(a_threadId).Send();
|
||||
}
|
||||
void gmDebuggerStop(gmDebugSession * a_session, int a_threadId) {
|
||||
a_session->Pack(ID_dstp).Pack(a_threadId).Send();
|
||||
}
|
||||
void gmDebuggerSource(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName, const char * a_source) {
|
||||
a_session->Pack(ID_dsrc).Pack(a_sourceId).Pack(a_sourceName).Pack(a_source).Send();
|
||||
}
|
||||
void gmDebuggerBeginContext(gmDebugSession * a_session, int a_threadId, int a_callFrame) {
|
||||
a_session->Pack(ID_dctx).Pack(a_threadId).Pack(a_callFrame);
|
||||
}
|
||||
void gmDebuggerContextCallFrame(gmDebugSession * a_session, int a_callFrame, const char * a_functionName, int a_sourceId, int a_lineNumber, const char * a_thisSymbol, const char * a_thisValue, int a_thisId) {
|
||||
a_session->Pack(ID_call).Pack(a_callFrame).Pack(a_functionName).Pack(a_sourceId).Pack(a_lineNumber).Pack(a_thisSymbol).Pack(a_thisValue).Pack(a_thisId);
|
||||
}
|
||||
void gmDebuggerContextVariable(gmDebugSession * a_session, const char * a_varSymbol, const char * a_varValue, int a_varId) {
|
||||
a_session->Pack(ID_vari).Pack(a_varSymbol).Pack(a_varValue).Pack(a_varId);
|
||||
}
|
||||
void gmDebuggerEndContext(gmDebugSession * a_session) {
|
||||
a_session->Pack(ID_done).Send();
|
||||
}
|
||||
void gmDebuggerBeginSourceInfo(gmDebugSession * a_session) {
|
||||
a_session->Pack(ID_dsri);
|
||||
}
|
||||
void gmDebuggerSourceInfo(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName) {
|
||||
a_session->Pack(ID_srci).Pack(a_sourceId).Pack(a_sourceName);
|
||||
}
|
||||
void gmDebuggerEndSourceInfo(gmDebugSession * a_session) {
|
||||
a_session->Pack(ID_done).Send();
|
||||
}
|
||||
void gmDebuggerBeginThreadInfo(gmDebugSession * a_session) {
|
||||
a_session->Pack(ID_dthi);
|
||||
}
|
||||
void gmDebuggerThreadInfo(gmDebugSession * a_session, int a_threadId, int a_threadState) {
|
||||
a_session->Pack(ID_thri).Pack(a_threadId).Pack(a_threadState);
|
||||
}
|
||||
void gmDebuggerEndThreadInfo(gmDebugSession * a_session) {
|
||||
a_session->Pack(ID_done).Send();
|
||||
}
|
||||
void gmDebuggerError(gmDebugSession * a_session, const char * a_error) {
|
||||
a_session->Pack(ID_derr).Pack(a_error).Send();
|
||||
}
|
||||
void gmDebuggerMessage(gmDebugSession * a_session, const char * a_message) {
|
||||
a_session->Pack(ID_dmsg).Pack(a_message).Send();
|
||||
}
|
||||
void gmDebuggerAck(gmDebugSession * a_session, int a_response, int a_posNeg) {
|
||||
a_session->Pack(ID_dack).Pack(a_response).Pack(a_posNeg).Send();
|
||||
}
|
||||
void gmDebuggerQuit(gmDebugSession * a_session) {
|
||||
a_session->Pack(ID_dend).Send();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// lib binding
|
||||
//
|
||||
|
||||
|
||||
int GM_CDECL gmdDebug(gmThread * a_thread)
|
||||
{
|
||||
// if the machine has a debug session, attach a debug hook to the thread
|
||||
if(a_thread->GetMachine()->m_debugUser && a_thread->GetMachine()->GetDebugMode())
|
||||
{
|
||||
a_thread->m_debugUser = (a_thread->GetFrame()) ? a_thread->GetFrame()->m_returnBase : 0;
|
||||
a_thread->m_debugFlags = TF_STEPINTO | TF_STEPOVER;
|
||||
}
|
||||
return GM_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gmFunctionEntry s_debugLib[] =
|
||||
{
|
||||
/*gm
|
||||
\lib gm
|
||||
\brief functions in the gm lib are all global scope
|
||||
*/
|
||||
|
||||
/*gm
|
||||
\function debug
|
||||
\brief debug will cause a the debugger to break at this point while running.
|
||||
*/
|
||||
|
||||
{"debug", gmdDebug},
|
||||
};
|
||||
|
||||
|
||||
|
||||
void gmBindDebugLib(gmMachine * a_machine)
|
||||
{
|
||||
a_machine->RegisterLibrary(s_debugLib, sizeof(s_debugLib) / sizeof(s_debugLib[0]));
|
||||
}
|
||||
|
||||
#endif
|
||||
88
vscript/languages/gm/src/gm/gmDebug.h
Normal file
88
vscript/languages/gm/src/gm/gmDebug.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMDEBUG_H_
|
||||
#define _GMDEBUG_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmStreamBuffer.h"
|
||||
#include "gmHash.h"
|
||||
|
||||
class gmMachine;
|
||||
class gmDebugSession;
|
||||
|
||||
// bind debug lib
|
||||
void gmBindDebugLib(gmMachine * a_machine);
|
||||
|
||||
// callbacks used to hook up comms
|
||||
typedef void (GM_CDECL *gmSendDebuggerMessage)(gmDebugSession * a_session, const void * a_command, int a_len);
|
||||
typedef const void * (GM_CDECL *gmPumpDebuggerMessage)(gmDebugSession * a_session, int &a_len);
|
||||
|
||||
#if GMDEBUG_SUPPORT
|
||||
|
||||
/// \class gmDebugSession
|
||||
class gmDebugSession
|
||||
{
|
||||
public:
|
||||
|
||||
gmDebugSession();
|
||||
~gmDebugSession();
|
||||
|
||||
/// \brief Update() must be called to pump messages
|
||||
void Update();
|
||||
|
||||
/// \brief Open() will start debugging on a_machine
|
||||
bool Open(gmMachine * a_machine);
|
||||
|
||||
/// \brief Close() will stop debugging
|
||||
bool Close();
|
||||
|
||||
/// \brief GetMachine()
|
||||
inline gmMachine * GetMachine() const { return m_machine; }
|
||||
|
||||
gmSendDebuggerMessage m_sendMessage;
|
||||
gmPumpDebuggerMessage m_pumpMessage;
|
||||
void * m_user;
|
||||
|
||||
// send message helpers
|
||||
gmDebugSession &Pack(int a_val);
|
||||
gmDebugSession &Pack(const char * a_val);
|
||||
void Send();
|
||||
|
||||
// rcv message helpers
|
||||
gmDebugSession &Unpack(int &a_val);
|
||||
gmDebugSession &Unpack(const char * &a_val);
|
||||
|
||||
// helpers
|
||||
bool AddBreakPoint(const void * a_bp, int a_threadId);
|
||||
int * FindBreakPoint(const void * a_bp); // return thread id
|
||||
bool RemoveBreakPoint(const void * a_bp);
|
||||
|
||||
private:
|
||||
|
||||
class BreakPoint : public gmHashNode<void *, BreakPoint>
|
||||
{
|
||||
public:
|
||||
inline const void * GetKey() const { return m_bp; }
|
||||
const void * m_bp;
|
||||
int m_threadId;
|
||||
};
|
||||
|
||||
gmMachine * m_machine;
|
||||
|
||||
gmHash<void *, BreakPoint> m_breaks;
|
||||
gmStreamBufferDynamic m_out;
|
||||
gmStreamBufferStatic m_in;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _GMDEBUG_H_
|
||||
321
vscript/languages/gm/src/gm/gmDebugger.cpp
Normal file
321
vscript/languages/gm/src/gm/gmDebugger.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "gmDebugger.h"
|
||||
|
||||
//
|
||||
// Please note that gmDebugger.c/.h are for implementing
|
||||
// a debugger application and should not be included
|
||||
// in an normal GM application build.
|
||||
//
|
||||
|
||||
#ifndef GM_MAKE_ID32
|
||||
#define GM_MAKE_ID32( a, b, c, d ) ( ((d)<<24) | ((c)<<16) | ((b)<<8) | (a))
|
||||
#endif //GM_MAKE_ID32
|
||||
|
||||
#define ID_mrun GM_MAKE_ID32('m','r','u','n')
|
||||
#define ID_msin GM_MAKE_ID32('m','s','i','n')
|
||||
#define ID_msou GM_MAKE_ID32('m','s','o','u')
|
||||
#define ID_msov GM_MAKE_ID32('m','s','o','v')
|
||||
#define ID_mgct GM_MAKE_ID32('m','g','c','t')
|
||||
#define ID_mgsr GM_MAKE_ID32('m','g','s','r')
|
||||
#define ID_mgsi GM_MAKE_ID32('m','g','s','i')
|
||||
#define ID_mgti GM_MAKE_ID32('m','g','t','i')
|
||||
#define ID_mgvi GM_MAKE_ID32('m','g','v','i')
|
||||
#define ID_msbp GM_MAKE_ID32('m','s','b','p')
|
||||
#define ID_mbrk GM_MAKE_ID32('m','b','r','k')
|
||||
#define ID_mend GM_MAKE_ID32('m','e','n','d')
|
||||
|
||||
#define ID_dbrk GM_MAKE_ID32('d','b','r','k')
|
||||
#define ID_dexc GM_MAKE_ID32('d','e','x','c')
|
||||
#define ID_drun GM_MAKE_ID32('d','r','u','n')
|
||||
#define ID_dstp GM_MAKE_ID32('d','s','t','p')
|
||||
#define ID_dsrc GM_MAKE_ID32('d','s','r','c')
|
||||
#define ID_dctx GM_MAKE_ID32('d','c','t','x')
|
||||
#define ID_call GM_MAKE_ID32('c','a','l','l')
|
||||
#define ID_vari GM_MAKE_ID32('v','a','r','i')
|
||||
#define ID_done GM_MAKE_ID32('d','o','n','e')
|
||||
#define ID_dsri GM_MAKE_ID32('d','s','r','i')
|
||||
#define ID_srci GM_MAKE_ID32('s','r','c','i')
|
||||
#define ID_done GM_MAKE_ID32('d','o','n','e')
|
||||
#define ID_dthi GM_MAKE_ID32('d','t','h','i')
|
||||
#define ID_thri GM_MAKE_ID32('t','h','r','i')
|
||||
#define ID_done GM_MAKE_ID32('d','o','n','e')
|
||||
#define ID_derr GM_MAKE_ID32('d','e','r','r')
|
||||
#define ID_dmsg GM_MAKE_ID32('d','m','s','g')
|
||||
#define ID_dack GM_MAKE_ID32('d','a','c','k')
|
||||
#define ID_dend GM_MAKE_ID32('d','e','n','d')
|
||||
|
||||
//
|
||||
// Please note that gmDebugger.c/.h are for implementing
|
||||
// a debugger application and should not be included
|
||||
// in an normal GM application build.
|
||||
//
|
||||
|
||||
|
||||
gmDebuggerSession::gmDebuggerSession()
|
||||
{
|
||||
m_outSize = 256;
|
||||
m_out = (void*) new char[m_outSize];
|
||||
m_outCursor = 0;
|
||||
m_in = NULL;
|
||||
m_inCursor = m_inSize = NULL;
|
||||
}
|
||||
|
||||
|
||||
gmDebuggerSession::~gmDebuggerSession()
|
||||
{
|
||||
if(m_out)
|
||||
{
|
||||
delete [] (char*)m_out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gmDebuggerSession::Update()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
m_in = m_pumpMessage(this, m_inSize);
|
||||
if(m_in == NULL) break;
|
||||
m_inCursor = 0;
|
||||
|
||||
int id, pa, pb, pc;
|
||||
const char * sa, * sb, * sc;
|
||||
|
||||
Unpack(id);
|
||||
switch(id)
|
||||
{
|
||||
case ID_dbrk :
|
||||
Unpack(id).Unpack(pa).Unpack(pb);
|
||||
gmDebuggerBreak(this, id, pa, pb);
|
||||
break;
|
||||
case ID_drun :
|
||||
Unpack(id);
|
||||
gmDebuggerRun(this, id);
|
||||
break;
|
||||
case ID_dstp :
|
||||
Unpack(id);
|
||||
gmDebuggerStop(this, id);
|
||||
break;
|
||||
case ID_dsrc :
|
||||
Unpack(id).Unpack(sa).Unpack(sb);
|
||||
gmDebuggerSource(this, id, sa, sb);
|
||||
break;
|
||||
case ID_dexc :
|
||||
Unpack(id);
|
||||
gmDebuggerException(this, id);
|
||||
break;
|
||||
case ID_dctx :
|
||||
Unpack(id).Unpack(pa); // thread id, callframe
|
||||
gmDebuggerBeginContext(this, id, pa);
|
||||
for(;;)
|
||||
{
|
||||
Unpack(id);
|
||||
if(id == ID_call)
|
||||
{
|
||||
Unpack(id).Unpack(sa).Unpack(pa).Unpack(pb).Unpack(sb).Unpack(sc).Unpack(pc);
|
||||
gmDebuggerContextCallFrame(this, id, sa, pa, pb, sb, sc, pc);
|
||||
}
|
||||
else if(id == ID_vari)
|
||||
{
|
||||
Unpack(sa).Unpack(sb).Unpack(pa);
|
||||
gmDebuggerContextVariable(this, sa, sb, pa);
|
||||
}
|
||||
else if(id == ID_done) break;
|
||||
else break;
|
||||
}
|
||||
gmDebuggerEndContext(this);
|
||||
break;
|
||||
case ID_dsri :
|
||||
// todo
|
||||
break;
|
||||
case ID_dthi :
|
||||
gmDebuggerBeginThreadInfo(this);
|
||||
for(;;)
|
||||
{
|
||||
Unpack(id);
|
||||
if(id == ID_thri)
|
||||
{
|
||||
Unpack(pa).Unpack(pb);
|
||||
gmDebuggerThreadInfo(this, pa, pb);
|
||||
}
|
||||
else if(id == ID_done) break;
|
||||
else break;
|
||||
}
|
||||
gmDebuggerEndThreadInfo(this);
|
||||
break;
|
||||
case ID_derr :
|
||||
Unpack(sa);
|
||||
gmDebuggerError(this, sa);
|
||||
break;
|
||||
case ID_dmsg :
|
||||
Unpack(sa);
|
||||
gmDebuggerMessage(this, sa);
|
||||
break;
|
||||
case ID_dack :
|
||||
Unpack(pa).Unpack(pb);
|
||||
gmDebuggerAck(this, pa, pb);
|
||||
break;
|
||||
case ID_dend :
|
||||
gmDebuggerQuit(this);
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool gmDebuggerSession::Open()
|
||||
{
|
||||
m_outCursor = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool gmDebuggerSession::Close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
gmDebuggerSession &gmDebuggerSession::Pack(int a_val)
|
||||
{
|
||||
Need(4);
|
||||
memcpy((char *) m_out + m_outCursor, &a_val, 4);
|
||||
m_outCursor += 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
gmDebuggerSession &gmDebuggerSession::Pack(const char * a_val)
|
||||
{
|
||||
if(a_val)
|
||||
{
|
||||
int len = strlen(a_val) + 1;
|
||||
Need(len);
|
||||
memcpy((char *) m_out + m_outCursor, a_val, len);
|
||||
m_outCursor += len;
|
||||
}
|
||||
else
|
||||
{
|
||||
Need(1);
|
||||
memcpy((char *) m_out + m_outCursor, "", 1);
|
||||
m_outCursor += 1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void gmDebuggerSession::Send()
|
||||
{
|
||||
m_sendMessage(this, m_out, m_outCursor);
|
||||
m_outCursor = 0;
|
||||
}
|
||||
|
||||
|
||||
gmDebuggerSession &gmDebuggerSession::Unpack(int &a_val)
|
||||
{
|
||||
if(m_inCursor + 4 <= m_inSize)
|
||||
{
|
||||
memcpy(&a_val, (const char *) m_in + m_inCursor, 4);
|
||||
m_inCursor += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
a_val = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
gmDebuggerSession &gmDebuggerSession::Unpack(const char * &a_val)
|
||||
{
|
||||
a_val = (const char *) m_in + m_inCursor;
|
||||
m_inCursor += strlen(a_val) + 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void gmDebuggerSession::Need(int a_bytes)
|
||||
{
|
||||
if((m_outCursor + a_bytes) >= m_outSize)
|
||||
{
|
||||
int newSize = m_outSize + a_bytes + 256;
|
||||
void * buffer = (void*)new char[newSize];
|
||||
memcpy(buffer, m_out, m_outCursor);
|
||||
delete [] (char*)m_out;
|
||||
m_out = buffer;
|
||||
m_outSize = newSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gmMachineRun(gmDebuggerSession * a_session, int a_threadId)
|
||||
{
|
||||
a_session->Pack(ID_mrun).Pack(a_threadId).Send();
|
||||
}
|
||||
|
||||
void gmMachineStepInto(gmDebuggerSession * a_session, int a_threadId)
|
||||
{
|
||||
a_session->Pack(ID_msin).Pack(a_threadId).Send();
|
||||
}
|
||||
|
||||
void gmMachineStepOver(gmDebuggerSession * a_session, int a_threadId)
|
||||
{
|
||||
a_session->Pack(ID_msov).Pack(a_threadId).Send();
|
||||
}
|
||||
|
||||
void gmMachineStepOut(gmDebuggerSession * a_session, int a_threadId)
|
||||
{
|
||||
a_session->Pack(ID_msou).Pack(a_threadId).Send();
|
||||
}
|
||||
|
||||
void gmMachineGetContext(gmDebuggerSession * a_session, int a_threadId, int a_callframe)
|
||||
{
|
||||
a_session->Pack(ID_mgct).Pack(a_threadId).Pack(a_callframe).Send();
|
||||
}
|
||||
|
||||
void gmMachineGetSource(gmDebuggerSession * a_session, int a_sourceId)
|
||||
{
|
||||
a_session->Pack(ID_mgsr).Pack(a_sourceId).Send();
|
||||
}
|
||||
|
||||
void gmMachineGetSourceInfo(gmDebuggerSession * a_session)
|
||||
{
|
||||
a_session->Pack(ID_mgsi).Send();
|
||||
}
|
||||
|
||||
void gmMachineGetThreadInfo(gmDebuggerSession * a_session)
|
||||
{
|
||||
a_session->Pack(ID_mgti).Send();
|
||||
}
|
||||
|
||||
void gmMachineGetVariableInfo(gmDebuggerSession * a_session, int a_variableId)
|
||||
{
|
||||
a_session->Pack(ID_mgvi).Pack(a_variableId).Send();
|
||||
}
|
||||
|
||||
void gmMachineSetBreakPoint(gmDebuggerSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled)
|
||||
{
|
||||
a_session->Pack(ID_msbp).Pack(a_responseId).Pack(a_sourceId).Pack(a_lineNumber).Pack(a_threadId).Pack(a_enabled).Send();
|
||||
}
|
||||
|
||||
void gmMachineBreak(gmDebuggerSession * a_session, int a_threadId)
|
||||
{
|
||||
a_session->Pack(ID_mbrk).Pack(a_threadId).Send();
|
||||
}
|
||||
|
||||
void gmMachineQuit(gmDebuggerSession * a_session)
|
||||
{
|
||||
a_session->Pack(ID_mend).Send();
|
||||
}
|
||||
114
vscript/languages/gm/src/gm/gmDebugger.h
Normal file
114
vscript/languages/gm/src/gm/gmDebugger.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMDEBUGGER_H_
|
||||
#define _GMDEBUGGER_H_
|
||||
|
||||
//
|
||||
// Please note that gmDebugger.c/.h are for implementing
|
||||
// a debugger application and should not be included
|
||||
// in an normal GM application build.
|
||||
//
|
||||
|
||||
|
||||
class gmDebuggerSession;
|
||||
|
||||
// callbacks used to hook up comms
|
||||
typedef void (*gmSendMachineMessage)(gmDebuggerSession * a_session, const void * a_command, int a_len);
|
||||
typedef const void * (*gmPumpMachineMessage)(gmDebuggerSession * a_session, int &a_len);
|
||||
|
||||
/// \class gmDebuggerSession
|
||||
class gmDebuggerSession
|
||||
{
|
||||
public:
|
||||
|
||||
gmDebuggerSession();
|
||||
~gmDebuggerSession();
|
||||
|
||||
/// \brief Update() must be called to pump messages
|
||||
void Update();
|
||||
|
||||
/// \brief Open() will start debugging
|
||||
bool Open();
|
||||
|
||||
/// \brief Close() will stop debugging
|
||||
bool Close();
|
||||
|
||||
gmSendMachineMessage m_sendMessage;
|
||||
gmPumpMachineMessage m_pumpMessage;
|
||||
void * m_user; // hook to your debugger
|
||||
|
||||
// send message helpers
|
||||
gmDebuggerSession &Pack(int a_val);
|
||||
gmDebuggerSession &Pack(const char * a_val);
|
||||
void Send();
|
||||
|
||||
// rcv message helpers
|
||||
gmDebuggerSession &Unpack(int &a_val);
|
||||
gmDebuggerSession &Unpack(const char * &a_val);
|
||||
|
||||
private:
|
||||
|
||||
void * m_out;
|
||||
int m_outCursor, m_outSize;
|
||||
void Need(int a_bytes);
|
||||
|
||||
const void * m_in;
|
||||
int m_inCursor, m_inSize;
|
||||
};
|
||||
|
||||
//
|
||||
// the debugger must implement the following functions
|
||||
//
|
||||
|
||||
extern void gmDebuggerBreak(gmDebuggerSession * a_session, int a_threadId, int a_sourceId, int a_lineNumber);
|
||||
extern void gmDebuggerRun(gmDebuggerSession * a_session, int a_threadId);
|
||||
extern void gmDebuggerStop(gmDebuggerSession * a_session, int a_threadId);
|
||||
extern void gmDebuggerSource(gmDebuggerSession * a_session, int a_sourceId, const char * a_sourceName, const char * a_source);
|
||||
extern void gmDebuggerException(gmDebuggerSession * a_session, int a_threadId);
|
||||
|
||||
extern void gmDebuggerBeginContext(gmDebuggerSession * a_session, int a_threadId, int a_callFrame);
|
||||
extern void gmDebuggerContextCallFrame(gmDebuggerSession * a_session, int a_callFrame, const char * a_functionName, int a_sourceId, int a_lineNumber, const char * a_thisSymbol, const char * a_thisValue, int a_thisId);
|
||||
extern void gmDebuggerContextVariable(gmDebuggerSession * a_session, const char * a_varSymbol, const char * a_varValue, int a_varId);
|
||||
extern void gmDebuggerEndContext(gmDebuggerSession * a_session);
|
||||
|
||||
extern void gmDebuggerBeginSourceInfo(gmDebuggerSession * a_session);
|
||||
extern void gmDebuggerSourceInfo(gmDebuggerSession * a_session, int a_sourceId, const char * a_sourceName);
|
||||
extern void gmDebuggerEndSourceInfo(gmDebuggerSession * a_session);
|
||||
|
||||
extern void gmDebuggerBeginThreadInfo(gmDebuggerSession * a_session);
|
||||
extern void gmDebuggerThreadInfo(gmDebuggerSession * a_session, int a_threadId, int a_threadState);
|
||||
extern void gmDebuggerEndThreadInfo(gmDebuggerSession * a_session);
|
||||
|
||||
extern void gmDebuggerError(gmDebuggerSession * a_session, const char * a_error);
|
||||
extern void gmDebuggerMessage(gmDebuggerSession * a_session, const char * a_message);
|
||||
extern void gmDebuggerAck(gmDebuggerSession * a_session, int a_response, int a_posNeg);
|
||||
extern void gmDebuggerQuit(gmDebuggerSession * a_session);
|
||||
|
||||
//
|
||||
// the debugger can use the following functions to send messages to the machine
|
||||
//
|
||||
|
||||
void gmMachineRun(gmDebuggerSession * a_session, int a_threadId);
|
||||
void gmMachineStepInto(gmDebuggerSession * a_session, int a_threadId);
|
||||
void gmMachineStepOver(gmDebuggerSession * a_session, int a_threadId);
|
||||
void gmMachineStepOut(gmDebuggerSession * a_session, int a_threadId);
|
||||
void gmMachineGetContext(gmDebuggerSession * a_session, int a_threadId, int a_callframe);
|
||||
void gmMachineGetSource(gmDebuggerSession * a_session, int a_sourceId);
|
||||
void gmMachineGetSourceInfo(gmDebuggerSession * a_session);
|
||||
void gmMachineGetThreadInfo(gmDebuggerSession * a_session);
|
||||
void gmMachineGetVariableInfo(gmDebuggerSession * a_session, int a_variableId);
|
||||
void gmMachineSetBreakPoint(gmDebuggerSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled);
|
||||
void gmMachineBreak(gmDebuggerSession * a_session, int a_threadId);
|
||||
void gmMachineQuit(gmDebuggerSession * a_session);
|
||||
|
||||
#endif
|
||||
|
||||
286
vscript/languages/gm/src/gm/gmFunctionObject.cpp
Normal file
286
vscript/languages/gm/src/gm/gmFunctionObject.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmFunctionObject.h"
|
||||
#include "gmMachine.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
gmFunctionObject::gmFunctionObject()
|
||||
{
|
||||
m_cFunction = NULL;
|
||||
m_cUserData = NULL;
|
||||
m_debugInfo = NULL;
|
||||
m_byteCode = NULL;
|
||||
m_byteCodeLength = 0;
|
||||
m_maxStackSize = 1; // return value
|
||||
m_numLocals = 0;
|
||||
m_numParams = 0;
|
||||
m_numParamsLocals = 0;
|
||||
m_numReferences = 0;
|
||||
m_references = NULL;
|
||||
}
|
||||
|
||||
void gmFunctionObject::Destruct(gmMachine * a_machine)
|
||||
{
|
||||
if(m_references)
|
||||
{
|
||||
a_machine->Sys_Free(m_references);
|
||||
m_references = NULL;
|
||||
}
|
||||
if(m_byteCode)
|
||||
{
|
||||
a_machine->Sys_Free(m_byteCode);
|
||||
m_byteCode = NULL;
|
||||
}
|
||||
if(m_debugInfo)
|
||||
{
|
||||
if(m_debugInfo->m_debugName) { a_machine->Sys_Free(m_debugInfo->m_debugName); }
|
||||
if(m_debugInfo->m_lineInfo) { a_machine->Sys_Free(m_debugInfo->m_lineInfo); }
|
||||
if(m_debugInfo->m_symbols)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < m_numParamsLocals; ++i)
|
||||
{
|
||||
a_machine->Sys_Free(m_debugInfo->m_symbols[i]);
|
||||
}
|
||||
a_machine->Sys_Free(m_debugInfo->m_symbols);
|
||||
}
|
||||
a_machine->Sys_Free(m_debugInfo);
|
||||
m_debugInfo = NULL;
|
||||
}
|
||||
|
||||
#if GM_USE_INCGC
|
||||
a_machine->DestructDeleteObject(this);
|
||||
#endif //GM_USE_INCGC
|
||||
}
|
||||
|
||||
#if GM_USE_INCGC
|
||||
|
||||
|
||||
bool gmFunctionObject::Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < m_numReferences; ++i)
|
||||
{
|
||||
gmObject * object = a_machine->GetObject(m_references[i]);
|
||||
a_gc->GetNextObject(object);
|
||||
++a_workDone;
|
||||
}
|
||||
|
||||
++a_workDone;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else //GM_USE_INCGC
|
||||
|
||||
void gmFunctionObject::Mark(gmMachine * a_machine, gmuint32 a_mark)
|
||||
{
|
||||
if(m_mark != GM_MARK_PERSIST) m_mark = a_mark;
|
||||
int i;
|
||||
for(i = 0; i < m_numReferences; ++i)
|
||||
{
|
||||
gmObject * object = a_machine->GetObject(m_references[i]);
|
||||
if(object->NeedsMark(a_mark)) object->Mark(a_machine, a_mark);
|
||||
}
|
||||
}
|
||||
#endif //GM_USE_INCGC
|
||||
|
||||
|
||||
bool gmFunctionObject::Init(gmMachine * a_machine, bool a_debug, gmFunctionInfo &a_info, gmuint32 a_sourceId)
|
||||
{
|
||||
// byte code
|
||||
if(a_info.m_byteCodeLength)
|
||||
{
|
||||
m_byteCode = (gmuint8 *) a_machine->Sys_Alloc(a_info.m_byteCodeLength);
|
||||
memcpy(m_byteCode, a_info.m_byteCode, a_info.m_byteCodeLength);
|
||||
m_byteCodeLength = a_info.m_byteCodeLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_byteCode = NULL;
|
||||
m_byteCodeLength = 0;
|
||||
}
|
||||
|
||||
// stack info
|
||||
m_maxStackSize = a_info.m_maxStackSize;
|
||||
m_numLocals = a_info.m_numLocals;
|
||||
m_numParams = a_info.m_numParams;
|
||||
m_numParamsLocals = a_info.m_numParams + a_info.m_numLocals;
|
||||
|
||||
// references
|
||||
m_numReferences = 0;
|
||||
m_references = NULL;
|
||||
|
||||
if(m_byteCode)
|
||||
{
|
||||
// find the objects this function references by iterating over the byte code and collecting them.
|
||||
// we could perform this step in the compilation phase if we don't want to iterate over the byte code.
|
||||
|
||||
gmptr * references = (gmptr *) GM_NEW( char[a_info.m_byteCodeLength] );
|
||||
|
||||
union
|
||||
{
|
||||
const gmuint8 * instruction;
|
||||
const gmuint32 * instruction32;
|
||||
};
|
||||
|
||||
instruction = (const gmuint8 *) m_byteCode;
|
||||
const gmuint8 * end = instruction + m_byteCodeLength;
|
||||
for(;instruction < end;)
|
||||
{
|
||||
switch(*(instruction32++))
|
||||
{
|
||||
case BC_GETDOT :
|
||||
case BC_SETDOT :
|
||||
case BC_BRA :
|
||||
case BC_BRZ :
|
||||
case BC_BRNZ :
|
||||
case BC_BRZK :
|
||||
case BC_BRNZK :
|
||||
case BC_FOREACH :
|
||||
case BC_PUSHINT :
|
||||
case BC_GETGLOBAL :
|
||||
case BC_SETGLOBAL :
|
||||
case BC_GETTHIS :
|
||||
case BC_SETTHIS : instruction += sizeof(gmptr); break;
|
||||
case BC_PUSHFP : instruction += sizeof(gmfloat); break;
|
||||
|
||||
case BC_CALL :
|
||||
case BC_GETLOCAL :
|
||||
case BC_SETLOCAL : instruction += sizeof(gmuint32); break;
|
||||
|
||||
case BC_PUSHSTR :
|
||||
case BC_PUSHFN :
|
||||
{
|
||||
// if the reference does not already exist, add it.
|
||||
gmptr reference = *((gmptr *) instruction);
|
||||
instruction += sizeof(gmptr);
|
||||
int i;
|
||||
for(i = 0; i < m_numReferences; ++i)
|
||||
{
|
||||
if(references[i] == reference) break;
|
||||
}
|
||||
if(i == m_numReferences) references[m_numReferences++] = reference;
|
||||
break;
|
||||
}
|
||||
|
||||
default : break;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_numReferences > 0)
|
||||
{
|
||||
m_references = (gmptr *) a_machine->Sys_Alloc(sizeof(gmptr) * m_numReferences);
|
||||
memcpy(m_references, references, sizeof(gmptr) * m_numReferences);
|
||||
}
|
||||
|
||||
delete [] (char*) references;
|
||||
}
|
||||
|
||||
// debug info
|
||||
m_debugInfo = NULL;
|
||||
if(a_debug)
|
||||
{
|
||||
m_debugInfo = (gmFunctionObjectDebugInfo *) a_machine->Sys_Alloc(sizeof(gmFunctionObjectDebugInfo));
|
||||
memset(m_debugInfo, 0, sizeof(gmFunctionObjectDebugInfo));
|
||||
|
||||
// source code id
|
||||
m_debugInfo->m_sourceId = a_sourceId;
|
||||
|
||||
// debug name
|
||||
if(a_info.m_debugName)
|
||||
{
|
||||
int len = strlen(a_info.m_debugName) + 1;
|
||||
m_debugInfo->m_debugName = (char *) a_machine->Sys_Alloc(len);
|
||||
memcpy(m_debugInfo->m_debugName, a_info.m_debugName, len);
|
||||
}
|
||||
|
||||
// symbols
|
||||
if(a_info.m_symbols)
|
||||
{
|
||||
m_debugInfo->m_symbols = (char **) a_machine->Sys_Alloc(sizeof(char *) * m_numParamsLocals);
|
||||
int i;
|
||||
for(i = 0; i < m_numParamsLocals; ++i)
|
||||
{
|
||||
int len = strlen(a_info.m_symbols[i]) + 1;
|
||||
m_debugInfo->m_symbols[i] = (char *) a_machine->Sys_Alloc(len);
|
||||
memcpy(m_debugInfo->m_symbols[i], a_info.m_symbols[i], len);
|
||||
}
|
||||
}
|
||||
|
||||
// line number debugging.
|
||||
if(a_info.m_lineInfo)
|
||||
{
|
||||
// alloc and copy
|
||||
m_debugInfo->m_lineInfo = (gmLineInfo *) a_machine->Sys_Alloc(sizeof(gmLineInfo) * a_info.m_lineInfoCount);
|
||||
memcpy(m_debugInfo->m_lineInfo, a_info.m_lineInfo, sizeof(gmLineInfo) * a_info.m_lineInfoCount);
|
||||
m_debugInfo->m_lineInfoCount = a_info.m_lineInfoCount;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int gmFunctionObject::GetLine(int a_address) const
|
||||
{
|
||||
if(m_debugInfo && m_debugInfo->m_lineInfo)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < m_debugInfo->m_lineInfoCount; ++i)
|
||||
{
|
||||
if(a_address < m_debugInfo->m_lineInfo[i].m_address)
|
||||
{
|
||||
// return entry before
|
||||
if(i > 0) --i;
|
||||
return m_debugInfo->m_lineInfo[i].m_lineNumber;
|
||||
}
|
||||
}
|
||||
return m_debugInfo->m_lineInfo[i - 1].m_lineNumber;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const void * gmFunctionObject::GetInstructionAtLine(int a_line) const
|
||||
{
|
||||
if(m_debugInfo && m_debugInfo->m_lineInfo && m_byteCode)
|
||||
{
|
||||
// serach for the first address using this line.
|
||||
int i;
|
||||
for(i = 0; i < m_debugInfo->m_lineInfoCount; ++i)
|
||||
{
|
||||
if(m_debugInfo->m_lineInfo[i].m_lineNumber == a_line)
|
||||
{
|
||||
return (void *) ((char *) m_byteCode + m_debugInfo->m_lineInfo[i].m_address);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gmuint32 gmFunctionObject::GetSourceId() const
|
||||
{
|
||||
if(m_debugInfo)
|
||||
{
|
||||
return m_debugInfo->m_sourceId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
163
vscript/languages/gm/src/gm/gmFunctionObject.h
Normal file
163
vscript/languages/gm/src/gm/gmFunctionObject.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMFUNCTIONOBJECT_H_
|
||||
#define _GMFUNCTIONOBJECT_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmVariable.h"
|
||||
#include "gmCodeGenHooks.h"
|
||||
#include "gmMem.h"
|
||||
|
||||
// fwd decls
|
||||
class gmThread;
|
||||
|
||||
enum gmCFunctionReturn
|
||||
{
|
||||
GM_OK = 0,
|
||||
GM_EXCEPTION = -1,
|
||||
GM_SYS_YIELD = -2, // system only
|
||||
GM_SYS_BLOCK = -3, // system only
|
||||
GM_SYS_SLEEP = -4, // system only
|
||||
GM_SYS_KILL = -5, // system only
|
||||
GM_SYS_STATE = -6, // system only
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief gmCFunction is the function type for binding c functions to gm.
|
||||
\return gmCFunctionReturn
|
||||
*/
|
||||
typedef int (GM_CDECL *gmCFunction)(gmThread *);
|
||||
|
||||
/*!
|
||||
\class gmFunctionObject
|
||||
\brief
|
||||
*/
|
||||
class gmFunctionObject : public gmObject
|
||||
{
|
||||
public:
|
||||
|
||||
virtual int GetType() const { return GM_FUNCTION; }
|
||||
|
||||
virtual void Destruct(gmMachine * a_machine);
|
||||
#if GM_USE_INCGC
|
||||
virtual bool Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone);
|
||||
#else //GM_USE_INCGC
|
||||
virtual void Mark(gmMachine * a_machine, gmuint32 a_mark);
|
||||
#endif //GM_USE_INCGC
|
||||
|
||||
|
||||
/*!
|
||||
\brief Init() will initialise a function object.
|
||||
\param a_debug is true if this is a debug build
|
||||
\param a_info is a function info struct as built by gmCodeGenHooks.
|
||||
\param a_sourceId is an unique id specifiying the source code of the function, is used for debugging.
|
||||
\return true on success.
|
||||
*/
|
||||
bool Init(gmMachine * a_machine, bool a_debug, gmFunctionInfo &a_info, gmuint32 a_sourceId = 0);
|
||||
|
||||
/*!
|
||||
\brief GetMaxStackSize
|
||||
\return the maximum stack growth not including parameters or locals
|
||||
*/
|
||||
inline int GetMaxStackSize() const { return m_maxStackSize; }
|
||||
|
||||
/// \brief GetNumLocals
|
||||
inline int GetNumLocals() const { return m_numLocals; }
|
||||
|
||||
/// \brief GetNumParams
|
||||
inline int GetNumParams() const { return m_numParams; }
|
||||
|
||||
/// \brief GetNumParamsLocals()
|
||||
inline int GetNumParamsLocals() const { return m_numParamsLocals; }
|
||||
|
||||
/// \brief GetByteCode()
|
||||
inline const void * GetByteCode() const { return m_byteCode; }
|
||||
|
||||
/// \brief GetDebugName()
|
||||
inline const char * GetDebugName() const;
|
||||
|
||||
/// \brief GetLine() will return the source line for the given address
|
||||
int GetLine(int a_address) const;
|
||||
int GetLine(const void * a_instruction) const { return GetLine((const char * ) a_instruction - (char *) m_byteCode); }
|
||||
|
||||
/// \brief GetInstructionAtLine() will return the instruction at the given line, or NULL of line was not within this function
|
||||
const void * GetInstructionAtLine(int a_line) const;
|
||||
|
||||
/// \brief GetSourceId() will get the source code id when in debug mode, else 0
|
||||
gmuint32 GetSourceId() const;
|
||||
|
||||
/// \brief GetSymbol() will return the symbol name at the given offset.
|
||||
inline const char * GetSymbol(int a_offset) const;
|
||||
|
||||
// public data
|
||||
gmCFunction m_cFunction;
|
||||
const void* m_cUserData;
|
||||
|
||||
protected:
|
||||
|
||||
/// \brief Non-public constructor. Create via gmMachine.
|
||||
gmFunctionObject();
|
||||
friend class gmMachine;
|
||||
|
||||
private:
|
||||
|
||||
/*!
|
||||
\brief gmFunctionObjectDebugInfo stores debugging info for a debug build
|
||||
*/
|
||||
struct gmFunctionObjectDebugInfo
|
||||
{
|
||||
char * m_debugName;
|
||||
char ** m_symbols;
|
||||
int m_lineInfoCount;
|
||||
gmuint32 m_sourceId; // source code id.
|
||||
gmLineInfo * m_lineInfo;
|
||||
};
|
||||
|
||||
gmFunctionObjectDebugInfo * m_debugInfo;
|
||||
void * m_byteCode;
|
||||
int m_byteCodeLength;
|
||||
int m_maxStackSize;
|
||||
int m_numLocals;
|
||||
int m_numParams;
|
||||
int m_numParamsLocals; //!< m_numLocals + m_numParams
|
||||
int m_numReferences; //!< number of references within the byte code.
|
||||
gmptr * m_references; //!< references from the byte code
|
||||
};
|
||||
|
||||
//
|
||||
//
|
||||
// INLINE IMPLEMENTATION
|
||||
//
|
||||
//
|
||||
|
||||
inline const char * gmFunctionObject::GetDebugName() const
|
||||
{
|
||||
if(m_debugInfo && m_debugInfo->m_debugName)
|
||||
{
|
||||
return m_debugInfo->m_debugName;
|
||||
}
|
||||
return "__unknown";
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline const char * gmFunctionObject::GetSymbol(int a_offset) const
|
||||
{
|
||||
if(m_debugInfo && m_debugInfo->m_symbols && (a_offset >= 0) && (a_offset < m_numParamsLocals))
|
||||
{
|
||||
return m_debugInfo->m_symbols[a_offset];
|
||||
}
|
||||
return "__unknown";
|
||||
}
|
||||
|
||||
|
||||
#endif // _GMFUNCTIONOBJECT_H_
|
||||
17
vscript/languages/gm/src/gm/gmHash.cpp
Normal file
17
vscript/languages/gm/src/gm/gmHash.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmHash.h"
|
||||
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
348
vscript/languages/gm/src/gm/gmHash.h
Normal file
348
vscript/languages/gm/src/gm/gmHash.h
Normal file
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMHASH_H_
|
||||
#define _GMHASH_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmIterator.h"
|
||||
|
||||
#define TMPL template<class KEY, class T, class HASHER>
|
||||
#define QUAL gmHash<KEY, T, HASHER>
|
||||
#define NQUAL gmHashNode<KEY, T, HASHER>
|
||||
|
||||
TMPL class gmHash;
|
||||
class gmDefaultHasher;
|
||||
|
||||
/// \class gmHashNode
|
||||
/// \brief inherit gmHashNode
|
||||
template<class KEY, class T, class HASHER = gmDefaultHasher>
|
||||
class gmHashNode
|
||||
{
|
||||
public:
|
||||
inline gmHashNode() {}
|
||||
|
||||
/// \brief Return whatever is the 'key' for this type. Used by gmHash
|
||||
//const KEY& GetKey() const = 0;
|
||||
|
||||
private:
|
||||
|
||||
T * m_next;
|
||||
friend class QUAL;
|
||||
};
|
||||
|
||||
/// \class gmHash
|
||||
/// \brief templated intrusive hash class
|
||||
/// HASHER must provide static gmuint ::Hash(const KEY &a_key) and int ::Compare(const KEY &a_key, const KEY &a_key)
|
||||
template<class KEY, class T, class HASHER = gmDefaultHasher>
|
||||
class gmHash
|
||||
{
|
||||
public:
|
||||
|
||||
/// \class Iterator
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
|
||||
GM_INCLUDE_ITERATOR_KERNEL(T)
|
||||
|
||||
inline Iterator()
|
||||
{
|
||||
m_hash = NULL;
|
||||
m_elem = NULL;
|
||||
m_slot = 0;
|
||||
}
|
||||
|
||||
inline Iterator(const gmHash * a_hash)
|
||||
{
|
||||
m_hash = a_hash;
|
||||
m_slot = 0;
|
||||
m_elem = NULL;
|
||||
Inc();
|
||||
}
|
||||
|
||||
inline void Inc(void)
|
||||
{
|
||||
GM_ASSERT(m_hash);
|
||||
if(m_elem)
|
||||
{
|
||||
m_elem = m_hash->GetNext(m_elem);
|
||||
}
|
||||
while(m_elem == NULL && m_slot < m_hash->m_size)
|
||||
{
|
||||
m_elem = m_hash->m_table[m_slot++];
|
||||
}
|
||||
}
|
||||
|
||||
inline void Dec(void) { GM_ASSERT(false); }
|
||||
inline T * Resolve(void) { return m_elem; }
|
||||
inline const T * Resolve(void) const { return m_elem; }
|
||||
inline bool IsValid() const { return (m_elem != NULL); }
|
||||
|
||||
private:
|
||||
|
||||
const gmHash * m_hash;
|
||||
T * m_elem;
|
||||
unsigned int m_slot;
|
||||
};
|
||||
|
||||
// members
|
||||
|
||||
gmHash(gmuint a_size);
|
||||
~gmHash();
|
||||
|
||||
void RemoveAll();
|
||||
void RemoveAndDeleteAll();
|
||||
|
||||
/// \brief Insert() will insert an item into the hash table.
|
||||
/// \return non-null on failure, in which case the returned item is the duplicate existing in the hash
|
||||
T * Insert(T * a_node);
|
||||
|
||||
/// \brief Remove() will remove an item from the hash table
|
||||
/// \return the removed item
|
||||
T * Remove(T * a_node);
|
||||
|
||||
/// \brief Remove() will remove an item from the hash table via an iterator
|
||||
/// \return the removed item
|
||||
T * Remove(Iterator & a_it);
|
||||
|
||||
/// \brief RemoveKey() will remove an item by key
|
||||
/// \return the removed item
|
||||
T * RemoveKey(const KEY &a_key);
|
||||
|
||||
/// \brief Find()
|
||||
T * Find(const KEY &a_key);
|
||||
|
||||
inline gmuint Count() const { return m_count; }
|
||||
inline Iterator First() const { return Iterator(this); }
|
||||
|
||||
private:
|
||||
|
||||
T * GetNext(T * a_elem) const { return a_elem->NQUAL::m_next; }
|
||||
T ** m_table;
|
||||
gmuint m_count;
|
||||
gmuint m_size;
|
||||
|
||||
friend class Iterator;
|
||||
};
|
||||
|
||||
|
||||
/// \class gmDefaultHasher
|
||||
/// \brief use the gmDefaultHasher as the HASHER template arg for the common hashing keys
|
||||
class gmDefaultHasher
|
||||
{
|
||||
public:
|
||||
|
||||
static inline gmuint Hash(const char * a_key)
|
||||
{
|
||||
gmuint key = 0;
|
||||
const char * cp = (const char *) a_key;
|
||||
|
||||
while(*cp != '\0')
|
||||
{
|
||||
key = (key + ((key << 5) + *cp));
|
||||
++cp;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
static inline int Compare(const char * a_keyA, const char * a_keyB)
|
||||
{
|
||||
return strcmp(a_keyA, a_keyB);
|
||||
}
|
||||
|
||||
static inline gmuint Hash(int a_key)
|
||||
{
|
||||
return (gmuint) a_key;
|
||||
}
|
||||
|
||||
static inline int Compare(int a_keyA, int a_keyB)
|
||||
{
|
||||
return (a_keyA - a_keyB);
|
||||
}
|
||||
|
||||
static inline gmuint Hash(const void * a_key)
|
||||
{
|
||||
return (gmuint) (((gmuint) a_key) / sizeof(double));
|
||||
}
|
||||
|
||||
static inline int Compare(const void * a_keyA, const void * a_keyB)
|
||||
{
|
||||
return (int) ((char *) a_keyA - (char *) a_keyB);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
TMPL
|
||||
QUAL::gmHash(gmuint a_size)
|
||||
{
|
||||
// make sure size is power of 2
|
||||
GM_ASSERT((a_size & (a_size - 1)) == 0);
|
||||
m_size = a_size;
|
||||
m_table = GM_NEW(T * [a_size]);
|
||||
int i = m_size;
|
||||
while(i--)
|
||||
{
|
||||
m_table[i] = NULL;
|
||||
}
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
TMPL
|
||||
QUAL::~gmHash()
|
||||
{
|
||||
delete [] m_table;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
void QUAL::RemoveAll()
|
||||
{
|
||||
int i = m_size;
|
||||
while(i--)
|
||||
{
|
||||
m_table[i] = NULL;
|
||||
}
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
void QUAL::RemoveAndDeleteAll()
|
||||
{
|
||||
// iterate over table and delete all
|
||||
int i = m_size;
|
||||
T * node, * next;
|
||||
while(i--)
|
||||
{
|
||||
node = m_table[i];
|
||||
while(node)
|
||||
{
|
||||
next = node->NQUAL::m_next;
|
||||
delete node;
|
||||
node = next;
|
||||
}
|
||||
m_table[i] = NULL;
|
||||
}
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
T * QUAL::Insert(T * a_node)
|
||||
{
|
||||
gmuint slot = HASHER::Hash(a_node->GetKey()) & (m_size - 1);
|
||||
T ** node = &m_table[slot];
|
||||
|
||||
while(*node)
|
||||
{
|
||||
int compare = HASHER::Compare(a_node->GetKey(), (*node)->GetKey());
|
||||
if(compare == 0) return (*node);
|
||||
else if(compare < 0) break;
|
||||
node = &((*node)->NQUAL::m_next);
|
||||
}
|
||||
|
||||
a_node->NQUAL::m_next = *node;
|
||||
*node = a_node;
|
||||
++m_count;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
T * QUAL::Remove(T * a_node)
|
||||
{
|
||||
gmuint slot = HASHER::Hash(a_node->GetKey()) & (m_size - 1);
|
||||
T ** node = &m_table[slot];
|
||||
|
||||
while(*node)
|
||||
{
|
||||
if(a_node == *node)
|
||||
{
|
||||
*node = a_node->NQUAL::m_next;
|
||||
--m_count;
|
||||
return a_node;
|
||||
}
|
||||
node = &((*node)->NQUAL::m_next);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
T * QUAL::Remove(Iterator & a_it)
|
||||
{
|
||||
T * node = a_it.Resolve();
|
||||
if(node)
|
||||
{
|
||||
a_it.Inc();
|
||||
return Remove(node);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TMPL
|
||||
T * QUAL::RemoveKey(const KEY &a_key)
|
||||
{
|
||||
gmuint slot = HASHER::Hash(a_key) & (m_size - 1);
|
||||
T ** node = &m_table[slot];
|
||||
T * found;
|
||||
|
||||
while(*node)
|
||||
{
|
||||
int compare = HASHER::Compare((*node)->GetKey(), a_key);
|
||||
if(compare == 0)
|
||||
{
|
||||
--m_count;
|
||||
found = *node;
|
||||
*node = found->NQUAL::m_next;
|
||||
return (found);
|
||||
}
|
||||
else if(compare > 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
node = &((*node)->NQUAL::m_next);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
TMPL
|
||||
T * QUAL::Find(const KEY &a_key)
|
||||
{
|
||||
gmuint slot = HASHER::Hash(a_key) & (m_size - 1);
|
||||
T * node = m_table[slot];
|
||||
|
||||
while(node)
|
||||
{
|
||||
int compare = HASHER::Compare(static_cast<T*>(node)->GetKey(), a_key);
|
||||
if(compare == 0)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
else if(compare > 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
node = node->NQUAL::m_next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#undef TMPL
|
||||
#undef QUAL
|
||||
#undef NQUAL
|
||||
|
||||
#endif // _GMHASH_H_
|
||||
838
vscript/languages/gm/src/gm/gmIncGC.cpp
Normal file
838
vscript/languages/gm/src/gm/gmIncGC.cpp
Normal file
@@ -0,0 +1,838 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#include "gmConfig.h"
|
||||
#include "gmIncGC.h"
|
||||
|
||||
// Must be last header
|
||||
#include "memdbgon.h"
|
||||
|
||||
// NOTES:
|
||||
|
||||
// o Q: What about when we turn GC off manually to prevent new objects from disappearing ?
|
||||
// A: This can't happen with allocate black AS LONG AS the collect is not called during processing, only at yield.
|
||||
// Still, would be nice to put this functionality in for non-game use.
|
||||
//
|
||||
|
||||
// How to make new types compatible with GC.
|
||||
//
|
||||
// 1) Implement the following user type callbacks:
|
||||
// typedef void (GM_CDECL *gmGCDestructCallback)(gmMachine * a_machine, gmUserObject* a_object);
|
||||
// typedef bool (GM_CDECL *gmGCTraceCallback)(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone);
|
||||
//
|
||||
// o The Destruct function merely destructs and frees the user object. (Same as it used to.)
|
||||
//
|
||||
// o The Trace function calls gc->GetNextObject(obj) where obj is each gmObject* at that level.
|
||||
// You should increment a_workDone for each call and once extra (so min value should be +1).
|
||||
// The Trace has the potential to be re-called for multiple increments, but this functionality is not necessary and has not been tested.
|
||||
// Always return true when finished tracing. Returning false signals that you are not finished and it should be re-called.
|
||||
//
|
||||
// 2) If you make a table or array type class that contains variables that could be gmObjects,
|
||||
// call gc->WriteBarrier(obj) where obj is the old gmObject about to be overwritten in a SetInd or SetDot call etc.
|
||||
//
|
||||
// Note that permanant strings are stored in a separate list so they are ignored by the GC.
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// gmGCColorSet
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
gmGCColorSet::gmGCColorSet()
|
||||
{
|
||||
Init(NULL);
|
||||
}
|
||||
|
||||
|
||||
gmGCColorSet::~gmGCColorSet()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void gmGCColorSet::Init(gmGarbageCollector* a_gc)
|
||||
{
|
||||
m_gc = a_gc;
|
||||
|
||||
// Note that only the Scan and Free markers actually move
|
||||
//
|
||||
// | GRAY BLACK FREE | WHITE |
|
||||
// ^ ^ ^ ^ ^
|
||||
// G S F W T
|
||||
//
|
||||
m_gray = &m_headObject;
|
||||
m_white = &m_separatorObject;
|
||||
m_free = m_white;
|
||||
m_scan = m_free;
|
||||
m_tail = &m_tailObject;
|
||||
|
||||
m_tailObject.SetPrev(&m_separatorObject);
|
||||
m_tailObject.SetNext(NULL);
|
||||
|
||||
m_separatorObject.SetPrev(&m_headObject);
|
||||
m_separatorObject.SetNext(&m_tailObject);
|
||||
|
||||
m_headObject.SetPrev(NULL);
|
||||
m_headObject.SetNext(&m_separatorObject);
|
||||
|
||||
// Make persistList into a list node
|
||||
m_persistList.SetNext(&m_persistList);
|
||||
m_persistList.SetPrev(&m_persistList);
|
||||
|
||||
#if GM_GC_STATS
|
||||
m_numAllocated = 0;
|
||||
#endif //GM_GC_STATS
|
||||
}
|
||||
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
bool gmGCColorSet::VerifyIntegrity()
|
||||
{
|
||||
// Scan through list make sure all pointers are in valid positions and all objects are colored correctly
|
||||
gmGCObjBase* curObj = m_gray->GetNext();
|
||||
int curCol = GM_GC_DEBUG_COL_GRAY;
|
||||
while(curObj != m_tail)
|
||||
{
|
||||
if(curObj == m_scan)
|
||||
{
|
||||
curCol = GM_GC_DEBUG_COL_BLACK;
|
||||
}
|
||||
if(curObj == m_free)
|
||||
{
|
||||
curCol = GM_GC_DEBUG_COL_FREE;
|
||||
}
|
||||
if(curObj == m_white)
|
||||
{
|
||||
curCol = GM_GC_DEBUG_COL_WHITE;
|
||||
}
|
||||
if(curObj != &m_separatorObject)
|
||||
{
|
||||
GM_ASSERT(curObj->m_curPosColor == curCol);
|
||||
}
|
||||
|
||||
curObj = curObj->GetNext();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
|
||||
void gmGCColorSet::DestructPersistantObjects()
|
||||
{
|
||||
int count=0;
|
||||
|
||||
gmGCObjBase* curObj = m_persistList.GetNext();
|
||||
while(curObj != &m_persistList)
|
||||
{
|
||||
gmGCObjBase* objToDestruct = curObj;
|
||||
curObj = curObj->GetNext();
|
||||
|
||||
objToDestruct->Destruct(m_gc->GetVM());
|
||||
++count;
|
||||
}
|
||||
|
||||
// Reset persist list (Make persistList into a list node)
|
||||
m_persistList.SetNext(&m_persistList);
|
||||
m_persistList.SetPrev(&m_persistList);
|
||||
}
|
||||
|
||||
|
||||
int gmGCColorSet::FollowPointers(int a_maxBytesToTrace)
|
||||
{
|
||||
int workDone = 0;
|
||||
|
||||
if(m_gc->GetTraceState().m_object->Trace(m_gc->GetVM(), m_gc, a_maxBytesToTrace, workDone))
|
||||
{
|
||||
m_gc->GetTraceState().m_done = true;
|
||||
}
|
||||
|
||||
return workDone;
|
||||
}
|
||||
|
||||
|
||||
bool gmGCColorSet::BlackenNextGray(int& a_workDone, int a_workLeftToGo)
|
||||
{
|
||||
if(m_gc->GetTraceState().m_done == true)
|
||||
{
|
||||
if(m_scan->GetPrev() == m_gray) // No grays to blacken.
|
||||
{
|
||||
a_workDone = 0;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_scan = m_scan->GetPrev();
|
||||
m_gc->GetTraceState().m_object = m_scan;
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
GM_ASSERT(m_scan->m_curPosColor == GM_GC_DEBUG_COL_GRAY);
|
||||
m_scan->m_curPosColor = GM_GC_DEBUG_COL_BLACK;
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
a_workDone = FollowPointers(a_workLeftToGo);
|
||||
|
||||
return true; // Gray was blackened
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resume previously interrupted scanning of an object
|
||||
a_workDone = FollowPointers(a_workLeftToGo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gmGCColorSet::GrayThisObject(gmGCObjBase* a_obj)
|
||||
{
|
||||
gmGCObjBase* objPrev = a_obj->GetPrev();
|
||||
gmGCObjBase* objNext = a_obj->GetNext();
|
||||
|
||||
// This routine should never get called with a shaded object
|
||||
GM_ASSERT(!m_gc->IsShaded(a_obj));
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
GM_ASSERT(a_obj->m_curPosColor == GM_GC_DEBUG_COL_WHITE);
|
||||
a_obj->m_curPosColor = GM_GC_DEBUG_COL_GRAY;
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
// Set object`s color to shaded first.
|
||||
a_obj->SetColor(m_gc->GetCurShadeColor());
|
||||
|
||||
// The object must be shaded
|
||||
GM_ASSERT(m_gc->IsShaded(a_obj));
|
||||
|
||||
// Splice the object out of the white list
|
||||
// This can be done unconditionally as no set pointers can point to any object in this set.
|
||||
objPrev->SetNext(objNext);
|
||||
objNext->SetPrev(objPrev);
|
||||
|
||||
// Put the object into the correct place in the gray list
|
||||
|
||||
#if DEPTH_FIRST
|
||||
// Put the gray object at the head of the gray list.
|
||||
a_obj->SetPrev(m_scan->GetPrev());
|
||||
a_obj->SetNext(m_scan);
|
||||
m_scan->GetPrev()->SetNext(a_obj);
|
||||
m_scan->SetPrev(a_obj);
|
||||
#else // BREADTH_FIRST
|
||||
// Put the gray object at the tail of the gray list.
|
||||
a_obj->SetPrev(m_gray);
|
||||
a_obj->SetNext(m_gray->GetNext());
|
||||
m_gray->GetNext()->SetPrev(a_obj);
|
||||
m_gray->SetNext(a_obj);
|
||||
#endif // BREADTH_FIRST
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
// Slow, paranoid check
|
||||
VerifyIntegrity();
|
||||
#endif //GM_GC_DEBUG
|
||||
}
|
||||
|
||||
|
||||
void gmGCColorSet::Revive(gmGCObjBase* a_obj)
|
||||
{
|
||||
// NOTE: Once objects are in the free list, we can't trust the color mark,
|
||||
// it may have been set either side of the flip.
|
||||
// We should only revive 'free', but we can't simply tell where in the list the object is.
|
||||
|
||||
// Always mark black, this is only done for strings, and we are logically re-allocating the dead object
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
a_obj->m_curPosColor = GM_GC_DEBUG_COL_BLACK; // Blacken as if re-allocated
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
// Set object`s color to shaded first.
|
||||
a_obj->SetColor(m_gc->GetCurShadeColor());
|
||||
|
||||
// Fix scan (NOTE: If scan == obj, obj must already be black, so could skip rest of this function)
|
||||
if( m_scan == a_obj )
|
||||
{
|
||||
m_scan = m_scan->GetNext(); // Not Prev as scan should be the start of black inclusive
|
||||
}
|
||||
// We don't care if (m_gc->GetTraceState().m_object == a_obj ) as this is Done and write only, and can only be string, not potentially a resumable object.
|
||||
#if GM_GC_DEBUG
|
||||
if( m_gc->GetTraceState().m_object == a_obj )
|
||||
{
|
||||
GM_ASSERT( m_gc->GetTraceState().m_done );
|
||||
}
|
||||
#endif // GM_GC_DEBUG
|
||||
|
||||
// Fix sentinels
|
||||
if( m_free == a_obj )
|
||||
{
|
||||
m_free = m_free->GetNext();
|
||||
}
|
||||
|
||||
// Splice the object out of the free/white list (Or anywhere in this case)
|
||||
gmGCObjBase* objPrev = a_obj->GetPrev();
|
||||
gmGCObjBase* objNext = a_obj->GetNext();
|
||||
objPrev->SetNext(objNext);
|
||||
objNext->SetPrev(objPrev);
|
||||
|
||||
// Insert at the end of black list
|
||||
a_obj->SetNext(m_free); // Next is first Free
|
||||
a_obj->SetPrev(m_free->GetPrev()); // Prev is last Black
|
||||
m_free->GetPrev()->SetNext(a_obj); // Last Black next is now this
|
||||
m_free->SetPrev(a_obj); // Free prev is now this
|
||||
|
||||
// If there were no blacks, move scan forward to prevent scanning this new black
|
||||
if( m_scan == m_free )
|
||||
{
|
||||
m_scan = a_obj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gmGCColorSet::ReclaimGarbage()
|
||||
{
|
||||
GM_ASSERT(m_scan->GetPrev() == m_gray);
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
{
|
||||
// Traverse the newly found garbage objects just to make sure there
|
||||
// aren't any live objects in there. Used for debugging only.
|
||||
for(gmGCObjBase* temp = m_white->GetNext();
|
||||
temp != m_tail;
|
||||
temp = temp->GetNext())
|
||||
{
|
||||
GM_ASSERT(!m_gc->IsShaded(temp));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_white->GetNext() != m_tail) // There are garbage objects
|
||||
{
|
||||
#if GM_GC_DEBUG
|
||||
//flag white->tail as white?
|
||||
for(gmGCObjBase* temp = m_white->GetNext();
|
||||
temp != m_tail;
|
||||
temp = temp->GetNext())
|
||||
{
|
||||
GM_ASSERT(temp->m_curPosColor == GM_GC_DEBUG_COL_WHITE);
|
||||
temp->m_curPosColor = GM_GC_DEBUG_COL_FREE;
|
||||
}
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
bool fixScan = false;
|
||||
if( m_scan == m_free ) // There are no black objects
|
||||
{
|
||||
fixScan = true;
|
||||
}
|
||||
|
||||
// Reclaim the garbage.
|
||||
// Insert old White->Tail at start of Free (Free may == White)
|
||||
gmGCObjBase* firstFree = m_white->GetNext();
|
||||
|
||||
firstFree->SetPrev(m_free->GetPrev());
|
||||
m_free->GetPrev()->SetNext(firstFree);
|
||||
|
||||
m_tail->GetPrev()->SetNext(m_free);
|
||||
m_free->SetPrev(m_tail->GetPrev());
|
||||
|
||||
m_free = firstFree;
|
||||
|
||||
if( fixScan )
|
||||
{
|
||||
m_scan = m_free;
|
||||
}
|
||||
|
||||
m_white->SetNext(m_tail);
|
||||
m_tail->SetPrev(m_white);
|
||||
}
|
||||
|
||||
// Whiten the live objects.
|
||||
if (m_scan != m_free) // There are live (black) objects
|
||||
{
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
//flag scan->free as white?
|
||||
for(gmGCObjBase* temp = m_scan;//m_scan->GetNext();
|
||||
temp != m_free;
|
||||
temp = temp->GetNext())
|
||||
{
|
||||
GM_ASSERT(temp->m_curPosColor == GM_GC_DEBUG_COL_BLACK);
|
||||
temp->m_curPosColor = GM_GC_DEBUG_COL_WHITE;
|
||||
}
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
GM_ASSERT(m_white->GetNext() == m_tail);
|
||||
GM_ASSERT(m_tail->GetPrev() == m_white);
|
||||
|
||||
m_scan->GetPrev()->SetNext(m_free);
|
||||
m_free->GetPrev()->SetNext(m_tail);
|
||||
m_tail->SetPrev(m_free->GetPrev());
|
||||
m_free->SetPrev(m_scan->GetPrev());
|
||||
m_scan->SetPrev(m_white);
|
||||
m_white->SetNext(m_scan);
|
||||
m_scan = m_free;
|
||||
}
|
||||
|
||||
GM_ASSERT(m_gray->GetNext() == m_scan);
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
{
|
||||
int count = 0;
|
||||
for(gmGCObjBase* temp = m_free; temp != m_white; temp = temp->GetNext())
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int gmGCColorSet::DestructSomeFreeObjects(int a_maxToDestruct)
|
||||
{
|
||||
int numDestructed = 0;
|
||||
// Go through the free list (perhaps over multiple installments in future) and call Destruct() on them.
|
||||
if(m_free != m_white)
|
||||
{
|
||||
gmGCObjBase* beforeFree = m_free->GetPrev(); // Save previous node so we can relink after removing some
|
||||
bool fixScan = false;
|
||||
if(m_scan == m_free) // Will need to fix the scan ptr later if this is so.
|
||||
{
|
||||
fixScan = true;
|
||||
}
|
||||
|
||||
while(m_free != m_white)
|
||||
{
|
||||
gmGCObjBase* objToRecycle = m_free;
|
||||
m_free = m_free->GetNext();
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
//GM_ASSERT(objToRecycle->m_curPosColor == GM_GC_DEBUG_COL_WHITE);
|
||||
GM_ASSERT(objToRecycle->m_curPosColor == GM_GC_DEBUG_COL_FREE);
|
||||
objToRecycle->m_curPosColor = GM_GC_DEBUG_COL_INVALID;
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
#if GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
GM_ASSERT(!objToRecycle->GetPersist());
|
||||
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
#if GM_GC_STATS
|
||||
--m_numAllocated;
|
||||
#endif //GM_GC_STATS
|
||||
|
||||
objToRecycle->Destruct(m_gc->GetVM());
|
||||
++numDestructed;
|
||||
--a_maxToDestruct;
|
||||
if(a_maxToDestruct <= 0)
|
||||
{
|
||||
// Relink since we removed elements
|
||||
beforeFree->SetNext(m_free);
|
||||
m_free->SetPrev(beforeFree);
|
||||
if(fixScan)
|
||||
{
|
||||
m_scan = m_free;
|
||||
}
|
||||
return numDestructed; // Work is done for now.
|
||||
}
|
||||
}
|
||||
// Relink since we removed elements
|
||||
beforeFree->SetNext(m_free);
|
||||
m_free->SetPrev(beforeFree);
|
||||
if(fixScan)
|
||||
{
|
||||
m_scan = m_free;
|
||||
}
|
||||
}
|
||||
return numDestructed;
|
||||
}
|
||||
|
||||
|
||||
void gmGCColorSet::Allocate(gmGCObjBase* a_obj)
|
||||
{
|
||||
#if GM_GC_STATS
|
||||
++m_numAllocated;
|
||||
#endif //GM_GC_STATS
|
||||
|
||||
a_obj->SetPersist(false);
|
||||
|
||||
a_obj->SetColor(m_gc->GetCurShadeColor());
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
GM_ASSERT(a_obj->m_curPosColor == GM_GC_DEBUG_COL_INVALID);
|
||||
a_obj->m_curPosColor = GM_GC_DEBUG_COL_BLACK;
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
//Insert at the end of black list
|
||||
a_obj->SetNext(m_free); //Next is first Free
|
||||
a_obj->SetPrev(m_free->GetPrev()); //Prev is last Black
|
||||
m_free->GetPrev()->SetNext(a_obj); //Last Black next is now this
|
||||
m_free->SetPrev(a_obj); //Free prev is now this
|
||||
|
||||
//If there were no blacks, move scan forward to prevent scanning this new black
|
||||
if(m_scan == m_free)
|
||||
{
|
||||
m_scan = a_obj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gmGCColorSet::DestructAll()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
DestructPersistantObjects();
|
||||
|
||||
// Black and Gray
|
||||
gmGCObjBase* curGrayOrBlack = m_gray->GetNext();
|
||||
while(curGrayOrBlack != m_free)
|
||||
{
|
||||
gmGCObjBase* objToRecycle = curGrayOrBlack;
|
||||
curGrayOrBlack = curGrayOrBlack->GetNext();
|
||||
#if GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
GM_ASSERT(!objToRecycle->GetPersist());
|
||||
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
objToRecycle->Destruct(m_gc->GetVM());
|
||||
#if GM_GC_STATS
|
||||
--m_numAllocated;
|
||||
#endif //GM_GC_STATS
|
||||
++count;
|
||||
}
|
||||
|
||||
// Whites
|
||||
gmGCObjBase* curWhite = m_white->GetNext();
|
||||
while(curWhite != m_tail)
|
||||
{
|
||||
gmGCObjBase* objToRecycle = curWhite;
|
||||
curWhite = curWhite->GetNext();
|
||||
|
||||
#if GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
GM_ASSERT(!objToRecycle->GetPersist());
|
||||
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
objToRecycle->Destruct(m_gc->GetVM());
|
||||
#if GM_GC_STATS
|
||||
--m_numAllocated;
|
||||
#endif //GM_GC_STATS
|
||||
++count;
|
||||
}
|
||||
|
||||
// Free list
|
||||
gmGCObjBase* curFree = m_free;
|
||||
while(curFree != m_white)
|
||||
{
|
||||
gmGCObjBase* objToRecycle = curFree;
|
||||
curFree = curFree->GetNext();
|
||||
{
|
||||
#if GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
GM_ASSERT(!objToRecycle->GetPersist());
|
||||
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
objToRecycle->Destruct(m_gc->GetVM());
|
||||
#if GM_GC_STATS
|
||||
--m_numAllocated;
|
||||
#endif //GM_GC_STATS
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
Init(m_gc);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// gmGarbageCollector
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
gmGarbageCollector::gmGarbageCollector()
|
||||
{
|
||||
Init(NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
gmGarbageCollector::~gmGarbageCollector()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void gmGarbageCollector::Init(gmGCScanRootsCallBack a_scanRootsCallback, gmMachine* a_gmMachine)
|
||||
{
|
||||
m_curShadeColor = 0; // Another color is !0
|
||||
m_workPerIncrement = GM_GC_DEFAULT_WORK_INCREMENT;
|
||||
m_maxObjsToDestructPerIncrement = GM_GC_DEFAULT_DESTRUCT_INCREMENT;
|
||||
m_workLeftToGo = 0;
|
||||
m_fullThrottle = false;
|
||||
m_gcTurnedOff = true; // Start in OFF state, machine will turn on when needed.
|
||||
m_firstCollectionIncrement = true;
|
||||
m_doneTracing = false;
|
||||
m_colorSet.Init(this);
|
||||
m_traceState.Reset();
|
||||
m_flipCallback = NULL;
|
||||
m_scanRootsCallback = a_scanRootsCallback;
|
||||
m_gmMachine = a_gmMachine;
|
||||
}
|
||||
|
||||
|
||||
bool gmGarbageCollector::BlackenGrays()
|
||||
{
|
||||
int workDone;
|
||||
|
||||
while(m_colorSet.AnyGrays())
|
||||
{
|
||||
// gmGCColorSet::BlackenNextGray returns 1 if there was a gray to
|
||||
// blacken (even if it couldn't finish blackening it), and a 0 otherwise.
|
||||
|
||||
workDone = m_workLeftToGo;
|
||||
|
||||
while(m_colorSet.BlackenNextGray(workDone, m_workLeftToGo))
|
||||
{
|
||||
m_workLeftToGo -= workDone;
|
||||
if (m_workLeftToGo <= 0)
|
||||
{
|
||||
// Quit early
|
||||
return true; // We have completed one increment of work
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool gmGarbageCollector::Collect()
|
||||
{
|
||||
if(m_fullThrottle)
|
||||
{
|
||||
m_workLeftToGo = GM_MAX_INT32;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_workLeftToGo = m_workPerIncrement;
|
||||
}
|
||||
|
||||
m_doneTracing = false;
|
||||
|
||||
if(m_firstCollectionIncrement)
|
||||
{
|
||||
// Scan each root object and gray it
|
||||
GM_ASSERT(m_scanRootsCallback);
|
||||
m_scanRootsCallback(m_gmMachine, this);
|
||||
|
||||
m_firstCollectionIncrement = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If any grays exist, scan them first
|
||||
if(m_colorSet.AnyGrays())
|
||||
{
|
||||
if(BlackenGrays()) // Returns 0 if no more grays, and 1 if done with an increment of collection.
|
||||
{
|
||||
return false; // Out of time, so exit function
|
||||
}
|
||||
}
|
||||
|
||||
m_doneTracing = true;
|
||||
|
||||
// Let the collect continue until garbage memory has been reclaimed
|
||||
// This could be done as an external phase
|
||||
if(ReclaimSomeFreeObjects())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if GM_GC_TURN_OFF_ABLE
|
||||
// Turn off gc until almost out of memory.
|
||||
// Can only do when allocating black.
|
||||
m_gcTurnedOff = true;
|
||||
#else //GM_GC_TURN_OFF_ABLE
|
||||
Flip();
|
||||
#endif //GM_GC_TURN_OFF_ABLE
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void gmGarbageCollector::Flip()
|
||||
{
|
||||
m_firstCollectionIncrement = true;
|
||||
|
||||
if(m_flipCallback)
|
||||
{
|
||||
m_flipCallback();
|
||||
}
|
||||
|
||||
#if GM_GC_TURN_OFF_ABLE
|
||||
// The garbage collector can only be turned off if we are allocating black.
|
||||
m_gcTurnedOff = false; // Turn the garbage collector back on.
|
||||
#endif //GM_GC_TURN_OFF_ABLE
|
||||
|
||||
m_colorSet.ReclaimGarbage();
|
||||
|
||||
ToggleCurShadeColor();
|
||||
}
|
||||
|
||||
|
||||
// This function is called when there are no free objects in the colorset.
|
||||
// If the gc is turned off, it calls flip to reclaim any garbage objects
|
||||
// that have been found by the garbage collector.
|
||||
void gmGarbageCollector::ReclaimObjectsAndRestartCollection()
|
||||
{
|
||||
#if GM_GC_TURN_OFF_ABLE
|
||||
// The garbage collector only gets turned off if we are allocating
|
||||
// black. GC is turned off after finishing tracing and before
|
||||
// doing a gc flip. So if there are no free objects left, first
|
||||
// flip the GC and turn white objects into free. Hopefully, this
|
||||
// will provide more free objects for allocation.
|
||||
|
||||
if(m_gcTurnedOff)
|
||||
{
|
||||
Flip();
|
||||
}
|
||||
#endif //GM_GC_TURN_OFF_ABLE
|
||||
}
|
||||
|
||||
|
||||
/// \brief Destruct all objects.
|
||||
void gmGarbageCollector::DestructAll()
|
||||
{
|
||||
m_colorSet.DestructAll();
|
||||
|
||||
//Reset some of our members
|
||||
m_curShadeColor = 0;
|
||||
m_workPerIncrement = 100;
|
||||
m_maxObjsToDestructPerIncrement = 100;
|
||||
m_workLeftToGo = 0;
|
||||
m_doneTracing = false;
|
||||
m_fullThrottle = false;
|
||||
m_firstCollectionIncrement = true;
|
||||
m_traceState.Reset();
|
||||
}
|
||||
|
||||
|
||||
void gmGarbageCollector::FullCollect()
|
||||
{
|
||||
m_fullThrottle = true;
|
||||
|
||||
if(IsOff()) // If GC is off
|
||||
{
|
||||
ReclaimObjectsAndRestartCollection(); // Do flip and turn it back on
|
||||
}
|
||||
|
||||
while(!Collect())
|
||||
{
|
||||
// Do the collect phase
|
||||
}
|
||||
ReclaimObjectsAndRestartCollection(); // Do flip and turn it back on
|
||||
|
||||
// Collect a second time to catch floating black objects
|
||||
while(!Collect())
|
||||
{
|
||||
// Do the collect phase
|
||||
}
|
||||
ReclaimObjectsAndRestartCollection(); // Do flip and turn it back on
|
||||
|
||||
// NOTE: The GC is now restarted and in an 'On' state, meaning it will now collect again from the machine.
|
||||
// This behavior may not be desirable, so this function really needs more analysis to determine the
|
||||
// optimum sequence for a full collect with minimal redundancy.
|
||||
|
||||
// Free memory of garbage objects
|
||||
while(ReclaimSomeFreeObjects())
|
||||
{
|
||||
// Reclaim all garbage
|
||||
}
|
||||
m_fullThrottle = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Helper functions for VM and debugger
|
||||
//////////////////////////////////////////////////
|
||||
#include "gmVariable.h"
|
||||
#include "gmFunctionObject.h"
|
||||
|
||||
const void* gmGCColorSet::GetInstructionAtBreakPoint(gmuint32 a_sourceId, int a_line)
|
||||
{
|
||||
gmGCObjBase* cur;
|
||||
|
||||
// Search Gray to Free
|
||||
cur = m_gray->GetNext();
|
||||
while(cur != m_free)
|
||||
{
|
||||
gmObject* object = (gmObject*)cur;
|
||||
if(object->GetType() == GM_FUNCTION)
|
||||
{
|
||||
gmFunctionObject * function = (gmFunctionObject *) object;
|
||||
if(function->GetSourceId() == a_sourceId)
|
||||
{
|
||||
const void * instr = function->GetInstructionAtLine(a_line);
|
||||
if(instr)
|
||||
{
|
||||
return instr;
|
||||
}
|
||||
}
|
||||
}
|
||||
cur = cur->GetNext();
|
||||
}
|
||||
|
||||
// Search White
|
||||
cur = m_white->GetNext();
|
||||
while(cur != m_tail)
|
||||
{
|
||||
gmObject* object = (gmObject*)cur;
|
||||
if(object->GetType() == GM_FUNCTION)
|
||||
{
|
||||
gmFunctionObject * function = (gmFunctionObject *) object;
|
||||
if(function->GetSourceId() == a_sourceId)
|
||||
{
|
||||
const void * instr = function->GetInstructionAtLine(a_line);
|
||||
if(instr)
|
||||
{
|
||||
return instr;
|
||||
}
|
||||
}
|
||||
}
|
||||
cur = cur->GetNext();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
gmObject* gmGCColorSet::CheckReference(gmptr a_ref)
|
||||
{
|
||||
gmGCObjBase* cur;
|
||||
|
||||
// Search Gray to Free
|
||||
cur = m_gray->GetNext();
|
||||
while(cur != m_free)
|
||||
{
|
||||
gmObject* object = (gmObject*)cur;
|
||||
if((gmptr)object == a_ref)
|
||||
{
|
||||
return object;
|
||||
}
|
||||
cur = cur->GetNext();
|
||||
}
|
||||
|
||||
// Search White
|
||||
cur = m_white->GetNext();
|
||||
while(cur != m_tail)
|
||||
{
|
||||
gmObject* object = (gmObject*)cur;
|
||||
if((gmptr)object == a_ref)
|
||||
{
|
||||
return object;
|
||||
}
|
||||
cur = cur->GetNext();
|
||||
}
|
||||
|
||||
// Search Persistant list
|
||||
cur = m_persistList.GetNext();
|
||||
while(cur != &m_persistList)
|
||||
{
|
||||
gmObject* object = (gmObject*)cur;
|
||||
if((gmptr)object == a_ref)
|
||||
{
|
||||
return object;
|
||||
}
|
||||
cur = cur->GetNext();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
436
vscript/languages/gm/src/gm/gmIncGC.h
Normal file
436
vscript/languages/gm/src/gm/gmIncGC.h
Normal file
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
_____ __ ___ __ ____ _ __
|
||||
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
||||
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
||||
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
||||
/___/ /_/
|
||||
|
||||
See Copyright Notice in gmMachine.h
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _GMINCGC_H_
|
||||
#define _GMINCGC_H_
|
||||
|
||||
#include "gmConfig.h"
|
||||
|
||||
// Configuration options
|
||||
#define GM_GC_TURN_OFF_ABLE 1 // Let GC turn off after completion, can be turned back on when memory low
|
||||
#define GM_GC_KEEP_PERSISTANT_SEPARATE 1 // Keep persistant object in separate list for efficiency
|
||||
#define DEPTH_FIRST 1 // Depth first or bredth first tracing
|
||||
#ifdef GM_DEBUG_BUILD
|
||||
#define GM_GC_STATS 1 // Some stats
|
||||
#else //GM_DEBUG_BUILD
|
||||
#define GM_GC_STATS 0
|
||||
#endif //GM_DEBUG_BUILD
|
||||
|
||||
#define GM_GC_DEBUG 0 // GC Debugging paranoid check code. Only set to 1 when debugging the GC routines.
|
||||
|
||||
// Fwd decls
|
||||
class gmgmGCObjBase;
|
||||
class gmGarbageCollector;
|
||||
class gmMachine;
|
||||
class gmObject;
|
||||
|
||||
typedef void (GM_CDECL *GCFlipCallBack)();
|
||||
typedef void (GM_CDECL *gmGCScanRootsCallBack)(gmMachine* a_machine, gmGarbageCollector* a_gc);
|
||||
|
||||
|
||||
// Incremental garbage collection method:
|
||||
//
|
||||
// A tricolor marking scheme is used with new objects allocated Black.
|
||||
// A write barrier is used to maintain integrity of the list.
|
||||
// The list looks like:
|
||||
//
|
||||
// | GRAY BLACK FREE | WHITE |
|
||||
// ^ ^ ^ ^ ^
|
||||
// G S F W T
|
||||
//
|
||||
// Legend:
|
||||
// G Gray (head) pointer
|
||||
// S Scan pointer
|
||||
// F Free pointer
|
||||
// W White pointer
|
||||
// T Tail pointer
|
||||
// | List sentinels
|
||||
//
|
||||
// Note that only the Scan and Free markers actually move
|
||||
// The objects are classified between the pointer pairs as follows:
|
||||
//
|
||||
// GRAY: Gray (exclusive) to Scan (exclusive)
|
||||
// BLACK: Scan (inclusive) to Free (exclusive)
|
||||
// FREE: Free (inclusive) to White (exclusive)
|
||||
// WHITE: White (exclusive) to Tail (exclusive)
|
||||
//
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// gmgmGCObjBase
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
enum
|
||||
{
|
||||
GM_GC_DEBUG_COL_INVALID, //0
|
||||
GM_GC_DEBUG_COL_GRAY, //1
|
||||
GM_GC_DEBUG_COL_BLACK, //2
|
||||
GM_GC_DEBUG_COL_WHITE, //3
|
||||
GM_GC_DEBUG_COL_FREE, //4
|
||||
};
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
/// \brief All GC objects are dervied from this class
|
||||
class gmGCObjBase
|
||||
{
|
||||
public:
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
gmGCObjBase()
|
||||
{
|
||||
m_curPosColor = GM_GC_DEBUG_COL_INVALID;
|
||||
}
|
||||
int m_curPosColor;
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
inline void SetColor(int a_color) {m_color = (char)a_color;}
|
||||
inline int GetColor() {return (int)m_color;}
|
||||
inline gmGCObjBase* GetPrev() const {return m_prev;}
|
||||
inline void SetPrev(gmGCObjBase* a_prev) {m_prev = a_prev;}
|
||||
inline gmGCObjBase* GetNext() const {return m_next;}
|
||||
inline void SetNext(gmGCObjBase* a_next) {m_next = a_next;}
|
||||
|
||||
inline char GetPersist() {return m_persist;}
|
||||
inline void SetPersist(bool a_flag) {m_persist = a_flag;}
|
||||
|
||||
/// \brief Called when GC wants to free this memory
|
||||
virtual void Destruct(gmMachine * a_machine) {}
|
||||
|
||||
/// \brief Trace pointers this object contains to other objects.
|
||||
/// It must call gmGarbageCollector::GetNextObject() on each pointer,
|
||||
/// until it has traced all pointers, or used up the a_workLeftToGo count.
|
||||
/// \param a_workLeftToGo Number of pointers to trace.
|
||||
/// \param a_workDone, The number of pointers traced.
|
||||
/// \return true if finished tracing this object, false if not finished.
|
||||
virtual bool Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
|
||||
{
|
||||
//NOTE: Use a_gc->GetTraceState().m_context to help resume incremental tracing.
|
||||
a_workDone = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
gmGCObjBase* m_prev; ///< Point to previous object in color set
|
||||
gmGCObjBase* m_next; ///< Point to next object in color set
|
||||
char m_color; ///< Is gray or black flag, really only need by 1 bit
|
||||
char m_persist; ///< This object is persistant
|
||||
char m_pad[2]; ///< Pad to dword
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// gmGCColorSet
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
/// \brief Tri color managing class
|
||||
class gmGCColorSet
|
||||
{
|
||||
public:
|
||||
|
||||
/// \brief Constructor
|
||||
gmGCColorSet();
|
||||
/// \brief Destructor
|
||||
virtual ~gmGCColorSet();
|
||||
|
||||
/// \brief Initialize members
|
||||
void Init(gmGarbageCollector* a_gc);
|
||||
|
||||
/// \brief Returns true if there are any gray objects in this size class, false otherwise.
|
||||
inline bool AnyGrays(void)
|
||||
{
|
||||
return (m_gray->GetNext() != m_scan);
|
||||
}
|
||||
|
||||
int FollowPointers(int a_maxBytesToTrace);
|
||||
|
||||
/// \brief Blacken the next object (at the scan pointer) in the gray set.
|
||||
/// Object is blackened and its children are grayed.
|
||||
/// Returns 0 when no grays are left.
|
||||
bool BlackenNextGray(int& a_workDone, int a_workLeftToGo);
|
||||
|
||||
/// \brief Called by GCGetNextObject() that is called by user code while tracing over a object.
|
||||
inline void GrayAWhite(gmGCObjBase* a_obj);
|
||||
|
||||
/// \brief Gray this object.
|
||||
void GrayThisObject(gmGCObjBase* a_obj);
|
||||
|
||||
/// \brief Called on a new object being allocated.
|
||||
void Allocate(gmGCObjBase* a_obj);
|
||||
|
||||
/// \brief This routine reclaims the garbage memory for the system.
|
||||
void ReclaimGarbage();
|
||||
|
||||
/// \brief Destruct some free objects
|
||||
/// \return The number of objects destructed
|
||||
int DestructSomeFreeObjects(int a_maxToDestruct);
|
||||
|
||||
/// \brief Destruct all objects.
|
||||
void DestructAll();
|
||||
|
||||
/// \brief Make an object persistant by moving it into the persistant list.
|
||||
void MakePersistant(gmGCObjBase* a_obj)
|
||||
{
|
||||
// Fix scan (If scan == obj, obj must already be black, as we have just allocated or revived this string)
|
||||
if( m_scan == a_obj )
|
||||
{
|
||||
m_scan = m_scan->GetNext(); // Not Prev as scan should be the start of black inclusive
|
||||
}
|
||||
// Fix sentinels
|
||||
if( m_free == a_obj )
|
||||
{
|
||||
m_free = m_free->GetNext();
|
||||
}
|
||||
|
||||
// Unlink from current list
|
||||
a_obj->GetNext()->SetPrev(a_obj->GetPrev());
|
||||
a_obj->GetPrev()->SetNext(a_obj->GetNext());
|
||||
|
||||
// Insert at start of persist list
|
||||
a_obj->SetNext(m_persistList.GetNext());
|
||||
a_obj->SetPrev(&m_persistList);
|
||||
|
||||
m_persistList.GetNext()->SetPrev(a_obj);
|
||||
m_persistList.SetNext(a_obj);
|
||||
}
|
||||
|
||||
/// \brief Destruct all objects in persistant list
|
||||
void DestructPersistantObjects();
|
||||
|
||||
/// \brief Revive an object before it is finalized
|
||||
void Revive(gmGCObjBase* a_obj);
|
||||
|
||||
/// \brief Check if reference is valid for VM
|
||||
gmObject* CheckReference(gmptr a_ref);
|
||||
|
||||
/// \brief Get instruction at point for VM Debugger.
|
||||
const void * GetInstructionAtBreakPoint(gmuint32 a_sourceId, int a_line);
|
||||
|
||||
#if GM_GC_DEBUG
|
||||
bool VerifyIntegrity();
|
||||
#endif //GM_GC_DEBUG
|
||||
|
||||
protected:
|
||||
|
||||
gmGCObjBase* m_gray;
|
||||
gmGCObjBase* m_scan;
|
||||
gmGCObjBase* m_free;
|
||||
gmGCObjBase* m_white;
|
||||
gmGCObjBase* m_tail;
|
||||
|
||||
gmGCObjBase m_tailObject;
|
||||
gmGCObjBase m_headObject;
|
||||
gmGCObjBase m_separatorObject;
|
||||
|
||||
gmGCObjBase m_persistList;
|
||||
|
||||
gmGarbageCollector* m_gc;
|
||||
#if GM_GC_STATS
|
||||
int m_numAllocated;
|
||||
#endif //GM_GC_STATS
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// gmGarbageCollector
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
struct gmGCTraceState
|
||||
{
|
||||
gmGCTraceState()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
m_done = true;
|
||||
m_object = NULL;
|
||||
m_context = NULL;
|
||||
}
|
||||
|
||||
bool m_done;
|
||||
gmGCObjBase* m_object;
|
||||
void* m_context;
|
||||
};
|
||||
|
||||
|
||||
/// \brief Incremental garbage collection
|
||||
/// Method: Tri-Color, Non-copying, Write barrier, Root Snapshot.
|
||||
class gmGarbageCollector
|
||||
{
|
||||
public:
|
||||
|
||||
/// \brief Constructor
|
||||
gmGarbageCollector();
|
||||
|
||||
/// \brief Destructor
|
||||
virtual ~gmGarbageCollector();
|
||||
|
||||
/// \brief Initialize the garbage collection system
|
||||
void Init(gmGCScanRootsCallBack a_scanRootsCallback, gmMachine* a_gmMachine);
|
||||
|
||||
/// \brief Perform write barrier operation on Left and/or Right side objects.
|
||||
inline void WriteBarrier(gmGCObjBase* a_lObj /*, gmGCObjBase* a_rObj*/);
|
||||
|
||||
/// \brief Call to start collection
|
||||
/// \return true if collection completed, false if more work to do.
|
||||
bool Collect();
|
||||
|
||||
/// \brief Do a full collect and don't return until done.
|
||||
void FullCollect();
|
||||
|
||||
/// \brief Called during trace by client code, and by scan roots callback.
|
||||
/// This grays a white object.
|
||||
inline void GetNextObject(gmGCObjBase* a_obj) {m_colorSet.GrayAWhite(a_obj);}
|
||||
|
||||
/// \brief Called on a new object being allocated
|
||||
inline void AllocateObject(gmGCObjBase* a_obj) {m_colorSet.Allocate(a_obj);}
|
||||
|
||||
/// \brief Get the current shade color since it is flipped each cycle.
|
||||
inline int GetCurShadeColor() {return (m_curShadeColor);}
|
||||
|
||||
/// \brief Is the object colored gray or black?
|
||||
inline bool IsShaded(gmGCObjBase* a_obj) {return (a_obj->GetColor() == m_curShadeColor);}
|
||||
|
||||
/// \brief if GC is turned off, reclaim garbage memory
|
||||
void ReclaimObjectsAndRestartCollection();
|
||||
|
||||
/// \brief Get the trace state to resume incremental collecting
|
||||
inline gmGCTraceState& GetTraceState() {return m_traceState;}
|
||||
|
||||
/// \brief Set the amount of work to do per increment of collecting
|
||||
inline void SetWorkPerIncrement(int a_workPerIncrement) {m_workPerIncrement = a_workPerIncrement;}
|
||||
/// \brief Set the amount of objects to destruct per increment of collecting
|
||||
inline void SetDestructPerIncrement(int a_destructPerIncrement) {m_maxObjsToDestructPerIncrement = a_destructPerIncrement;}
|
||||
|
||||
/// \brief Get the amount of work to do per increment of collecting
|
||||
inline int GetWorkPerIncrement() {return m_workPerIncrement;}
|
||||
/// \brief Get the amount of objects to destruct per increment of collecting
|
||||
inline int GetDestructPerIncrement() {return m_maxObjsToDestructPerIncrement;}
|
||||
|
||||
/// \brief Set function to be called before flip when dead objects are reclaimed.
|
||||
/// Optional, so pass NULL to disable.
|
||||
inline void SetFlipCallback(GCFlipCallBack a_flipCallback) {m_flipCallback = a_flipCallback;}
|
||||
|
||||
/// \brief Is collector currently turned off?
|
||||
inline bool IsOff() {return m_gcTurnedOff;}
|
||||
|
||||
/// \brief Destruct all objects.
|
||||
void DestructAll();
|
||||
|
||||
/// \brief Reclaim some free objects.
|
||||
int ReclaimSomeFreeObjects() {return m_colorSet.DestructSomeFreeObjects(m_maxObjsToDestructPerIncrement);}
|
||||
|
||||
/// \brief Make an object persistant by moving it into the persistant list.
|
||||
void MakeObjectPersistant(gmGCObjBase* a_obj) {m_colorSet.MakePersistant(a_obj);}
|
||||
|
||||
/// \brief Get the virtual machine for language
|
||||
inline gmMachine* GetVM() {return m_gmMachine;}
|
||||
|
||||
/// \brief Check if reference is valid for VM
|
||||
gmObject* CheckReference(gmptr a_ref) {return m_colorSet.CheckReference(a_ref);}
|
||||
|
||||
/// \brief Get instruction at point for VM Debugger.
|
||||
const void * GetInstructionAtBreakPoint(gmuint32 a_sourceId, int a_line) {return m_colorSet.GetInstructionAtBreakPoint(a_sourceId, a_line);}
|
||||
|
||||
/// \brief Revive a dead object (only used to re-live a shared string before it is finalized)
|
||||
void Revive(gmGCObjBase* a_obj)
|
||||
{
|
||||
if( !a_obj->GetPersist() )
|
||||
{
|
||||
m_colorSet.Revive(a_obj);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/// \brief Has Collect() completed yet?
|
||||
inline bool IsDoneTracing() {return m_doneTracing;}
|
||||
|
||||
/// \brief Called by Collect()
|
||||
bool BlackenGrays();
|
||||
|
||||
/// \brief Flip system and reclaim garbage.
|
||||
void Flip();
|
||||
|
||||
/// \brief Toggle bit used to represent 'colored'
|
||||
inline void ToggleCurShadeColor() {m_curShadeColor = !m_curShadeColor;}
|
||||
|
||||
gmGCColorSet m_colorSet; ///< Tri color helper class
|
||||
int m_curShadeColor; ///< Cur color used to shade this generation
|
||||
int m_workPerIncrement; ///< How much work to do per increment
|
||||
int m_workLeftToGo; ///< How much work left in this increment
|
||||
int m_maxObjsToDestructPerIncrement; ///< How much destructing work to do this frame?
|
||||
bool m_gcTurnedOff; ///< Is the GC currently turned off?
|
||||
bool m_firstCollectionIncrement; ///< Using snapshot method, scan roots atomically first
|
||||
bool m_fullThrottle; ///< Set to true when forcing a full collection
|
||||
bool m_doneTracing; ///< Has Collect() completed yet?
|
||||
gmGCTraceState m_traceState; ///< Allows trace to resume where it left off
|
||||
|
||||
GCFlipCallBack m_flipCallback; ///< Called before flip, when dead objects are reclaimed. Default is NULL.
|
||||
gmGCScanRootsCallBack m_scanRootsCallback; ///< Called at start of Collect() to add gray all roots. MUST be implemented.
|
||||
gmMachine* m_gmMachine; ///< Virtual machine to pass around
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// inline functions
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
void gmGCColorSet::GrayAWhite(gmGCObjBase* a_obj)
|
||||
{
|
||||
#if GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
if(a_obj->GetPersist()) // Don't do anything with persistant objects
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
|
||||
// If right object is not shaded, shade it
|
||||
if(!m_gc->IsShaded(a_obj))
|
||||
{
|
||||
GrayThisObject(a_obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gmGarbageCollector::WriteBarrier(gmGCObjBase* a_lObj/*, gmGCObjBase* a_rObj*/)
|
||||
{
|
||||
// If we are allocating black and the collector is off, do nothing
|
||||
if(m_gcTurnedOff)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't need to use write barrier on root objects, so check for it if we can.
|
||||
// if(IsRoot(a_lObj)) { return; }
|
||||
|
||||
// Note: Left side is logically the old right side
|
||||
|
||||
// This is a snapshot write barrier. There is not old pointer to overwrite, so do nothing.
|
||||
if(!a_lObj)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
if(a_lObj->GetPersist()) // Don't do anything with persistant objects
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
|
||||
|
||||
if(!IsShaded(a_lObj))
|
||||
{
|
||||
m_colorSet.GrayThisObject(a_lObj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //_GMINCGC_H_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user