Hello,
I am the new IWBasic compiler coder. In this topic I will share with you what changes are made, and what I'm working on.
This topic will be modified as I'm progressing with the compiler.
Command line:
- syntax: /[option][parameter] without space between option name and parameter. Option name is not case sensitive.
- option /As - Compile and assemble. Optional s is passed to nasm.
- option /Bpath - include file <path> before parsing the main file.
- option /C number - change default codepage from Ansi (CP_ACP) to number. This option ensures that unicode strigs L"" are properly converted from multibyte character set (controlled by your text editor) to unicode binary data. If your editor is working with utf-8, set this option to /C65001 (CP_UTF8).
You will be warned if a codepage is not valid for you system. - option /D name{=value} - define a numeric constant.
- option /E number - show up to number errors; default is 5. Does not apply to syntax errors.
- option /H or /? displays this help.
- option /I path - add a search path for include files. Pass full path, or a relative path to current file (\..\ is supported)
- option /Lname.dll - Loads a plugin (TODO).
- option /P defines arguments and local variables for use in inline assembly. Take care of variable names, so that they don't interfere with assembler registers or instructions.
sub sum(int a, int b),int
int c
_asm
mov eax,[a]
add eax,[b]
mov [c],eax
_endasm
return c
endsub
- option /R - Inputfile specifies a path to response file, where each line is a pathname to an IWBasic source file.
- option /W number - disable warnings. 1: uninitialized variable, 2: unreferenced variable, and 5 more for advanced users. If not specified then all warnings are enabled.
You can set command line options directly from the source code:
$option "name=value"
$option "name value"
Where
name can be one of: /c or Codepage, /e or ErrorLimit, /i or IncludePath, /p or AsmVariables, /w or Warning, Return, Intrinsic, Optimization, CaseSensitive, novtable, oldlibslocation, strict, ZeroVariables, /m, /a, LibPath, QuoteLibPaths, ResolveLibs.
Value depends on the current option, and can be:
- Codepage - push,pop,utf8,ansi,number. Sets the codepage for unicode strings.
- ErrorLimit - push,pop,number. Sets the upper limit for compiler error messages.
- IncludePath - string. Add a search path for include files.
- AsmVariables - push,pop,0,1,yes,no,on,off,enable,disable. Define formal and local variables for inline assembly code.
- Warning - push,pop,default,number,enable:number,disable:number. Sets a new or modifies the current filter for warning messages.
- Return - push,pop,0,1,yes,no,on,off,enable,disable. Force the compiler to not alter eax,edx when returning from a subroutine. The automatic "return 0" will be inactive, so be sure to return zero from window handlers.
- Intrinsic - push,pop,0,1,yes,no,on,off,enable,disable. Enable/disable intrinsic functions.
- Optimization - push,pop,0,1,yes,no,on,off,enable,disable. Enable/disable Optimizations.
- CaseSensitive - push,pop,0,1,yes,no,on,off,enable,disable. Enable/disable case sensitive compilation.
- NoVtable - push,pop,0,1,yes,no,on,off,enable,disable.
- OldLibsLocation - push,pop,0,1,yes,no,on,off,enable,disable. Controls where the compiler should create $$$lib.link file. Previously the /bin directory was used, and the new IWBasic will use project directory.
- strict - push,pop,0,1,yes,no,on,off,enable,disable. Show any possible problems, for example unknown type in DECLARE (even unused).
- ZeroVariables - push,pop,0,1,yes,no,on,off,enable,disable. If active, local variables will be initialized to zero.
- /m - push,pop,0,1,yes,no,on,off,enable,disable. The same as command line /m switch, but it can be canceled.
- /a - push,pop,0,1,yes,no,on,off,enable,disable. Compile and assemble.
- LibPath - directory. Add a search path for .lib files. Used with ResolveLibs option to write full lib paths to $$$lib.link file.
- QuoteLibPaths - push,pop,0,1,yes,no,on,off,enable,disable. Quote spaces in $$$lib.link file. Useful option if you want to use another linker.
- ResolveLibs - push,pop,0,1,yes,no,on,off,enable,disable. Convert all relative paths passed to $USE, to full paths.
- DimOrder - push,pop,left,right,default - change the order of variables definition
int a,b,c,d ' the default is 'right', the 'd' variable is defined first.
Push/pop option value is to save/restore the current value. If you want to modify a option for a few lines in your code, use:
$option "name push" ' save current
$option "name new_value" ' modify
...
$option "name pop" ' restore
Numbers passed to $option can be formatted in decimal, or hexadecimal with 0x prefix.
$option "codepage 65001"
$option "codepage 0xFDE9"
Currently implemented warning filters:
- 0x0001 uninitialized variables
- 0x0002 unreferenced local variables
- 0x0004 missing return value
- 0x0008 size of local variables exceeded 32KB
- 0x0010 variable/constant name collision
- 0x0020 undeclared function
- 0x0040 temporary string/udt assigned to pointer
Special constants:
- __WARNINGFILTER__ returns the current warning filter
- __CODEPAGE__ returns the current codepage
- __FILE__ returns the current file path (a string)
- __LINE__ returns the current line number
- __IWVER__ returns a number with compiler version 0x02xxyyzz.
- __CASESENSITIVE__ - not a constant, but will be defined if case sensitive mode has been enabled
Strings:
- escape sequences \\, \", \t, \n, \octal, \xHEX. Where octal (number) can be up to 377 (377o=255), HEX up to FF.
Example: "\377" - one character string, same as "\xFF"
Example: "\400" - will generate two characters "\40" and "0", because 400o=256 (is greater than 255) - Operator & before "text" will return address of the string when assigning to non-string/pointer type
- Added multiplication operator: string*boolean - will return string if boolean is true, and an empty string if the boolean is false
int count = 2
print "found ", count, " item", "s"*(count<>1)
Unicode strings:
- escape sequences \\, \", \t, \n, \octal, \xHEX. Where octal (number) can be up to 177777, HEX up to FFFF.
Example: L"\"string\"\0\n\tstring\\\x0string". - UTF-8 encoded strings are supported up to 6 bytes per character.
- DBCS encoding is supported too (both encodings are supported in scintilla control: SCI_SETCODEPAGE (http://www.scintilla.org/ScintillaDoc.html#SCI_SETCODEPAGE).
- UTF-8 BOM marker (at the beginning of source file) is now detected, and has higher precedence than the codepage switch /c
- Operator & before L"text" will return address of the string when assigning to non-string/pointer type
- Multiplication operator - see Strings above.
String Literals:
- Added `expression` where expression can be up to 4 bytes (4 or less characters). `A` will return 65, the same as ASC("A") but without calling a command.
uint i = `abcd`
- Added L`expression` (unicode version with utf-8 support) where expression can be up to 4 bytes (2 or less characters). L'A' is equal to 65.
iwstring w[4]
w[0] = L`ä`
w[1] = L`ó`
w[2] = L`æ¦`
w[3] = L`\0` ' =0; =`\x0`; =L`\x0`
istring s[4]
s[0] = `A`,0
- Both literals, ansi and unicode, support escape sequences, where \r returns 13, and \n returns 10.
Preprocessor:
- $USE will search for libraries first in relative to current file path location, then in default locations.
- added $undef, for undefining conditions or constants (defined by $define)
- added $error and $warning directives
$ifndef UNICODE
$error "This file must be compiled with UNICODE condition"
$endif
- added $typedef as an alias for typedef.
- a space and/or tab (one or more) is allowed after the dollar character in $include, $ifdef, $ifndef, $else, $endif, $typedef.
- added $if expression. You can use the following operators: =, ==, <>, !=, +, -, *, /, %, |, or, || (xor), &, &&, and, <<, >>, !, ~, >, >=, <, <=
All the operators are handled also in CONST, ENUM and $DEFINE - added $elif, $elifdef, $elifndef
$if (VERSION > 0x0202) and (SUBVERSION <> 0)
...
$endif
$ifdef TRIAL_VERSION
$elifdef DEMO_VERSION
$else
$endif
Subroutines:
- Use STATIC keyword to define static variables
sub function()
static POINT pt
static int x
static def y as int
static dim z as int
- The CDECL modifier can be specified directly in the subroutine:
sub cdecl blah()
global sub cdecl blah()
Labels:
- Labels defined inside a subroutine are private to that subroutine, that is, you can now use the same label in multiple subroutines (eg. LABEL cleanup). It is no more possible to GOTO into subroutine, or GOTO from a subroutine. If you really want to jump in/out, use inline assembly to define a label and the jump. Internally all local labels have unique names.
label outside
'goto cleanup <- undefined, unless defined in global namespace
'goto subroutine1 <- error
sub subroutine1()
'goto outside <- undefined
goto cleanup
cleanup:
endsub
sub subroutine2()
'goto outside <- undefined
goto cleanup
cleanup:
endsub
Language:
- Names of variables, constants ($define, const, setid, enum), typedef, structures and subroutine (sub, export) can be of any size. Only when compiling in debug mode, subroutine names are truncated to 63 bytes (characters)
- Passing arrays without operator "&" to a function or method with unknown number of arguments behaves now correctly, the array will be passed by reference.
char array[8]
declare extern function(...)
function(array)
- VarArg operator (...) requires that at least one parameter is passes to a function or method.
- PRINT function has a short version: ?
? 1,2,3
- Variables can be imported
import __argc as int
import __wargv as pointer ' call __wgetmainargs to initialize
import __argv as pointer ' call __getmainargs to initialize
class CC
declare import CC()
declare import _CC()
declare import method()
endclass
New variable types:
- TSTRING - If UNICODE is defined, it will be WSTRING, else STRING
- ITSTRING - If UNICODE is defined, it will be IWSTRING, else ISTRING
Inline assembler:
- In case of an syntax error, the line number in Build window will be correct.
- added $emit, $asm, $endasm
$emit nop
Mixing Basic with Assembler:
- Added "DECLARE name AS type" for assember labels
type mytype
int a
int b
endtype
typedef DWORD uint
declare iVal as int
declare sVal as string
declare tVal as mytype ' TYPE/ENDTYPE
declare dVal as DWORD ' typedef
print iVal, iVal[1]
print sVal
print tVal.a, tVal.b
print tVal[1].a, tVal[1].b
print dVal
_asm
segment .data
iVal: dd 6, 5, 4, 3, 2, 1
sVal: db "hello", 0
tVal: dd 100,101, 200,201
dVal: dd 500
segment .text
_endasm
Constants:
- Added uq (unsigned quadword) modifier for decimal numbers: uint64 = 1234567890uq.
autodef = 1234567890uq
if (typeof(autodef) = @TYPEINT64)
print "type of 1234567890uq is INT64"
elseif (typeof(autodef) = @TYPEUINT64)
print "type of 1234567890uq is UINT64"
endif
int binary = /* optional zero's-> */ 00000000000000000000000000011000b ' Bin
uint ubinary = 00000000000000000000000000011000bu ' Bin Unsigned
int64 binary64 = 0100000000000000000000000000000010000000000000000000000000011000bq ' Bin Quadword
uint64 ubinary64 = 1000000000000000000000000000000010000000000000000000000000011000buq ' Bin Unsigned Quadword
- Enumerations can be created with operators:
enum myenum
minusone = -1
zero
one
two = one+1
six = 2+2*2
endenum
print minusone, zero, one, two, six
Operators:
- Added a?b:c - inlined version of IIF function from Windows headers. The advantage of this operator is that b or c will be executed depending on the A condition.
If A is true, and C is a function call, C will be not executed. Internally this operator generates IF A then return B else return C.
If A is known at compile time, B or C will be removed from the code. - Added ==, !=, && operators:
if (a == b) then equal()
if (a = b) then equal() ' still there
if (a != b) then notEqual()
if (a && b) then BothTrue()
if (!a && !b) then BothFalse()
- Added ~ operator (bitwise NOT), supported in constants and variables up to 32-bit: (s)char,(s)word,(u)int
SetWindowLong(hwnd, GWL_STYLE, styles & ~WS_VISIBLE) ' hide window
- The following operators can be used to compare UDT's: =, ==, <>, !=, <, >, <=, >=
- Added [pre|post][inc|dec]rement operators: a=b++ : a=b-- : b=--b : a=++b
Code generation:
- Expressions with the following operators will be evaluated at compile-time, (when possibe):
+, -, *, /, %, and, &, or, |, ^ (aka XOR), <<, >>, = (or ==)
The following example code will initialize the variable with precomputed value 0x2000F instead generating code to calculate it:
int a = 1+2|4|8|@MINBOX
Supported contant types: numbers (decimal, hex, signed/unsigned, 32/64 bit), constants defined by CONST, SETID, $define
Intrinsic functions:
- The compiler supports the following intrinsic functions: sin,sind,fsin,fsind,cos,cosd,fcos,fcosd,sqrt,tan,ftan,tand,ftand,log,log10,abs (and more in the future).
- DEFINE_GUID for global and local variables has intrinsic form too, but only if you supply all the guid members as numbers.
Globally defined GUID is successfully initialized with intrinsic DEFINE_GUID, will be allocated as raw data, without any code. - RGB function if used with numbers, will be replaced with precomputed value.
Loops:
- Char and word (signed or unsigned) can be passed to WHILE stateent
- Added BREAK keyword, to terminate the execution of the nearest enclosing loop (FOR-NEXT, DO-UNTIL, WHILE-ENDWHILE)
Other:
- The temporary $$$out.a file will be automativally deleted.
- The temporary $$$lib.link file will be created in current directory.
- If an error occurs while procssing .incc file, the correct file name will be reported (instead .eba)
- Invalid assignment, bad type errors will be more descriptive: unable to convert TYPE to TYPE, cannot assign TYPE to TYPE...
I am thinking to add a $pragma directive to the compiler. It will make possible to set the command-line options directly from your source code.
Examples:
$pragma compiler "include=..\\zlib" ' additional include search path, relative to current file path.
$pragma compiler "include=$(ProjectDir)\\..\\zlib" ' additional include search path, relative to project path.
$pragma pack "push{,number}|pop|default|number" ' change default structure packing
$pragma warning "disable=1234" ' disable warning 1234
$pragma compiler "codepage=65001" ' use utf-8 codepage for unicode strings
$pragma linker "libs=..\\zlib\\lib" ' add library search path, relative to current file path.
EDIT: The $OPTION keyword has been extended, so there is no need to add a new $PRAGMA keyword.
That would be great Sapero.
If i may say something abut backslash using.
Is it not enugh one backslash not two?
You can use single backslashes, but only if the next character is not one of 0,1,2,3,4,5,6,7,x,\,t,n," and optionally r,` characters inside character constants.
If you invite me for a beer, I'll add a option to disable escape sequenes ;)
If it were me I'd leave it as \\.
If you add an option to disable it then you need to make sure it doesn't break existing code that is using \\.
LarryMc
Ok guys i understand, i just ask..
Or, you could do something similar to C#:
String myVar = "c:\\directory\\something";
Sting myVar = @"c:\directory\something";
Of course, in the last option you cannot include escape sequences like "\n\r".
I am now working on optimized operations on constants.
int a = 1 + 2
This simple program currently will generate code to add 1 and 2, but in the future it will evaluate the expression at compile time.
I have already a working idea, the implementation can handle basic operators like +, -, *, /, %, and, &, or, |, ^ (known as xor), <<, >>. It handles integers and floating point constants, with exception for shifts.
Done, ID's are now recognized and evaluated, if possible.
There is also POWER operator. A simple "2^8" expression could be evaluated too.
I think to evaluate only integers, because the raw floating point result ("1.2^3.4") can be controlled by the end-user (FPU precision), and the user should have full control over it.
Shifts added for 32 and 64 bit integers. Any idea how to handle floats?
1. do nothing, forward to default handling // current
2. convert to int or int64, shift, convert back
3. shift the raw float/double bits, keep sign bit (or shift it too)
4. separately shift mantissa and exponent
Honestly I don’t have any real life mock up why to shift a floating number at all :P
but may someone can come up with some reason.
Just leave this question open for feedback for a while.
Otherwise I would vote for 3 (shift it too) but I can’t tell you why just sounds logical. ;D
Added missing interfacing between assembler raw data and Basic variables:
declare rectangles as WINRECT
'unsupported yet: declare name[2] as type
'unsupported yet: declare name1,name2 as type
print rectangles.left, rectangles.top, rectangles.right, rectangles.bottom
print rectangles[1].left
_asm
segment .data
rectangles: dd 1,2,10,11 ; rectangles[0]
dd 3,4,30,31 ; rectangles[1]
segment .text
_endasm
This addition is planned:
declare rectangles as WINRECT = 1,2,10,11 {, next rectangle coordinates}
I like that.
I could have really used this on the Visual Designer!!!
declare rectangles as WINRECT = 1,2,10,11 {, next rectangle coordinates}
^
||
Is it your intention to make a space a delimiter in the parameter list?
Just curious.
LarryMc
I used the same syntax as Paul in his help file: function(required, required {, optional {, optional}}).
I don't remember where I saw it. Spaces are optional, only comma character is the delimiter.
declare rectangles as WINRECT=1,2,3,4,5,6,7,8
My bad!
You had it exactly right. I just misread it.
Senior moment!!! Sanctuary! Sanctuary! ;D
LarryMc
I like that feature very much as well! :)
I am working now on basic optimizations for function/method calls. If you pass a constant number, the value will be pushed on the stack without storing it first in register(s). Because we have floats and doubles, and you are not restricted to pass a float where a float should be. The compiler will convert types silently, if possible:
INT/UINT to int,uint,int64,uint64,float,double,comref,pointer,string,wstring
INT64/UINT64 to int,uint,int64,uint64,float,double
...
Example int->float:
declare go(float f)
go(1)
Integer 1 will be converted to 32-bit float value (0x3F800000) using processor specific FPU commands. Because there can happen data loss or rounding, you should be able to control how the processor should convert between integers and floats. I think I'll add additional command line switch (/F - from Float), where you just pass the "FCW" - Floating point Control Word, providing custom precision (24,53,64 bits) and one from four rounding modes.
It can be a number (0-15, 2bits:precision, 2bits:rounding), or a string: /Fnearest|up|down|toward,24|53|64.
Or should I just keep it as is, using some default settings from operating system, and then you could control the FPU mode by saving all constants in variables (to hide it from the optimization)? What do you think?
By the way, the first topic has been updated - Labels.
IWBasic 2 is now able to evaluate expressions in a new conditional keyword $IF:
$ifndef MYVERSION
$warning "MYVERSION was not defined, assuming default value"
$define MYVERSION 0
$endif
$if (1=2) or (MYVERSION < 1)
$error "i'm sorry, but something here is wrong"
$endif
Unicode UTF8 file marker (BOM) is now handled for each parsed command/source/include file.
The CONST, $DEFINE and $IF keyword can handle more operators: =, ==, <>, !=, +, -, *, %, /, |, or, || (xor), &, &&, and, <<, >>, !, ~, >, >=, <, <=
Enum has been extended:
enum myenum
one
two = one+1 ' including all operators
three
minusone = -1
endenum
Meant to ask about this earlier:
$ifndef MYVERSION
$warning "MYVERSION was not defined, assuming default value"
$define MYVERSION 0
$endif
$if (1=2) or (MYVERSION < 1)
$error "i'm sorry, but something here is wrong"
$endif
What do the $warning and $error lines actually do?
LarryMc
$warning and $error will display a string in Build window, in the IDE (stderror file stream).
$warning does nothing more, $error terminates compilation.
Sometimes you need to stop the compilation if some conditions are invalid, for example outdated header:
$include "designer.inc"
$if designer_header_version < 2
$error "this example requires an updated version (2) of designer.inc"
Makes perfectly good sense.
Thanks for the explanation.
LarryMc
For today, we have in addition:
- Extended $OPTION keyword (to set or change selected command line options)
- Four special constants __WARNINGFILTER__, __CODEPAGE__, __FILE__, __LINE__, __IWVER__
__FILE__ and __LINE__ macros you will probably love when compiling in debug mode, to display 'professional' info to your debug window.
print "current error filter: 0x", hex$(__WARNINGFILTER__)
print "current codepage: ", __CODEPAGE__
print "current file: ", __FILE__
print "current line number: ", __LINE__ :
print "compiler version: 0x", hex$(__IWVER__)
$option "warning push" ' optional: save current mask
$option "warning disable:0001"
sub mysub(int pValue)
int t
mysub(t)
endsub
$option "warning pop" ' optional: restore saved mask
$option "/p 1"
sub mysub3(int t)
_asm
mov eax,[t]
_endasm
endsub
$option "/p 0" ' optional: turn it off
I have planned to add new $PRAGMA keyword, but if we already have $OPTION, then why not to extend it?
The $USE directive has now additional feature: it will first search for the library in location(s) relative to current file path. If it finds one, the full path at compile time will be passed to linker, else the library will be accessed in defaut way.
Now if you want to share source code with .lib file, just put the library in the same directory where is the source or include file with $use directive. There is no need to copy it to installdir/libs.
$use "mylib.lib" ' if exists CurrentFileDir\mylib.lib - use it, else do default
$use "libraries\\mylib.lib" ' if exists CurrentFileDir\libraries\mylib.lib - use it, else do default
$use "..\\libraries\\mylib.lib" ' if exists CurrentFileParentDir\libraries\mylib.lib - use it, else do default
Imported variables:
You can now import variables form DLL's:import __argc as int
import __wargv as pointer ' call __wgetmainargs to initialize
import __argv as pointer ' call __getmainargs to initialize
$use "msvcrt.lib"
declare cdecl import, __getmainargs(pointer argc, pointer ppargv, pointer ppenv, int expand_wildcards, pointer new_mode)
declare cdecl import, __wgetmainargs(pointer argc, pointer ppwargv, pointer ppwenv, int expand_wildcards, pointer new_mode)
int argc, new_mode, a
pointer pargv, penv
pointer pwargv, pwenv
' initialize __argc and __argv
__getmainargs(&argc, &pargv, &penv, 0, &new_mode)
' initialize __argc and __wargv
__wgetmainargs(&argc, &pwargv, &pwenv, 0, &new_mode)
print "you passed ", __argc-1, "arguments"
for a=0 to __argc-1
print " arg ", a, "= ",*<string>(*<pointer>__argv[a])
print "warg ", a, "= ",*<wstring>(*<pointer>__wargv[a])
next a
Compile as varImport.exe (console target), open command prompt and type: varImport 123 abc "z:\program files"
Imported methods
You can now import methods from DLL's:
classdef.incclass CExporter
declare import CExporter()
declare import _CExporter()
declare import TestMethod()
endclass
DllMain.eba (dll code, compile and create import library)$include "classdef.inc"
export CExporter@CExporter
export CExporter@_CExporter
export CExporter@TestMethod
sub CExporter::CExporter()
print "CExporter constructor called"
endsub
sub CExporter::_CExporter()
print "CExporter destructor called"
endsub
sub CExporter::TestMethod()
print "CExporter TestMethod called"
endsub
WinMain.eba (exe code)$include "classdef.inc"
$use "DllMain.lib"
CExporter c
c.TestMethod()
-------------------------------
June, 7th
The compiler supports intrinsic functions, currently hardcoded in the compiler. Maybe in the future the compiler will read instructions from special library, or from binary file.
Programs that use intrinsic functions are faster because they do not have the overhead of function calls, but may be larger because of the additional code created.
DEFINE_GUID has also intrinsic form, for global and local variables:
GUID _IID_ICustomGuid
DEFINE_GUID(_IID_ICustomGuid, 1, 2,3, 4,5,6,7,8,9,10,12) ' no function call, no code generation
blah()
sub blah()
GUID g
DEFINE_GUID(g, 1, 2,3, 4,5,6,7,8,9,10,11) ' only 4 simple assembly instructions
if (g==_IID_ICustomGuid) ' you can compare two UDT's of the same type
print "equal"
else
print "not equal"
endif
endsub
--------------
June, 17
Added pre-incrementation, post-incrementation, pre-decrementation, post-decrementation operators:
variable++
variable--
++variable
--variable
All the expressions are returning a value, which can be used directly in another expression:
Print numbers 3,2,1,0
int v = 4
while (v--) ' or --v
print v
endwhile
int x = 4
print x++ ' will print 4, and x will be set to 5. Return current value, then modify
print ++x ' will print 6, and x will be set to 6; First modify, then return new value
Note: when used as function argument, the execution flows from right to left:
print x++,x++ ' will print x+1, x
-------------
Added operator & for strings and wstrings - will be usefull when passing an string as LPARAM in SendMessageA/W
int address = &"text"
address = &L"text"
SendMessageA(d1.hwnd, WM_SETTEXT, 0, &"caption")
Added BREAK keyword, to terminate the execution of the nearest enclosing loop (FOR-NEXT, DO-UNTIL, WHILE-ENDWHILE)
WHILE (ShouldEnterThisBlock)
if (!InitializeDevice()) then break
if (!SetDeviceParams()) then break
for a=0 to 4
if (a=1) then break ' quit from for. BREAK inside FOR will internally use BREAKFOR.
next a
break
endwhile
Cosmetic change: (s)char and (s)word can be passed to WHILE condition.
char c = 2
while (c)
print c
Added typecasting: word() schar() ... double(). Currently only casting to double is implemented. This is very usefull when you are using CRT string routines like sprintf, and need to output a float:
sprintf(buffer, "%f", double(VariableOrConst))
Optimizations
I have added some optimizations to code generation - when calling a subroutine/method, assigning a value, and in other places. This is in experimental phase, every day something is changed, and by default is disabled. To enable optimizations, put the line below, above the code you want to be optimized:$option "optimization=on"
I have also added some optimizations to the 'unoptimizable' code parts - removed operand size specifier in assembly instructions like "mov reg,value" or push value. If you want to use this optimization, add the following option to additional assembler options (in project options dialog): -O1
. The assembler will find "push 0", and seeing -O1 option, will generate 2 instead 5 bytes of code for the push instruction. Previously the PUSH instruction included operand size specifier "push dword 0", and nasm was disallowed from optimizing it.
June, 19
Optimized DELETE, WHILE and UNTIL.
"WHILE number" and "UNTIL number" will not compare, if "number" is known at compile time.
do
print
until 1
This example will generate code only for PRINT, as if do-until was not there.
Added T modifier for strings: T"". If you write unicode+ansi friendly programs, use T"string" to generate a string or wstring, based on the current UNICODE condition. If the condition is defined, T"" will generate unicode string, else an ansi string.
$ifdef UNICODE
declare import, testme alias testmeW(wstring p)
$else
declare import, testme alias testmeA(string p)
$endif
testme(T"hello")
Added two new variable types: TSTRING and ITSTRING
TSTRING = T"hello"
$ifdef UNICODE
' the first line was WSTRING = L"hello"
$else
' the first line was STRING = "hello"
$endif
Hi, Sapero,
Is there room for a REDIM command, or is that already in your plans?
Brian
Hmm, redimensioning static arrays is possible, but only when the new total size is not greater than the original allocation took.
int a[256]
redim a[4] ' possible
redim a[16,16] ' possible
redim a[512] ' not possible
It would be possible with pointers:
pointer p = 0
redim p[100] ' 100 bytes
redim p[@TYPEINT,100,100] ' integer matrix 100*100
redim p[0] ' delete
Hi, Sapero, thanks for the answer
Well, I suppose you could always DIM with a silly number, and then REDIM to the
required amount. Will that release the memory DIM'med in the first place?
Brian
Yes and not, it depends on the implementation, type of the variable, and where it is allocated.
If this will be a global variable, redim will not free the unused space, because the next global variable will be allocated always after the current (fixed location). Only enabled memory-swapping will free the memory, or better - will not allocate it in RAM until it is not touched (read/write).
The same allocation applies for local arrays in a subroutine - the location of next variable is always after current variable, so even if you redim the array to 0, the space you initially declared will be reserved, but not freed.
It would be possible to allocate the array from stack - search for alloca function. Stack memory is automatically released when returning from a subroutine. The alloca function can be easily inlined, it modifies only the ESP register:
sub esp,memory_size
And the ESP value after subtracting points to allocated memory.
To free it, we need to add memory_size to ESP register. There is no problem if you have only one dynamic array. For two or more arrays, you may have a problem relocating the first array, because the current ESP is pointing to the last allocated array. It would require relocation of all the allocated arrays, probably invalidating dynamic array pointers copied to other parts in your program.
If you understand this, you'll see that using NEW/Delete is much easier
For dynamic arrays attached to a pointer variable (the second example in my previous response), there is no problem with REDIM because internally the code will call new/delete/realloc without taking care for other arrays. And if we are here, then REDIM is already useless, we have new/delete, only realloc is missing (easy to add).
Note: alloca function will not work with our current compiler if your saving the returned value in local variable. The pointer should be returned by reference:
declare myalloca(int size, pointer ppv)
sub test_alloca()
pointer array1, array2
myalloca(256, &array1) ' be sure to allocate 4*N bytes
myalloca(512, &array2)
' array1 is at array2+512
' total allocated size: 256+512
*<string>array1 = "hello"
*<string>array2 = "world"
' relocate array2 to 256 bytes (array1 cannot be relocated directly, because array2 is still allocated. If you need to relocate array1, just modify the pointer)
myalloca(-512/*old size*/+256/*new size*/, &array2)
' total allocated size: 256+512-512+256 = 256+256
' -- optional part --
' free both arrays
myalloca(-256, &array2)
myalloca(-256, &array1)
' or with single shoot: myalloca(-(256+256), &array1)
endsub
_asm
align 4
myalloca:
pop edx ; return address
pop ecx ; ppv
pop eax ; size
sub esp,eax ; allocate memory on the stack
mov [ecx],esp ; *ppv=esp
jmp edx ; return
_endasm
Wow, that answer was like asking to read a Superman comic, and being given
War and Peace instead!
But from your explanation, I can see the logic in opting for dynamic arrays
attached to a pointer variable. As you point out, we can already do that
Thanks for the detailed info,
Brian
I have added $asm, $endasm, and $emit keywords, for inline assembly gurus ;)
There is additional $option "return 0|1|yes|no|on|off|enable|disable" if you want to return a value directly in eax register. If you turn this option ON, the compiler will not warn about missing return value, and will not return zero, as it does by default.
$option "return disable"
sub testsub(),int
$emit mov eax,7
return ' return 7
$emit mov eax,6
endsub ' return 6
$option "return enable"
print testsub()
I am now working on a bug - where previously defined constant is used as function's argument name and a select-case statement. The compiler was crashing (while Ebasic 1735 didn't):
$Include "windowssdk.inc"
Sub ShowErrors(Error as Int)
Select Error
Case 1
Error is defined as$define ERROR 0 ' wingdi.inc
Currently the compiler will throw "Name/constant conflict: Error" pointing to the line with "SUB" (instead crashing), and the CASE statement will show another error "invalid argument for comparision" (because the parameters passed to internal compiler function are invalid).
What I have planned:
1. Option for case sensitive names - Error and ERROR will be two different things.
2. Functions profiling (usage count and execution time) in debug mode. Will help you to optimize the code, you will know which function is too slow.
Timing and hit-count can be implemented before you actually call a function, and/or at the beginning/return of/from a function.
EDIT: OK, I have found the bug - it was caused by my optimizations, where constant names were replaced with values, without checking first if a variable with the same name is available.
My personal opinion is that having things case sensitive, even as an option, will cause more grief than benefit.
I have always seen things in IWBasic NOT being case sensitive as a real plus for the language.
And what happens when people share code with one not opting for case sensitive (like me) and the other person's code uses case sensitive and has a bunch of error/ERROR coding?
LarryMc
Nothing, it will just compile, until you keep the code as is, or follow the rules defined by the author.
Please note that the assembler and linker are case sensitive.
EXPORT MySub
SUB mySub
or' module 1
global MySub
' module 2
extern mysub
This will compile but not link, because the linker will fail to find MySub (export) or mysub (extern).
If case sensibility will be enabled, there will be no problem having a constant ERROR and a variable error (or even another mix like eRror). If you need it, just use $option "casesensitive=no", and this will apply only to current module.
When defining external variables and functions, you are forced to use the correct case independant from this option, because the linker is case sensitive, and each symbol must exactly match.
Now that you mentioned it I remember the linker being case sensitive. The first time I ran into that it took me a while to figure out what my problem was.
Thanks for refreshing my memory.
LarryMc
We can already add strings. What would you say if we could multiply them, like in Sinclair Basic (ZX Spectrum) ?
Example: create "1 item" or "2 items"
int items = ...
string s = str$(items) + " item" + ("s" * (items>0))
The operator would be string*boolean, returning string if boolean is true, or an empty string if boolean is false.
int boolean = true ' zero or anything
string booleanString = "true" * boolean + "false" * !boolean
I think that would be a nice little feature.
LarryMc
I have added inlined version of IIF function: a?b:c. Note - the colon is not used as a separator for new instruction.
int boolean = true
print boolean ? "true" : "false"
As b,c you can pass any type of data, but the types should be equal. Mixing INT and FLOAT, or FLOAT+DOUBLE, or any other "bad" combination is allowed, but it must be handled by the function you are passing the value. The type of b is returned independing of the type of c.
Mixing types of different size is not allowed - for example integer + double returned from a function:
print boolean ? 1 : abs(5)
Optimizations progress: size of code generated by the new compiler is 21% smaller than current official compiler generates. Compared with a 2050 lines image parsing program.
Bugfixes: fixed conversion from uint to double, and uint64 to double.
Added option of case sensitive compilation:
$option "casesensitive"
After enabling it, __CASESENSITIVE__ preprocessor condition will be defined. Use $ifdef to detect it:
typedef byte schar
$ifdef __CASESENSITIVE__
typedef BYTE char
$endif
To use case sensitive compilation mode with windowssdk.inc, you will need to update the headers, because at least 7 files require small modification (additional typedef for BYTE) - wchar.inc, stdio.inc, stdlib.inc, SNADOS.inc, ntdef.inc, intsafe.inc, basetsd.inc. This update will include only the $ifdef part form the example above.
Case sensitive compilation mode does not affect build-in variable types, so char and CHAR is the same type.
---
Added static keyword, used to create a static variables in a subroutine which does not lose it's contents, and acts much like a global variable.
QuoteAdded option of case sensitive compilation
I'll show my ignorance :-[ and ask:
Why would I need this? or How does this benefit me as a user? ???
LarryMc
This is a feature, a preference. One likes it, one hates it. Here you can choose to use it or not.
Case sensitivity allows faster compilation because the compiler doesn't have to alter the case of everything in order to make comparisons.
Benefits?
* safely mix constants and variables with the same name (ERROR+error).
* "change" the value of a constant by defining it again, with different case.
* when using SI (International System of Units) derieved names, it will be possible to define F for faraday and f for gravity.
Look from the other side - someone is preffering case sensitivity, and is looking for Basic compiler...
Thanks for the explanation.
Now, is there a way that a novice (or senile, in my case) programmer get into trouble selecting the option?
I only ask because one of the beauties of IWBasic is its simplicity in using.
That's not totally true. I'm also asking because all of these neat new things you are adding will ultimately have to be fully documented for users. And although your posts are always very precise and better than most of us who have English as our primary and only language, I know the pains you take in making your post.
Larry or me or someone else will have to put all of this in terms that a novice user can understand in the documentation.
LarryMc
Not much, just a few "undefined variable" errors.
WINDOW w
w.hwnd = 7 ' error, hwnd is undefined. Should be hWnd
string s = left$("abc", 1) ' error, should be LEFT$
I'll add a hint to the error message: undefined variable hwnd. Did you mean hWnd? (added).
Sounds good to me!
thanks for having patience with me, as you always do.
LarryMc
QuoteAdded static keyword
:)
Added $ELIF directive:
$if version==1
... something
$elif version==2
... something
$endif
All the $IF directives are now allowed inside TYPE-ENDTYPE.
Structure definition can be now nested:
TYPE MyNestType,1
int t
TYPE pt
int x,y
ENDTYPE
ENDTYPE
MyNestType var
print var.t, var.pt.x
Nesting level is unlimited.
Now working with UNION... done. A nested union can have a name:
TYPE MYDATA
MYENUM nType
UNION u ' optional name
TYPE personal
string name ' mydata.u.personal.name
string surname
ENDTYPE
ENDUNION
ENDTYPE
Now those are some awesome enhancements.!!!
LarryMc
Quote from: Larry McCaughn on July 01, 2010, 07:34:33 AM
Now those are some awesome enhancements.!!!
LarryMc
Yep, keep going! ;D :D
Hello Sapero !
Very good to see your enhancements. IW basic deserve that !
Do you need some beta testers ?
As to the case sensitivity, will there be a list of which commands and special fields with their exact spelling? (IE, hWnd and others like it).
Thanks,
Bill
Bill, if you enable case sensitive compilation, anything but build-in base types (and the list below) will be case sensitive - functions, constants, id's, variables, structures, typedefs.
The following will be case insensitive:
def, dim, $error, $warning, typedef, $typedef, $undef, gosub, goto, jump, label, ... the list is very big, it includes all the keywords defined as {command} in the .incc files.
Zaphod, this is up to Larry (Rock Ridge Farm), he is the owner. We have no beta testers yet.
I was thinking more in the line of w1.hWnd and other things that are already predefined.
We do:
WINDOW w1
under that w1.hWnd is valid, but w1.hwnd is not. That is the list I was wondering about, since we have no control over those field names.
Thanks,
Bill
The compiler will search in all possible locations (using case insensitive method) to give you a hint, with the proper case: "did you meant TheSymbol ?"
sub b()
point p
endsub
Error message: File: x.eba (19) unknown type point, did you meant POINT ?
Quote from: sapero on July 01, 2010, 01:11:58 PM
to give you a hint with the proper case: "did you mean hWnd ?"
And propably you have some enchanced Intellisense for the enchanced compiler in your mind as well ? ;D ;)
The compiler could generate intellisense database for use in the IDE, why not?
New additions:
* variable = new type
* variable = new type[count]
* typedef newtype structureName
* typedef newtype className
* typedef newtype interfaceName
* interface name, base_interface interface IMyInterface, IUnknown ... endinterface
* LEN(className)
If A in "typedef A B" is already defined as something other than B, the compiler will show an error:
typedef MYPOINT POINT
typedef MYPOINT POINTL ' Error: MYPOINT already defined as struct POINT
Added NOVTABLE option for classes:
$option "novtable on" ' yes, enable, on, 1
class CClass
int m_value
endclass
$option "novtable off" ' no, disable, off, 0
I am planning to add support for classes with the base on a structure, so novtable will be a great feature:
class CRect, WINRECT
"declare virtual" will be automatically disabled while novtable option is active, because a class without virtual functions table cannot have virtual methods.
OK, time for me to be the 'village idiot' again.
What does the NOVTABLE option do for me as a regular IWBasic user?
How does it benefit me and/or why would I want to use it?
LarryMc
It is used when you want to create a class compatible with a structure, where size of the class must be eual to size of structure.
It does not shift all class variables by 4 bytes forward.
Currently if you build a class compatible with WINRECT, and will access Left member, you need to cast the 'this' pointer to WINRECT
class CWinRect ' compatible with WINRECT
' pointer _VTABLE_ ' hidden class member at offset +0
' int left -> not possible, we are at offset +4 where is WINRECT.top
int top
int right
int bottom
declare GetLeft(),int
declare GetTop(),int
endclass
sub CWinRect::GetLeft(),int
return*<WINRECT>this.left ' same as *<int>this
endsub
sub CWinRect::GetTop(),int
return top ' or return*<WINRECT>this.top
endsub
When you turn off vftable, the first variable in our class will be at offset +0, as it is in regular structure:
$option "novtable on"
class CWinRect
int left ' -> possible, we are at offset +0
int top
int right
int bottom
endclass
Or without $option:
class CWinRect, WINRECT
endclass
In Aurora you could define a class with the same name as previously defined structure, but still to access structure members in a method of your class, you needed to cast 'this' pointer.
class RECT
{
declare width(),int {return *(RECT)this.right - *(RECT)this.left;}
}
So, to make it easier and remove the casting, use novtable option, and access all members directly.
Example program:
class CWinRect
int left
int top
int right
int bottom
endclass
CWinRect c
c.left = 1
c.top = 2
c.right = 3
c.bottom = 4
pointer p = &c
print "values:", *<int>p[0], *<int>p[1], *<int>p[2], *<int>p[3]
print "offsets: ", &c.left-&c, &c.top-&c, &c.right-&c, &c.bottom-&c
Output:
values:4206666 1 2 3
offsets: 4 8 12 16
With vtable disabled:
values: 1 2 3 4
offsets: 0 4 8 12
So, would the default would be "novtable off" ?
LarryMc
Yes, vtable by default will be enabled (novtable off).
The new compiler must be compatible with previous versions, so any changes will be enabled only at user request.
Additional tools like NEW without braces, or derieved interfaces, which do not interfere with the official syntax, are available at any time.
---
Improved error tracking:
* duplicate declaration is now detected better than proviously, and the (clickable) location of original definition will be shown
File: new.eba (8) Error: duplicate definition of variable or label: bottom
File: new.eba (7) See previous declaration
* whenever possible, the word (variable/function/class/method/typedef) name will be displayed, if it causes an error. Example: duplicate definition/class/interface.
With the new improvement, I have found two duplicates in rpc includes: a symbol was defined as pointer in file A, and as structure in file B.
Added structured exceptions handling:
$include "windowssdk.inc" ' need EXCEPTION_POINTERS and EXCEPTION_RECORD
int a=0
try
print "dividing by zero"
print 1/a
print "divide ok"
endtry
catch
print "exception code: 0x", hex$(GetExceptionCode())
pointer pep = GetExceptionInformation()
settype pep,EXCEPTION_POINTERS
print "exception address: 0x", hex$(*pep.*<EXCEPTION_RECORD>ExceptionRecord.ExceptionAddress)
endcatch
Output:dividing by zero
exception code : 0xC0000094
exception address: 0x40141E
finished
LEAVE will immediatelly quit from try-endtry block.
THROW requires an argument (a pointer, number, string) - generates in-place exception with code=0, passing the pointer to use in the next catch block:
try
throw "unable to divide by zero"
endtry
catch
pointer pep = GetExceptionInformation()
settype pep,EXCEPTION_POINTERS
settype *pep.ExceptionRecord, EXCEPTION_RECORD
' optional validation
' *pep.*ExceptionRecord.ExceptionCode must be 0
' *pep.*ExceptionRecord.NumberParameters must be >0
if (!*pep.*ExceptionRecord.ExceptionCode and *pep.*ExceptionRecord.NumberParameters)
' get the first integer from ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]
int pText = *pep.*ExceptionRecord.ExceptionInformation[0]
'print hex$(&"unable to divide by zero")
'print hex$(pText)
print *<string>pText
endif
endcatch
Some internals for advanced users:
Pointer to EXCEPTION_POINTERS structure is stored at ThreadEnvironmentBlock->UserReserved[0]. It will be allocated once, when handling the first exception.
The format of data stored in TEB->UserReserved[0] is:
type SEH_TLS ' custom structure
EXCEPTION_POINTERS ep ' to make GetExceptionInformation compatible with MSDN
EXCEPTION_RECORD er
CONTEXT ctx
BOOL HadException ' if TRUE, next CATCH will execute
endtype
GetExceptionInformation will return a direct pointer to this structure, initialized to the snapshot of thread state, when an exception occured.
Because EXCEPTION_POINTERS is the first member, GetExceptionInformation can be used to gain access to EXCEPTION_POINTERS and SEH_TLS.
When entering TRY block, the HadException member will be set to FALSE, if the SEH_TLS structure has been allocated.
When entering CATCH block, there will be a check if HadException is TRUE. If SEH_TLS is not allocated (no exception) or when HadException is FALSE, execution will continue at ENDCATCH.
GetExceptionCode and GetExceptionInformation are intrinsic, so there is no function call, just inline code. GetExceptionInformation can return NULL, if there was no exception previously, but inside catch-endcatch it will always return a valid pointer.
Nesting level for TRY block is unlimited, each level takes 44 bytes from stack memory to save important CPU registers, and the code address of ENDTRY, where a jump is made on exception.
TODO:
* handle goto inside try-endtry.
sub function1()
try
try
goto ' todo
break ' handled
breakfor ' handled
return ' handled
end ' handled
endtry
endtry
endsub
Added construction and destruction for classes defined in a structure (any level)
class myclass
declare myclass()
declare _myclass()
endclass
type child
myclass c
endtype
type mytype
myclass a
child b
endtype
mytype t
end
sub myclass::myclass()
print "constructor"
endsub
sub myclass::_myclass()
print "destructor"
endsub
Output:
constructor
constructor
destructor
destructor
EDIT: the same applied to new and delete.
There are the latest updates:
* New "strict"option - the compilerwill be showing all possible typos. For example, if you use undefined variable type in function declaration, you will be notified about.
declare mysub(MYTYPE p) ' use of undefined type
typedef MYTYPE int ' declaration
* ebstd.incc header will be parsed first, then all other incc files, using the default sorting algo from FindNextFile.
* Added additional location for include-directory list: INCLUDE environment variable. The search order for searching files accessed by $INCLUDE is:
1. The directory of currently parsed file
2. The Include directory
3. User directory list added in the /i command lines witch or "IncludePath" option
4. The Bin directory
5. The list from INCLUDE environment variable. The format is same as in PATH variable: "c:\dir1;d:\dir2;e:\dir2\dir 5; ... z:\last dir".
IDE updates:
* Clicking a keyword with CONTROL key pressed with select the whole word.
* Added fold points to do-until, for-next, while-endwhile, if-else-endif, select-endselect.
There is an additional tool for configuring per user global (default) settings. You can choose which options should be ON/OFF: case sensitive compilation, default state for autodefine, state for strict option, ..., additional preprocessor definitions and additional include directories.
This is a small property sheet program.
Looking good, Sapero - I will certainly be ordering the new version
when it surfaces
Can't be left behind...
Brian
Hopefully I will be getting my new credit card just in time to purchase v2.0 :) It's looking very nice. I also hope that one day (sooner rather than later) we'll also have a 64-bit version, too.
Barney
Quote from: sapero on July 03, 2010, 06:12:23 PM
Yes, vtable by default will be enabled (novtable off).
The new compiler must be compatible with previous versions, so any changes will be enabled only at user request.
Additional tools like NEW without braces, or derieved interfaces, which do not interfere with the official syntax, are available at any time.
---
Improved error tracking:
* duplicate declaration is now detected better than proviously, and the (clickable) location of original definition will be shown
File: new.eba (8) Error: duplicate definition of variable or label: bottom
File: new.eba (7) See previous declaration
* whenever possible, the word (variable/function/class/method/typedef) name will be displayed, if it causes an error. Example: duplicate definition/class/interface.
With the new improvement, I have found two duplicates in rpc includes: a symbol was defined as pointer in file A, and as structure in file B.
When you get this error
File: new.eba (8) Error: duplicate definition of variable or label: bottom
File: new.eba (7) See previous declaration
the See Previous line # is one greater then the actual line.
LarryMc
Thanks for this info!
Fixed.
I have added two special return types: FPU and __m128. The names are temporary (I am open for suggesions).
The FPU type marks a function as returning a float/double in the ST0 FPU register (for Bass users).
type FPU
endtype
declare GetFpu(),FPU
print GetFpu()
end
$asm
align 4
GetFpu:
fld qword [mydata]
ret
segment .data
mydata:
dq 1.23456789
$endasm
The __m128 type marks a function as returning an array of 4 floats or two doubles in XMM0 register (SSE users). Because the data is 128-bit, it is copied to temporary heap memory, in the same way as you would return a structure or string.
union __m128
float m128_f32[4]
float f32[4]
double d64[2]
endunion
declare get_xmm_value(),__m128
__m128 v = get_xmm_value()
print v.m128_f32[0], v.f32[1], v.f32[2], v.f32[3]
end
$asm
align 4
get_xmm_value:
movups xmm0,[myvalue]
ret
segment .data
align 16
myvalue:
dd 1.0, 2.0, 3.0, 4.0
$endasm
I forgot to say in my previous reply, that console programs, when running from the IDE will be executed in the default command processor window, and the window will be paused when your program terminates. You don't need to wait for a key press before terminating.
Awesome ! :D
Sapero
This is why my INI file code was broke and not due to you new headers.
The IF statement in test one is what was in my existing code and worked fine in EB/IWB (ver prior to last).
It doesn't work in the current version.
test two works with all versions.
LarryMc
openconsole
string tp=getstartpath()+ "test.cfg"
string section= "position"
string key= "left"
print "start"
print
print "Len(tp) ",Len(tp)
print "Len(section) ",Len(section)
print "Len(key) ",Len(key)
print
print "test one"
If ((Len(tp) > 0) & (Len(section) > 0) & (Len(key) > 0))
print "success"
else
print "failure"
EndIf
print
print "test two"
If (tp<>"" & section<>"" & key<>"")
print "success"
else
print "failure"
EndIf
print
print "finished"
waitcon
closeconsole
end
Larry, your program displays twice Success. There was a bug in the "compare" code part (optimizations), but I have fixed it a week ago. You must still have the buggy parser.
int a=1, b=1
if ((a+1)>b) then action() ' BUG: never true
I downloaded the newest installer I could find (dated 11/23/10) and it has that bug in it.
Reckon LarryS hasn't gotten around to a newer installer with your latest.
So, to keep going forward with the Visual Designer 2.0 I just changed the comparisons to <>"".
Thanks Sapero
LarryMc
Added automatic string conversion (from/to unicode) when returning from a function:
$define UNICODE
$define WIN32_LEAN_AND_MEAN
$include "windowssdk.inc"
MessageBoxW(0, function1(), L"ansi->unicode")
MessageBoxA(0, function2(), "unicode->ansi")
sub function1(),wstring
return "hello" ' will be converted to unicode, using the current codepage
endsub
sub function2(),string
return L"hello" ' will be converted to ansi, using the current codepage
endsub
Added $ELIFDEF and $ELIFNDEF. All the $IF/$ELSE conditions can be mixed:
$IFDEF something
...
$elif VERSION>3
...
$elif !(VERSION<3)
...
$ELIFDEF DEMO_VERSION
...
$ELIFNDEF TIME_LIMITED
...
$ELSE
...
$ENDIF
Added single-byte initializer for structures (use it instead calling RtlZeroMemory):
sub ExecuteProcess()
STARTUPINFO si = 0 ' up to 255
STATDATA sdata
sdata.formatetc = 0
Added partial interface support for NEW command:
The CLSID and IID are automatically created from interface name: _CLSID_name, _IID_Iname, so it is not possible (yet) to create a new instance of an interface with different name as the class.
IProgressDialog unk = new IProgressDialog
'IProgressDialog unk = new IProgressDialog[1] ' count is ignored
'IProgressDialog unk = new(IProgressDialog,1) ' count is ignored
The compiler created temporary files in /bin directory. This is no more true: the temporary $$$lib.link will be created in the current-directory by default, or it will append the data to a file specified in LIB_LINK_PATH environment variable. The IWBasic IDE, when compiling a project, initializes LIB_LINK_PATH environment variable to PROJECT_DIR\$$$lib.link before compiling all source files, so that the parser will append all referenced .lib files to PROJECT_DIR\$$$lib.link file.
When using the /P option (for easier variables access in inline assembly) the compiler will check all formal and local variable names for possible conflict with assembly reserved keywords. Use mixed case in variable names if you get a warning.
$option "/P"
sub asm_test(int ret) ' warning: 'ret' variable is conflicting with ret/retn instruction.
sub asm_test(int Ret) ' no warning
Added #define - very helpful for ResEdit fans. Converting #define to $define each time you save the resource script, is now optional.
Added "ZeroVariables" option - when active, all local variables will be initialized to zero.
Added a check to the part handling assignments - it will warn you if the number you want to assign exceeds the bounds of target variable:
SCHAR a = 128 ' warning
a = -129 ' warning
SWORD b = 32768 - warning
b = -32769 ' warning
Added a new item to Tools menu: Edit compiler options. It will open a property sheet where you can set the default state of 11 compiler options, define additional include paths, and specify some default conditions or constants.
Comments in inline assembler will have the color of a comment (green).
Added $REGION and $ENDREGION - just another version of 'REGION and 'ENDREGION.
The optimizer now includes tracking of esi,si,edi,di,ebx,bx,bl registers - if they are not modified in a subroutine, the assembly code will not include the code for saving and restoring them.
Working on a plugin system for the parser, which will make possible to generate code for any platform/cpu (Windows/Linux/Mac/Arm/Pic/Atmel...), assembler, or additional high level compiler.
Quote from: sapero on January 20, 2011, 05:31:35 PM
Working on a plugin system for the parser, which will make possible to generate code for any platform/cpu (Windows/Linux/Mac/Arm/Pic/Atmel...), assembler, or additional high level compiler.
NICE!!!!!!!!!!!!!!
LarryMc
Ditto what LarryMc said for sure!
-Doc-
Quote from: sapero on January 20, 2011, 05:31:35 PMWorking on a plugin system for the parser, which will make possible to generate code for any platform/cpu (Windows/Linux/Mac/Arm/Pic/Atmel...), assembler, or additional high level compiler.
Now you are talking! :)
Nice!Barney
As always, you are doing a bang up job--good enough to keep me with you.
I do have a few thoughts about missing capabilities in IWBasic that I think would substantially improve the language:
1. Have we given any thought to adding a capability to use/create MACROS in IWBasic?
2. If I'm not mistaking, the IDE doesn't have an auto indent capability when writing code. I really miss this.
3. The IWBasic compiler doesn't remove unused code like other compilers I've used. Is this still true? Might be a consideration down the road.
4. One of the greatest features of the IWBasic compiler is that it is a multi-pass compiler, which means we can put code anywhere in the program and don't have to declare all subs and etc. beforehand. I hope IWBasic remains a multi-pass compiler.
5. I'd like to see built-in Mutex and Thread capability rather than having to use the Win API to build code to do these processes.
6. Since I habitually incorporate assembly code in my IWBasic programs, I'd like to be able to update the NASM compiler in the BIN directory to keep pace with NASM development.
7. Probably still a long way off, but IWBasic really needs to be implemented in 64-bit over the next year.
Just some thoughts.
By the way, I'd be willing to help out by writing and maintaining an IWBasic User's Manual if no one else is doing this.
Logman ;D
Quote2. If I'm not mistaking, the IDE doesn't have an auto indent capability when writing code. I really miss this.
I don't know when it was added but I know EB 1.735 has auto indent.
It's down at the botton of the dialog where you set the IDE code colors.
LarryMc
Quote from: sapero on January 20, 2011, 05:31:35 PM
Working on a plugin system for the parser, which will make possible to generate code for any platform/cpu (Windows/Linux/Mac/Arm/Pic/Atmel...), assembler, or additional high level compiler.
Fancy that - my last program was written for the PC, and I was asked if it was
possible to produce it for the Mac, as well. Not at all possible now.
But very possible in the future, methinks. Good news!
Brian
QuoteWorking on a plugin system for the parser, which will make possible to generate code for any platform/cpu (Windows/Linux/Mac/Arm/Pic/Atmel...), assembler, or additional high level compiler.
Console and/or GUI mode?
Sergio
Great! Now if I can find a compiler for WinCE. Need it already!!! Been doing some programming for GPS Units and using eVB which works but is outdated. ;D
Clint
We're getting closer to the release of Ver 2.0.
I'm basically the holdup; trying to get all the changes/enhancements documented in the help file.
I've got it to the floorboard but I'm always slow at documentation.
LarryMc
Boy, don't I feel stupid. I've been using the IDE for two years now without the indent feature--wishing it had one--and all along it did. I searched in the manual for this and since I didn't initially see it, I've just lived with it.
Thanks for pointing this our Larry. I sure missed it.
Can't wait for the release of 2.0.
Logman
Sergio, what do you mean with gui/console? These two are not related with the compiler, the linker cares about the subsystem.
Added OEM string modifier, to create strings with diacritical characters, which can be displayed in console window without calling CharToOem api:
print OEM"Ã,,…äáàâã"
Internally the string is encoded to an unicode string using the current codepage, then CharToOemW api is called, to convert it to console OEM. This api on Windows ME/98/95 is supported by Microsoft layer for unicode, so you will need to install it on the older systems, if you want to compile prorams with the OEM modifier.
The compiler will warn you if it fails to load unicows.dll.
On Windows 2000 or newer, the required api is available in user32.dll.
I have now a question: how would you like to set IWBasic IDE to unicode mode (utf8) ?
a) File/New/IWBasic unicode source file.
b) a popup menu named "Encoding" with ansi and utf8 items.
c) other (describe).
Quote from: sapero on January 25, 2011, 10:07:05 AM
I have now a question: how would you like to set IWBasic IDE to unicode mode (utf8) ?
a) File/New/IWBasic unicode source file.
b) a popup menu named "Encoding" with ansi and utf8 items.
c) other (describe).
a option is probably the best for most users
I'd prefer
b option but not as a popup menu. A switch on the toolbar will do nicely.
Barney
QuoteSergio, what do you mean with gui/console? These two are not related with the compiler, the linker cares about the subsystem.
Hi Sapero,
I'm sorry but I was unclear.
I wanted to know if you can create GUI applications using the normal IWBASIC syntax without having to resort to specific functions of the operating system (linux, MAC...).
bye
Sergio
Hopefully, the compiler's plugin will be responsible for conversion of the internal Basic commands for the target operating system.
Any configuration will be passed to the compiler in some universal form, for example: in the $OPTION directive:
$OPTION "plugin linux\qt.dll option1=value1 option2=value2 ..."
For example a plugin for Android OS could generate a complete java project, and then launch apkbuilder.bat to create an .apk file (or an native Linux executable) ready to upload to user's phone.
Quote from: sapero on January 25, 2011, 10:07:05 AM
I have now a question: how would you like to set IWBasic IDE to unicode mode (utf8) ?
a) File/New/IWBasic unicode source file.
b) a popup menu named "Encoding" with ansi and utf8 items.
c) other (describe).
Option
a would be my choice :)
Egil
Added $MACRO directive:
$macro sum(a,b) (a+b)
$macro sum(a,b,c) (a+b+c)
print sum(1,2)
print sum(1,2,4)
A macro will override a function with the same name and number of parameters:
declare import,sum(int a,int b),int
declare import,sum(int a,int b,int c),int
print sum(1,2) ' function
$macro sum(a,b) (a+b)
print sum(1,2) ' macro
print sum(1,2,3) ' function
You can call a function from inside your macro:
$macro ListboxDeleteAllItems(win,id) (SENDMESSAGE(win,LB_RESETCONTENT,0,0,id))
ListboxDeleteAllItems(d1,IDC_MYLISTBOX)
A macro can be undefined: $UNDEF MacroName NumberOfParameters
$undef sum 2
$endef sum 3
To check if a macro is defined, use $IFDEF MacroName.
Horay for MACROS.
Logman :)
Macros are supported in enumerations and $IF conditions.
Added a special macro: defined(name) - returns TRUE if the parameter 'name' is defined. The 'defined' keyword is now reserved.
$define MYCOND
$define WIN32
'$define MAC
$if defined(WIN32) && defined(MAC)
$error "WIN32 and MAC is defined"
$elif defined(WIN32) && defined(MYCOND)
$error "WIN32 and MYCOND is defined"
$elif defined(WIN32) && !defined(MYCOND)
$error "WIN32 is defined, but MYCOND is not defined"
$elif defined(MAC) && defined(MYCOND)
$error "MAC and MYCOND is defined"
$elif defined(MAC) && !defined(MYCOND)
$error "MAC is defined, but MYCOND is not defined"
$else
$error "unhandled case"
$endif
The look-up precedence in macros is:
1. symbols from macro definition - $macro name(this, and_this) (expression)
2. variables
3. constants
So if you refer "A" symbol in a macro, the compiler will first try to find macro parameter A, then A variable, and finally an constant A.
As I mentioned earlier I'm working on the Docs for Ver 2.0.
Right now I figure I'm around 1/2 way through adding all that needs to be added to support the V2.0 changes (not including what Sapero has added in the last few days).
I generated a PDF help file a few minutes ago. It has 86 more pages than the version I have that came with EB 1.737.
LarryMc
This is an exciting time in the history of IBasic and its derivatives.
Great work sapero!
Thanks!
Added support for multiple expressions in a single macro, use ":" to separate them
$macro INITPOINT(pt,xx,yy) (pt.x=xx : pt.y=yy)
POINT p
INITPOINT(p,3,4)
Note: the = operator inside a macro will always assign a value, and == will compare. This exception makes possible to assign values in macros.
If you define a macro with multiple expressions, only the first expression (leftmost) can be used to return a value. The return value from additional expressions will be ignored, and if neccessary, temporary strings or UDT's will be deleted.
$macro mymacro(a,b)(a:b)
print mymacro(APPEND("a","b"), APPEND("c","d"))
The first APPEND will be printed, the second APPEND will be executed and it's temporary result will be deleted before the macro returns.
$END directive will mark the end of file, any text following it will be ignored.
The LEN statement can be now used in expressions requiring a constant value, known at compile time. Almost any expression passed to LEN is supported, but string.
const MYCONST = len(1) + len(int) + len(myarray[1,2,3,4,5]) + len(my_int_variable) + len(*<POINT>p)
A function name passed to LEN will return the size of this subroutine. But the subroutine must exists in current source file (or in any included file). In this case, LEN will complete while assembling.
A new statement SIZEOF will always return the size in bytes of memory reserved for a variable (numbers, arrays, strings, classes, ...). SIZEOF will always evaluate at compile time, without generating code. SIZEOF(1) is 4, sizeof(string) is 255, and so on.
iwstring iw[32]
' compute the maximum number of characters
const NumberOfCharacters = sizeof(iw) / sizeof(iw[0])
print NumberOfCharacters ' 32
A subroutine definition can wrap to multiple lines. There is only one requirement (as for today): the "_" character must be the last in a line, not even space is allowed.
sub multiline( /*comments go here*/_
int a,_
int b)
print a,b
endsub
GLOBAL keyword is now accepting multiple variables:
GLOBAL a,b,c,d1
Added more options and command line switches, see first post.
The parser will load $$$lib.link file to memory, to prevent generating multiple libs with the same name. Relative libs can be resolved (to full paths) at compile time, and optionally the relative or full paths can be quoted.
BUGFIX: if you compile a file with tilde in the name (like "~.eba" or "~.iwb"), the assembler stops with critical error at the first "%line" macro (such file name should be quoted).
I have finished with the first implementation of thread-private variables
Quote from: MSDNThread Local Storage (TLS) is the mechanism by which each thread in a given multithreaded process allocates storage for thread-specific data. In standard multithreaded programs, data is shared among all threads of a given process, whereas thread local storage is the mechanism for allocating per-thread data.
declare import,CreateThread(pointer q,int q,int q, pointer q,int q,pointer q),int
declare import,WaitForSingleObject(int q,int q)
declare import,CloseHandle(int q)
' define some per-thread variables
thread int x,y
thread POINT pt
' main thread
x = 1
y = 2
pt.x = 3
pt.y = 4
print "main thread:"
DumpVariables(0) ' shows 1,2,3,4
' run a secondary thread
print "\nsecondary thread:" ' shows 0,0,0,0
int dwId
int hThread = CreateThread(0,0,&DumpVariables,0,0,&dwId)
if (hThread)
WaitForSingleObject(hThread, -1)
CloseHandle(hThread)
endif
sub DumpVariables(int reserved)
print "thread int x = ",x
print "thread int y = ",y
print "thread POINT pt.x = ", pt.x
print "thread POINT pt.y = ", pt.y
endsub
The
thread keyword must be specified first, to take effect (first in a line). The compiler updates an internal UDT as it sees the thread variables, so if you want to share thread variables between multiple source files, define them in an include file, or use the same order in all source files.
Nice work Sapero. It will be great having a native THREAD command/capability in IWBasic. Will there be an accompanying MUTEX command?
Logman
Threads and mutexes are native resources, easy to use and implement a wrapper. Each time I need to use a mutex, I do it in a different way. Using the direct apis is always better than calling a general and very limited command. A Lock/Unlock mutex commands could be implemented in a simple class with two methods. But is we start writing wrappers for all possible things, it will be Basic-MFC (with the M renamed) :)
Could you give me a suggestion, example code - how do you see it?
Recent changes:
You can include an .asm file directly from your IWBasic source file, using the build-in $INCLUDE directive. You can also add .asm file to your IWBasic project. File name extension must be .asm, to handle it correctly.
You can change the order of variables definitions from right to left, to left to right.
int a,b,c,d ' d is defined first, then c,b,a (default)
$option "DimOrder left"
int e,f,g,h ' e will be defined first, then f,g,h
$option "DimOrder left"
would imply there is a
$option "DimOrder right"
to undo it?
I only ask because I don't know why I would use the option in the first place. ???
LarryMc
There is push,pop,left,right and default. If you don't care in which order the variables are defined, you don't need to use this option.
If you for example convert some C/C++ code - a structure, the order in C is "left" and IWBasic uses "right":
struct POINT
{
int x,y; // x is first
};
' 1.
type POINT
int y,x ' x is first
endtype
' 2.
type POINT
int x ' x is first
int y
endtype
' 3.
$option "DimOrder left"
type POINT
int x,y ' x is first
endtype
I've been working on the documentation for several weeks now for Ver 2.0.
The problem is that Sapero can make enhancements/changes faster than I can document them properly in the help file.
This means that I'm loosing ground almost every day and the release date for 2.0 is getting pushed back further and further.
After discussing the problem with LarryS the following has been decided.
Today is the cutoff date for changes that will appear in Ver 2.0.
This topic is going to be locked and not changed further.
As a result I will have a firm endpoint for my documentation effort.
When I'm finished then 2.0 will be released.
Sapero will continue on with his enhancements/changes which will be included at a later date in ver 2.1.
In a private area of the forums, Sapero will document his efforts on 2.1. When 2.0 is released his postings for 2.1 will be moved to the open forum for all to see.
LarryMc
We have drawn a line in the sand.
IWBasic2.0 is going to release as soon as Larry Mc can wrap up the help files.
I expect to release no later than Mar 1 (hope, hope)
Larry