123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772 |
- ' Copyright (c) 2013-2024 Bruce A Henderson
- '
- ' Based on the public domain Monkey "trans" by Mark Sibly
- '
- ' This software is provided 'as-is', without any express or implied
- ' warranty. In no event will the authors be held liable for any damages
- ' arising from the use of this software.
- '
- ' Permission is granted to anyone to use this software for any purpose,
- ' including commercial applications, and to alter it and redistribute it
- ' freely, subject to the following restrictions:
- '
- ' 1. The origin of this software must not be misrepresented; you must not
- ' claim that you wrote the original software. If you use this software
- ' in a product, an acknowledgment in the product documentation would be
- ' appreciated but is not required.
- '
- ' 2. Altered source versions must be plainly marked as such, and must not be
- ' misrepresented as being the original software.
- '
- ' 3. This notice may not be removed or altered from any source
- ' distribution.
- '
- SuperStrict
- Import BRL.LinkedList
- Import BRL.Map
- Import BRL.FileSystem
- Import BRL.Math
- Import "options.bmx"
- Import "base.stringhelper.bmx"
- Import "base64.bmx"
- Import "enums.c"
- Import "hash.c"
- Import "math.c"
- Import "zlib/zlib.bmx"
- ' debugging help
- Const DEBUG:Int = False
- Const ABORT_ON_NULL:Int = True
- Const PROFILER:Int = False
- Const DEBUGSTOP_ON_ERROR:Int = False
- Const SHOW_INTERNALERR_LOCATION:Int = True
- Global ENV_LANG$
- Global _errInfo$
- Global _errStack:TList = New TList
- ' bytes offset to the first field
- Global OBJECT_BASE_OFFSET:Int = 8
- ' 4 bytes on 32-bit, 8 bytes on 64-bit
- Global POINTER_SIZE:Int = 4
- Global _symbols$[]=[ "..","[]",":*",":/",":+",":-",":|",":&",":~~",":shr",":shl",":sar",":mod"]
- Global _symbols_map$[]=[ "..","[]","*=","/=","+=","-=","|=","&=","^=",">>=", "<<=",">>=","%=" ]
- Global _fileHasher:TFileHash
- Function PushErr( errInfo$ )
- _errStack.AddLast _errInfo
- _errInfo=errInfo
- End Function
- Function PopErr()
- _errInfo=String(_errStack.RemoveLast())
- End Function
- Function Err( err$ )
- If DEBUGSTOP_ON_ERROR Then
- DebugStop ' useful for debugging!
- End If
- Throw "Compile Error: "+err + "~n" + _errInfo + "~n"
- End Function
- Function Warn( err$ )
- 'If DEBUGSTOP_ON_ERROR Then
- ' DebugStop ' useful for debugging!
- 'End If
- Print "Compile Warning: "+err + "~n" + _errInfo + "~n"
- End Function
- Function FormatError:String(path:String, line:Int, char:Int)
- Return "[" + path + ";" + line + ";" + char + "]"
- End Function
- Function InternalErr(errorLocation:String)
- If DEBUGSTOP_ON_ERROR Then
- DebugStop ' useful for debugging!
- End If
- Local locationMsg:String
- If SHOW_INTERNALERR_LOCATION And errorLocation Then locationMsg = " in " + errorLocation
- Throw "Compile Error: Internal Error" + locationMsg + ".~nPlease report the issue, with an example if possible, to https://github.com/bmx-ng/bcc/issues/new~n" + _errInfo + "~n"
- End Function
- Function IsSpace:Int( ch:Int )
- Return ch<=Asc(" ") Or ch=$A0 ' NO-BREAK SPACE (U+00A0)
- End Function
- Function IsDigit:Int( ch:Int )
- Return ch>=Asc("0") And ch<=Asc("9")
- End Function
- Function IsAlpha:Int( ch:Int )
- Return (ch>=Asc("A") And ch<=Asc("Z")) Or (ch>=Asc("a") And ch<=Asc("z"))
- End Function
- Function IsBinDigit:Int( ch:Int )
- Return ch=Asc("0") Or ch=Asc("1")
- End Function
- Function IsHexDigit:Int( ch:Int )
- Return IsDigit(ch) Or (ch>=Asc("A") And ch<=Asc("F")) Or (ch>=Asc("a") And ch<=Asc("f"))
- End Function
- Function Todo()
- Err "TODO!"
- End Function
- Function StringToLong:Long(value:String)
- Local Sign:Int = 1
- Local i:Int
- While i < value.length And (value[i] = Asc("+") Or value[i] = Asc("-"))
- If value[i] = Asc("-") Then
- Sign = -1
- End If
- i :+ 1
- Wend
-
- Local n:Long = 0
- While i < value.length
- Local c:Int = value[i]
- If Not IsDigit(c) Exit
- n = n * 10 + (c-Asc("0"))
- i :+ 1
- Wend
- Return n
- End Function
- Function IsStandardFunc:Int(func:String)
- func = func.ToLower()
-
- Global funcs:String = ";isalnum;isalpha;isascii;isblank;iscntrl;isdigit;isgraph;islower;isprint;ispunct;isspace;isupper;isxdigit;" + ..
- "strlen;_wgetenv;_wputenv;"
-
- Return funcs.Find(func) > 0
- End Function
- Function mapSymbol:String(sym:String)
- For Local i:Int = 0 Until _symbols.length
- If sym = _symbols[i] Then
- Return _symbols_map[i]
- End If
- Next
- Return sym
- End Function
- 'enquote depending on ENV_LANG
- '
- Function LangEnquote$( str$ )
- str=EscapeString(str)
- ' str=str.Replace( "~0","\0" ) 'Fix me?
- For Local i:Int=0 Until str.Length
- If str[i]>=32 And str[i]<128 Continue
- Local t$,n:Int=str[i]
- While n
- Local c:Int=(n&15)+48
- If c>=58 c:+97-58
- t=Chr( c )+t
- n=(n Shr 4) & $0fffffff
- Wend
- If Not t t="0"
- If ENV_LANG = "cpp" Then
- 'Case "cpp"
- t="~q~q\x"+t+"~q~q"
- Else
- t="\u"+("0000"+t)[-4..]
- End If
- str=str[..i]+t+str[i+1..]
- i:+t.Length-1
- Next
- str="~q"+str+"~q"
- If ENV_LANG="cpp" str="L"+str
- Return str
- End Function
- Function EscapeString$(str$)
- str=str.Replace( "\","\\" )
- str=str.Replace( "~q","\~q" )
- str=str.Replace( "~n","\n" )
- str=str.Replace( "~r","\r" )
- str=str.Replace( "~t","\t" )
- Return str
- End Function
- Function EscapeLines:String(str:String)
- str=str.Replace("~n", "Newline")
- Return str
- End Function
- Function BmxEnquote$( str$ )
- str=str.Replace( "~~","~~~~" )
- str=str.Replace( "~q","~~q" )
- str=str.Replace( "~n","~~n" )
- str=str.Replace( "~r","~~r" )
- str=str.Replace( "~t","~~t" )
- str=str.Replace( "~0","~~0" )
- str="~q"+str+"~q"
- Return str
- End Function
- Function BmxUnquote$( str$, unquoted:Int = False )
- Local length:Int
- Local i:Int
- If Not unquoted Then
- If str.length < 2 Or str[str.length - 1] <> Asc("~q") Then
- Err "Expecting expression but encountered malformed string literal"
- End If
- length = str.length - 1
- i = 1
- Else
- length = str.length
- End If
- Local sb:TStringBuffer = New TStringBuffer
- While i < length
- Local c:Int = str[i]
- i :+ 1
- If c <> Asc("~~") Then
- sb.AppendChar(c)
- Continue
- End If
- If i = length Err "Bad escape sequence in string"
-
- c = str[i]
- i :+ 1
-
- Select c
- Case Asc("~~")
- sb.AppendChar(c)
- Case Asc("0")
- sb.AppendChar(0)
- Case Asc("t")
- sb.AppendChar(Asc("~t"))
- Case Asc("r")
- sb.AppendChar(Asc("~r"))
- Case Asc("n")
- sb.AppendChar(Asc("~n"))
- Case Asc("q")
- sb.AppendChar(Asc("~q"))
- Case Asc("$") ' hex
- c = str[i]
- i :+ 1
- Local n:Int
- While True
- Local v:Int
- If c >= Asc("0") And c <= Asc("9") Then
- v = c-Asc("0")
- Else If c >= Asc("a") And c <= Asc("f") Then
- v = c-Asc("a")+10
- Else If c >= Asc("A") And c <= Asc("F") Then
- v = c-Asc("A")+10
- Else If c <> Asc("~~")
- Err "Bad escape sequence in string"
- Else
- Exit
- End If
- n = (n Shl 4) | (v & $f)
- If i = length Err "Bad escape sequence in string"
- c = str[i]
- i :+ 1
- Wend
- If c <> Asc("~~") Err "Bad escape sequence in string"
- sb.AppendChar(n)
- Case Asc("%") ' bin
- c = str[i]
- i :+ 1
- Local n:Int
- While c = Asc("1") Or c = Asc("0")
- n :Shl 1
- If c = Asc("1") Then
- n :| 1
- End If
- If i = length Err "Bad escape sequence in string"
- c = str[i]
- i :+ 1
- Wend
- If c <> Asc("~~") Err "Bad escape sequence in string"
- sb.AppendChar(n)
- Default
- If c >= Asc("1") And c <= Asc("9") Then
- Local n:Int
- While c >= Asc("0") And c <= Asc("9")
- n = n * 10 + (c-Asc("0"))
- If i = length Err "Bad escape sequence in string"
- c = str[i]
- i :+ 1
- Wend
- If c <> Asc("~~") Err "Bad escape sequence in string"
- sb.AppendChar(n)
- Else
- Err "Bad escape sequence in string"
- End If
- End Select
- Wend
- Return sb.ToString()
- End Function
- Function BmxProcessMultiString:String( str:String )
- Local valid:Int
- If str.length < 7 Then
- Err "Expecting expression but encountered malformed multiline string literal"
- End If
-
- For Local i:Int = 0 Until 3
- If str[i] <> Asc("~q") Or str[str.length -1 -i] <> Asc("~q") Then
- Err "Expecting expression but encountered malformed multiline string literal"
- End If
- Next
-
- str = str[3..str.length - 3]
- ' normalise line endings
- str = str.Replace("~r~n", "~n").Replace("~r", "~n")
- If str[0] <> Asc("~n") Then
- Err "Expecting EOL but encountered malformed multiline string literal"
- End If
- str = str[1..]
- Local LINES:String[] = str.Split("~n")
- Local lineCount:Int = LINES.length - 1
- Local last:String = LINES[lineCount]
-
- Local i:Int = last.length - 1
- While i >= 0
- If last[i] <> Asc(" ") And last[i] <> Asc("~t") Then
- Err "Expecting trailing whitespace"
- End If
- i :- 1
- Wend
-
- Local trailingIndent:String = last
-
- ' strip indent
- If trailingIndent Then
- For i = 0 Until lineCount
- Local line:String = LINES[i]
- If line.StartsWith(trailingIndent) Then
- line = line[trailingIndent.length..]
- LINES[i] = line
- End If
- Next
- End If
- ' right trim
- For i = 0 Until lineCount
- Local line:String = LINES[i]
- Local index:Int = line.length
- While index
- index :- 1
- If line[index] <> Asc(" ") And line[index] <> Asc("~t") Then
- Exit
- End If
- Wend
- If index < line.length - 1 Then
- line = line[..index + 1]
- LINES[i] = line
- End If
- Next
- Local sb:TStringBuffer = New TStringBuffer
- For i = 0 Until lineCount
- Local line:String = LINES[i]
- Local length:Int = line.length
- Local softWrap:Int
- If line And line[line.length-1] = Asc("\") Then
- softWrap = True
- length :- 1
- End If
- If line Then
- sb.Append(line[..length])
- End If
- If Not softWrap And i < lineCount - 1 Then
- sb.Append("~n")
- End If
- Next
- Return BmxUnquote(sb.ToString(), True)
- End Function
- Type TStack Extends TList
- Method Push(obj:Object)
- AddFirst(obj)
- End Method
- Method Length:Int()
- Return count()
- End Method
-
- Method Get:Object(index:Int)
- Return ValueAtIndex(index)
- End Method
-
- Method Pop:Object()
- Return RemoveFirst()
- End Method
-
- End Type
- Type TStringList Extends TList
- Method Join:String(s:String)
- Local arr:String[] = New String[count()]
- Local index:Int
- For Local t:String = EachIn Self
- arr[index] = t
- index :+ 1
- Next
-
- Return s.Join(arr)
- End Method
- End Type
- Type TKeyValue
- Field key:Object
- Field value:Object
-
- Method Create:TKeyValue(key:Object,value:Object)
- Self.key = key
- Self.value = value
- Return Self
- End Method
-
- Method Compare:Int(other:Object)
- If Not TKeyValue(other) Return 0
- Return key.Compare(TKeyValue(other).key)
- End Method
-
- End Type
- Type TUnorderedMap
- Field list:TList = New TList
- Field map:TMap = New TMap
-
- Field valuesList:TList = New TList
- Method Insert( key:Object,value:Object )
- list.AddLAst(New TKeyValue.Create(key, value))
- valuesList.AddLast(value)
- map.Insert(key, value)
- End Method
-
- Method Keys:TList()
- Local klist:TList = New TList
- For Local kv:TKeyValue = EachIn list
- klist.AddLast(kv.key)
- Next
- Return klist
- End Method
-
- Method Values:TList()
- 'Local vlist:TList = New TList
- 'For Local kv:TKeyValue = EachIn list
- ' vlist.AddLast(kv.value)
- 'Next
- Return valuesList
- End Method
-
- Method Contains:Int( key:Object )
- Return map.Contains(key)
- End Method
-
- Method ValueForKey:Object( key:Object )
- Return map.ValueForKey(key)
- End Method
- End Type
- Function MakeKeywords:String()
- Local keywords:String
-
- keywords :+ "import brl.classes~n"
- keywords :+ "Asc%(v$)=~qbrl_blitz_keywords_asc~q~n"
- keywords :+ "Chr$(v%)=~qbrl_blitz_keywords_chr~q~n"
- keywords :+ "Len%(v:Object)=~qbrl_blitz_keywords_len~q~n"
- keywords :+ "IncbinPtr@*(v$)=~qbbIncbinPtr~q~n"
- keywords :+ "IncbinLen%(v$)=~qbbIncbinLen~q~n"
-
- Return keywords
- End Function
- Function FilePath:String(path:String)
- Local baseDir:String = ExtractDir(path)
- Local bmxDir:String = baseDir + "/.bmx"
-
- If FileType(bmxDir) <> FILETYPE_DIR Then
- Throw "Missing : " + bmxDir
- End If
-
- Return bmxDir
- End Function
- Function BuildHeaderName:String(path:String)
- If opt_buildtype = BUILDTYPE_MODULE Then
- path = opt_modulename + "_" + StripDir(path)
- Else
- Local dir:String = ExtractDir(path).ToLower().Replace("/.bmx","")
- dir = dir[dir.findLast("/") + 1..]
- If dir.EndsWith(".mod") Then
- dir = dir.Replace(".mod", "")
- End If
- Local file:String = StripDir(path).ToLower()
- path = dir + "_" + file
- End If
-
- Return TStringHelper.Sanitize(path, , True)
- End Function
- Rem
- bbdoc: Get the header file name from a given module ident, optionally with include path.
- End Rem
- Function ModuleHeaderFromIdent:String(ident:String, includePath:Int = False)
- Local ns:String = ident[..ident.find(".")]
- Local name:String = ident[ident.find(".") + 1..]
-
- Local file:String = name + ".bmx" + FileMung() + ".h"
-
- If includePath Then
- file = ns + ".mod/" + name + ".mod/.bmx/" + file
- End If
-
- Return file
- End Function
- Function HeaderFile:String(path:String, mung:String)
- Local fileDir:String = FilePath(path)
- Local file:String = StripDir(path)
-
- Return fileDir + "/" + file + mung + ".h"
- End Function
- Function OutputFilePath:String(path:String, mung:String, suffix:String, bmxDir:Int = False)
- Local fileDir:String = FilePath(path)
- If bmxDir Then
- fileDir :+ "/.bmx"
- End If
- Local file:String = StripDir(path)
-
- Return fileDir + "/" + file + mung + "." + suffix
- End Function
- Function FileMung:String(makeApp:Int = False)
- Local m:String = "."
-
- If makeApp Then
- Select opt_apptype
- Case APPTYPE_CONSOLE
- m :+ "console."
- Case APPTYPE_GUI
- m :+ "gui."
- End Select
- End If
-
- If opt_release Then
- m :+ "release"
- Else
- m :+ "debug"
- End If
-
- If opt_coverage Then
- m :+ ".cov"
- End If
- ' If opt_threaded Then
- ' m :+ ".mt"
- ' End If
-
- m :+ "." + opt_platform
-
- m :+ "." + opt_arch
-
- Return m
- End Function
- Function HeaderComment:String()
- ' TODO
- End Function
- Global fileRegister:TMap = New TMap
- Function GenHash:String(file:String)
- Local Hash:String = bmx_gen_hash(file)
- If Not fileRegister.Contains(Hash) Then
- fileRegister.Insert(Hash, file)
- End If
-
- Return Hash
- End Function
- Type TTemplateRecord
- Field start:Int
- Field file:String
- Field source:String
-
- Method Create:TTemplateRecord(start:Int, file:String, source:String)
- Self.start = start
- Self.file = file
- Self.source = source
- Return Self
- End Method
-
- Method ToString:String()
- Local s:Byte Ptr = source.ToUTF8String()
- ?Not bmxng
- Local slen:Int = strlen_(s)
- ?bmxng
- Local slen:ULongInt = strlen_(s)
- ?
- ?Not bmxng
- Local dlen:Int = slen + 12
- ?bmxng
- Local dlen:ULongInt = slen + 12
- ?
- Local data:Byte[dlen]
-
- compress2(data, dlen, s, slen, 9)
-
- MemFree(s)
-
- Local t:String = "{" + start +","+ slen +","+ LangEnquote(file) + ","
-
- t :+ LangEnquote(TBase64.Encode(data, Int(dlen), 0, TBase64.DONT_BREAK_LINES))
- Return t + "}"
- End Method
-
- Function Load:TTemplateRecord(start:Int, file:String, size:Int, source:String)
-
- ?Not bmxng
- Local dlen:Int = size + 1
- ?bmxng
- Local dlen:ULongInt = size + 1
- ?
- Local data:Byte[dlen]
-
- Local s:Byte[] = TBase64.Decode(source)
- ?Not bmxng
- uncompress(data, dlen, s, s.length)
- ?bmxng
- uncompress(data, dlen, s, ULongInt(s.length))
- ?
- Return New TTemplateRecord.Create(start, file, String.FromUTF8String(data))
- End Function
- End Type
- Type TCallback
- Method Callback(obj:Object) Abstract
- End Type
- Type TFileHash
- Field statePtr:Byte Ptr
-
- Method Create:TFileHash()
- statePtr = bmx_hash_createState()
- Return Self
- End Method
-
- Method CalculateHash:String(stream:TStream)
- Const BUFFER_SIZE:Int = 8192
-
-
- bmx_hash_reset(statePtr)
-
- Local data:Byte[BUFFER_SIZE]
-
- While True
- Local read:Int = stream.Read(data, BUFFER_SIZE)
- bmx_hash_update(statePtr, data, read)
-
- If read < BUFFER_SIZE Then
- Exit
- End If
- Wend
-
- Return bmx_hash_digest(statePtr)
-
- End Method
- End Type
- Function CalculateFileHash:String(path:String)
- If Not _fileHasher Then
- _fileHasher = New TFileHash.Create()
- End If
- If FileType(path) = FILETYPE_FILE Then
- Local stream:TStream = ReadStream(path)
- Local fileHash:String = _fileHasher.CalculateHash(stream)
- stream.Close()
-
- Return fileHash
- End If
-
- Return Null
- End Function
- ?Not bmxng
- Const OP_MUL:Int = 0
- Const OP_DIV:Int = 1
- Const OP_MOD:Int = 2
- Const OP_SHL:Int = 3
- Const OP_SHR:Int = 4
- Const OP_SAR:Int = 5
- Const OP_ADD:Int = 6
- Const OP_SUB:Int = 7
- Const OP_AND:Int = 8
- Const OP_XOR:Int = 9
- Const OP_OR:Int = 10
- Function OpToInt:Int(op:String)
- Select op
- Case "*" Return OP_MUL
- Case "/" Return OP_DIV
- Case "mod" Return OP_MOD
- Case "shl" Return OP_SHL
- Case "shr" Return OP_SHR
- Case "sar" Return OP_SAR
- Case "+" Return OP_ADD
- Case "-" Return OP_SUB
- Case "&" Return OP_AND
- Case "~~" Return OP_XOR
- Case "|" Return OP_OR
- End Select
- InternalErr "TBinaryMathExpr.Eval.OpToInt : " + op
- End Function
- ?
- Extern
- Function strlen_:Int(s:Byte Ptr)="strlen"
- Function bmx_enum_next_power(char:Int, val:Long Var, ret:Long Var)
- Function bmx_gen_hash:String(txt:String)
- Function bmx_hash_createState:Byte Ptr()
- Function bmx_hash_reset(state:Byte Ptr)
- Function bmx_hash_update(state:Byte Ptr, data:Byte Ptr, length:Int)
- Function bmx_hash_digest:String(state:Byte Ptr)
- Function bmx_bitwise_not_uint:String(value:String)
- Function bmx_bitwise_not_sizet:String(value:String)
- Function bmx_bitwise_not_ulong:String(value:String)
- Function bmx_bitwise_not_longint:String(value:String, size:Int)
- Function bmx_bitwise_not_ulongint:String(value:String, size:Int)
- Function bmx_binarymathexpr_sizet:String(op:Int, lhs:String, rhs:String)
- Function bmx_binarymathexpr_uint:String(op:Int, lhs:String, rhs:String)
- Function bmx_binarymathexpr_ulong:String(op:Int, lhs:String, rhs:String)
- Function bmx_binarymathexpr_longint:String(op:Int, lhs:String, rhs:String, size:Int)
- Function bmx_binarymathexpr_ulongint:String(op:Int, lhs:String, rhs:String, size:Int)
- End Extern
|