Repository navigation
Category String Library
Home › All Categories › String Library
#import "std/containers"
#import "std/strings"
using strings, containers
print Replace( "Hello World", "World", "Earth" ) ' Hello Earth
print Tally( "a,b,c", "," ) ' 2
print PadLeft( "42", 6, "0" ) ' 000042
print Between( "log(42) end", "(", ")" ) ' 42
dim parts as Array( of string ) = Split( "a,b,c" )
print parts.Count( ) ' 3
print Join( parts, " | " ) ' a | b | c
dim rows as Array( of CsvRow ) = SplitCsv( text ) ' RFC 4180, quotes and all
print rows[ 0 ].fields[ 1 ]Building text from values with a layout -- a width, a number of decimals, hexadecimal -- is Format, in the same module.
The built-in string intrinsics include LEFT, RIGHT, MID, INSTR, INSTRREV, TRIM, UCASE, LCASE, SPACE and STRING, but none of them replaces, splits, joins or pads, and none works with a set of characters. Every program that needs those writes them again, usually with a 'redim preserve' loop that is quadratic.
NOTHING HERE IS A KEYWORD. Replace, Contains, Split and the rest belong to the module: nothing is added to a program until it imports std/strings, and the bare names only after 'using strings', so they stay available as your own identifiers. The language is unchanged.
Every function is declared three times -- STRING, WSTRING, USTRING -- and ZSTRING is served by the STRING overload through the compiler's own conversion, which for bytes to bytes is exact and involves no locale. You never name which family you are in; the first argument decides.
There is no dynamic ZSTRING or WSTRING, so a function cannot return the type it was handed:
string, zstring -> string bytes, no encoding assumed
wstring, ustring -> ustring UTF-16 on every targetA WSTRING in giving a USTRING out is the surprising one. It is also the only answer available: a fixed-length result would need a buffer the caller has not supplied. On Windows it costs nothing to use, because a ustring already is UTF-16.
EVERY PARAMETER AFTER THE FIRST IS A USTRING, in all three overloads. That is forced, not preferred. The compiler ranks an overload on all of its parameters, so with per-family types on the second one,
Tally( someWstring, "abc" ) ' error 98: Ambiguous callis an ambiguity rather than a choice. One shared type on the later parameters moves the whole decision onto the first.
The cost is that a pattern handed to the BYTE family is converted UTF-8 -> UTF-16 -> UTF-8 on the way in. For an ASCII or UTF-8 pattern that is exact, and for a literal it costs nothing at run time -- the compiler converts string literals to ustring at compile time. It does not survive a pattern of arbitrary non-UTF-8 bytes; searching binary for a raw byte sequence is outside what this library does, and USTRING draws the same line. The haystack is never converted, so byte positions in a STRING stay byte positions.
Positions and lengths count CODE UNITS, matching LEN, [] and INSTR. On the wide family an astral character is a surrogate pair and counts as 2. A search can never land mid-character -- a surrogate half cannot equal a BMP unit -- though MID can split one.
POSITIONS ARE 1-BASED, and 0 means "not found", matching INSTR. The functions over string slices -- Find, Equals, TrimSpace, SplitSlices and the slice overloads of StartsWith and EndsWith -- are the exception: they count from 0, as s[i] and s[lo to hi] do, and Find answers -1. This is NOT the zero-based convention containers.Array uses. Strings and containers are different worlds and each keeps its own tradition.
A start below 1 is invalid and answers 0 (or "" for a function returning text) rather than clamping, so a position fed back in from a call that returned "not found" cannot silently restart the scan.
'ignoreCase' folds ASCII only on the byte family, because a STRING is a byte buffer with no declared encoding and folding above 127 would be guessing at a codepage. The wide family folds through the generated simple BMP table, so it gives the same answer on every machine and in every locale.
ON THE BYTE FAMILY A CHARACTER SET IS A SET OF BYTES. InstrChars, TallyChars, VerifySet, SpanOf, RemoveChars, RetainChars, ReplaceChars and SplitChars compare one unit at a time, and on a STRING a unit is a byte. Put a multi-byte character in the set and each of its bytes joins separately:
dim as string s = "xx" & chr(&hC3) & chr(&hA9) & "yy" ' UTF-8
InstrChars( 1, s, wchr(&hE8) ) ' 3 -- matched the C3 byteA STRING carries no encoding, so the set cannot be decoded into characters. Use the ustring overload for character sets over non-ASCII text; there a set member is one code unit and the answer is 4. The substring functions are unaffected on both families: they match whole sequences and are exact for any UTF-8 pattern.
n is the input length, m the pattern length.
SEARCH AND INSPECT returns
Tally( s, match [, ic] ) O(n*m) int32
Non-overlapping occurrences. "aaaa" contains "aa" TWICE.
TallyChars( s, chars [, ic] ) O(n*k) int32
InstrChars( start, s, chars [, ic] ) O(n*k) int32
VerifySet( start, s, chars [, ic] ) O(n*k) int32
First position NOT in the set, or 0 if every one is.
SpanOf( start, s, chars [, ic] ) O(n*k) int32
StartsWith( s, prefix [, ic] ) O(m) boolean
EndsWith( s, suffix [, ic] ) O(m) boolean
Contains( s, match [, ic] ) O(n*m) booleanEXTRACT
Extract( start, s, match [, ic] ) O(n*m) text before match
ExtractChars( start, s, chars [, ic] ) O(n*k)
Remain( s, match [, start] [, ic] ) O(n*m) text after match
RemainChars( s, chars [, start] [, ic] ) O(n*k)
Between( s, open, close [, start] [, ic] ) O(n*m)
ClipLeft( s, count ) O(n)
ClipRight( s, count ) O(n)
DeleteAt( s, start, count ) O(n)
InsertAt( s, insert, position ) O(n)TRANSFORM
Replace( s, match, with_ [, ic] ) O(n*m)
Remove( s, match [, ic] ) O(n*m)
RemoveChars( s, chars [, ic] ) O(n*k)
RetainChars( s, chars [, ic] ) O(n*k)
ReplaceChars( s, chars, with_ [, ic] ) O(n*k) length is invariant
RemoveBetween( s, open, close
[, all] [, start] [, ic] ) O(n*m)
Reverse( s ) O(n)
Repeat( count, s ) O(n*count)
Shrink( s [, mask] ) O(n*k)
MCase( s ) O(n) title casePAD, WRAP, ESCAPE, PREDICATES
PadLeft( s, width [, pad] ) O(width)
PadRight( s, width [, pad] ) O(width)
PadCenter( s, width [, pad] ) O(width)
Wrap( s [, open] [, close] ) O(n)
Unwrap( s [, open] [, close] [, ic] ) O(n)
Escape( s ) O(n)
Unescape( s ) O(n)
IsNumeric( s ) O(n) boolean
IsBlank( s ) O(n) booleanSPLIT AND JOIN
Split( s [, delim] [, ic] ) O(n*m) Array( of T )
SplitChars( s [, chars] [, ic] ) O(n*k) Array( of T )
Join( parts [, delim] ) O(total) textCSV
SplitCsv( s [, delim] ) O(n) Array( of CsvRow )
A whole RFC-4180 document: records and fields in one pass.
Zero rows means an unclosed quote.
SplitCsvRow( s [, delim] ) O(n) Array( of T )
One record. The quote-aware Split. Empty means malformed.Each of these has a defensible opposite, so each is a decision rather than an accident, and each is asserted in the test suite.
EMPTY ARGUMENTS
Tally( s, "" ) 0 a zero-width match advances nothing
InstrChars( n, s, "" ) 0 "find one of nothing" cannot succeed
VerifySet( n, s, "" ) n "find one NOT among nothing" cannot fail
SpanOf( n, s, "" ) 0 nothing is in the set, so the run is empty
StartsWith( s, "" ) true vacuous, including for s = ""
Contains( s, "" ) true same
RemoveChars( s, "" ) s an empty set removes nothing
RetainChars( s, "" ) "" and so keeps nothing -- the complement
Shrink( s, "" ) s there is nothing to shrink
Split( s, "" ) one field, the whole string
SplitCsv( s, "" ) one field per record, records still split
SplitCsv( "" ) one record of one empty fieldEXTRACT AND REMAIN MISS DIFFERENTLY, on purpose:
Extract( 1, s, missing ) the whole remainder
Remain( s, missing ) ""Extract's rule is what makes a parse loop terminate cleanly -- the final field has no trailing delimiter, and this hands it back instead of dropping it. Remain's is the only honest answer to "everything after X" when there was no X.
SPLIT NEVER DROPS A FIELD. N delimiters give N+1 fields, always:
Split( "a,,c" ) -> 3 Split( ",a" ) -> 2 Split( "" ) -> 1That is what makes Join( Split( s, d ), d ) equal s for every s.
SPLITCSV'S TRAILING RULE IS THE OPPOSITE, and the two sit next to each other, so it is worth saying twice: Split( "a," ) is two fields, and SplitCsv( "a,b\n" ) is one record. A trailing delimiter is a field the writer meant; a trailing newline is how text files end.
INVALID ARGUMENTS ARE NO-OPS, not errors and not truncation. DeleteAt with a start past the end, or a count of zero, returns the string unchanged; InsertAt with a position below 1 does the same. A pad width of 0 or less gives "".
ONE UNIT MEANS ONE UNIT. ReplaceChars maps a single unit to a single unit, so 'with_' must be exactly one; anything else -- empty, longer, or an astral character, which is two units -- returns the string unchanged rather than writing half a character. The pad functions take the same rule with a softer failure: a pad that is not one unit pads with a SPACE, because a pad function that pads with nothing is useless while a substitution that substitutes nothing is merely inert.
Split is the wrong tool for a comma-separated FILE, and not by a little. There is no delimiter that survives being both a separator and content, so split-on-comma-then-repair cannot be patched into correctness; a quoted field containing a comma is not an edge case, it is the first line of most real exports.
SplitCsv handles, in a single pass with a cursor:
"a,b" a delimiter inside quotes, taken literally
"" two quotes inside quotes, as one literal quote
CRLF, LF, CR mixed freely, each ending exactly one record
"l1\nl2" a NEWLINE INSIDE A QUOTED FIELD -- content, not a record break
a,b, a trailing empty field
a,b a last record with no trailing newlineRECORDS ARE FOUND BY THE SAME PASS AS FIELDS, which is what buys the embedded newline. A row is not a line. Splitting into lines first passes every other case and fails that one, and broker and CRM exports do contain memo columns with newlines in them.
The result is a table:
dim rows as Array( of CsvRow ) = SplitCsv( text )
for r as integer = 0 to rows.Count( ) - 1
for c as integer = 0 to rows[ r ].fields.Count( ) - 1
print rows[ r ].fields[ c ]
next
nextCsvRow holds STRING fields and CsvURow holds USTRING ones, following the same return rule as everything else here -- a STRING in gives CsvRow, a WSTRING or USTRING gives CsvURow. A row is a TYPE wrapping an array rather than a bare nested generic, because a generic will not instantiate directly in a TYPE field: Array( of Array( of string ) ) is not available.
NO FILE I/O. This takes text and returns rows; the reading belongs to the caller. That is what lets every awkward case be an assertion in the test suite instead of something discovered on a Tuesday with a real statement.
AN UNCLOSED QUOTE ANSWERS ZERO ROWS. A well-formed parse always yields at least one record -- an empty input is one record of one empty field, the same rule that makes Split( "" ) one field -- so a count of 0 is unambiguous and means exactly one thing:
dim rows as Array( of CsvRow ) = SplitCsv( text )
if rows.Count( ) = 0 then ' malformed; do not use the rowsConsuming the rest of the document as one field is the alternative, and it produces a plausible-looking single row rather than a complaint. Silence is the one answer that cannot be checked. There is no Result type here and no error code: the count already carries it, and a total function needs neither.
A TRAILING NEWLINE DOES NOT MAKE AN EXTRA RECORD -- "a,b\n" is one row, not two. That is the opposite of Split's never-drop-a-field rule, and deliberately so: a file ending in a newline is the normal case, not a file with a blank record at the end. A blank line in the MIDDLE is still a real, empty record.
A DELIMITER THAT IS NOT EXACTLY ONE CODE UNIT means no delimiter at all: every record comes back as one field, with records and quoting still honoured. Same rule as Split's empty delimiter, and there is no useful reading of a two-character CSV separator.
ON THE BYTE FAMILY THIS IS EXACT FOR UTF-8, unlike the character-set functions. Quote, comma, CR and LF are all below 0x80, and no UTF-8 continuation byte is, so a multi-byte character can never be mistaken for a delimiter or a record break. The fields come back as the same bytes that went in.
SplitCsvRow is the same parser over a single record, returning the Array( of T ) that Split returns -- a drop-in wherever the fields may be quoted. An empty result means malformed, on the same rule. Text holding more than one record answers the first; the rest is a table, and SplitCsv is what returns tables.
COMPLEXITY IS O(n). The scan appends whole runs of ordinary characters with MID rather than one unit at a time. Per-character '&=' is what the obvious version does, and it is quadratic in the field length, because a tiko string grows one 32-byte granule at a time, not geometrically.
Ported from the finara import engine (fnr_CsvParse), itself ported from tracks. The cursor is the same; three things changed on the way in:
- the failure is a ROW COUNT rather than a Result( of Table, Error ), so the string library keeps its single dependency on std/containers' Array and stays total;
- an EMPTY INPUT is one empty record rather than none, which is what makes the zero-row signal unambiguous;
- a delimiter that is not one unit is a no-op rather than an error, matching how every other invalid argument here behaves.
The column profiles and transaction mapping that surround the parser in finara did not come with it. They are that application's schema, not a string algorithm.
JoinCsvRow( fields [, sep] ) is SplitCsv's other half: it turns an Array( of ustring ) into one CSV record, and CsvWriter writes records to any fs.Stream -- a file, a pipe -- as UTF-8.
#import "std/strings"
#import "std/containers"
#import "std/fs"
dim as containers.Array( of ustring ) row
row.Push( "name" ) : row.Push( "a, b" ) : row.Push( !"say \"hi\"" )
print strings.JoinCsvRow( row ) ' name,"a, b","say ""hi"""
dim as fs.File f
if f.Create( "out.csv" ).IsOk( ) then
dim as strings.CsvWriter w = strings.CsvWriter( f )
w.WriteRow( row )
if w.Finish( ).IsOk( ) = false then print "write failed"
end ifA field is quoted when it holds the separator, a double quote, CR or LF, or starts or ends with a space or tab; a quote inside is doubled (RFC 4180). Records end with CRLF; CsvWriter( f, sep, false ) ends them with LF. Whatever JoinCsvRow writes, SplitCsv reads back as the same fields. The writer keeps the first failure: rows after it are not written, and Finish( ) reports it, or else the stream's Flush( ).
On the wide family SURROGATE PAIRS ARE KEPT INTACT. A ustring is UTF-16 by definition, so reversing its code units blindly would emit a low surrogate before its high one -- invalid UTF-16 every time an astral character is present, not occasionally.
On the byte family bytes reverse as bytes, which does mangle multi-byte UTF-8. A STRING has no declared encoding, so there is no pair to recognise and no way to know whether there would be one. Reverse UTF-8 text through the ustring overload.
Backslash escaping -- what makes a string safe to write between quotes in a config file, a log line, or generated source, and reversible afterwards.
\ " -> \\ \"
LF CR TAB NUL -> \n \r \t \0
other units below 32, and 127 -> \xHH
everything else, INCLUDING ALL NON-ASCII, passes throughNon-ASCII is left alone deliberately: escaping it would make a ustring unreadable and reduce a UTF-8 STRING to a wall of hex, and the job is to neutralise what breaks quoting, not to force the text to ASCII.
Unescape is the exact inverse. Unescape( Escape( s ) ) is s for every input, including one that already contains backslashes -- verified over every byte value 0..255. An unrecognised escape yields the escaped character ("\q" -> "q"); a trailing lone backslash is kept, since nothing follows it to unquote.
In tiko-rt the algorithms live in lib/rt/src/inc/ops-core.ti and are written ONCE, then instantiated twice -- lib/rt/src/str/ops.tiko at byte width, lib/rt/src/ustr/ops.tiko at 16-bit -- with ## token pasting. Two hand-maintained copies of a search loop drift, and the drift shows up as one width quietly disagreeing with the other on an edge case nobody tests twice.
Reached from tiko code through 'extern "C" declare ... alias' at the module's scope -- the pattern inc/string.bi already uses for FORMAT. Note that 'alias' alone is not enough inside a module: without the extern "C" the name is mangled through the module's chain, and a nested namespace does not inherit an enclosing extern either.
Split, SplitChars, Join, SplitCsv and SplitCsvRow are the only ones with tiko bodies, because their result is an Array( of T ) that the runtime cannot build. They are public members of the module, compiled once with it, so any number of files in a program can import std/strings and still link. The runtime side returns field boundaries rather than strings, so a split is two linear passes instead of the ParseCount-then-Parse-in-a-loop shape, which rescans from the start for every field and is quadratic.
MoneyFormat, from std/money, renders an amount the way the user's locale writes money. Assign a numeric value, set the properties you care about, read back the string.
#import "std/money"
using money
dim as MoneyFormat m = DecimalParse( "-1234.5" ).Value( )
print m.ToString( ) '' -$1,234.50 on en-CA
m.Negative = MONEY_NEG_PARENS
m.ThousandsSep = "" '' SUPPRESSED, not "use the locale"
print m.ToString( ) '' ($1234.50)
m.UseLocale( MONEY_F_THOUSANDSSEP ) '' back to the locale's separatorCurrency and Decimal HOLD money and the finance functions COMPUTE with it. Nothing displayed it. MoneyFormat is that half. It is documented in this library because what it does is build a string, and it ships in std/money beside Currency.
Everything the locale specifies -- symbol, decimal point, thousands separator, digit grouping, fraction digits, which side the symbol goes on, where the sign goes -- is used by default, and every one of them can be overridden or suppressed individually. Setting a property always marks it as yours, so m.Symbol = "" SUPPRESSES the symbol and only UseLocale gives it back; without that distinction suppression could not be expressed at all.
Windows reads GetLocaleInfoEx( LOCALE_NAME_USER_DEFAULT, ... ) and Linux and macOS read localeconv( ). Not one portable path, and both reasons are silent failures rather than errors: the Windows CRT is not the Region control panel and does not reliably see per-user overrides, and localeconv hands back bytes in the process code page, where the euro sign is one byte on a 1252 machine. std/money pulls in nothing from inc/win -- GetLocaleInfoEx sits behind _WIN32_WINNT >= &h0600 and inc/win/_mingw.bi defaults that to &h0502, so with the shipped headers it is not visible at all.
On Linux and macOS it asks for LC_MONETARY alone. Widening it to LC_ALL would move LC_NUMERIC too, and DecimalFromDouble parses snprintf( "%.14e" ) output -- under a German LC_NUMERIC every double assigned to a MoneyFormat would silently become an invalid Decimal. Formatting money must not change how the rest of the program reads numbers.
The thirty layout combinations behind MoneyCompose were generated from glibc, not transcribed: thirty synthetic locales built with localedef and formatted with strfmon(3), asserted one cell at a time.
Every entry, grouped by task. Text lists them beside the rest of the area.
| Function | What it does | From |
|---|---|---|
| Contains | Returns true if the text appears anywhere in the string. | std/strings |
| StartsWith | Returns true if the string begins with the given text. | std/strings |
| EndsWith | Returns true if the string ends with the given text. | std/strings |
| Tally | Counts how many times the text occurs, without overlaps. | std/strings |
| InstrChars | Returns the position of the first character from a set, or 0. | std/strings |
| TallyChars | Counts the characters that belong to a set. | std/strings |
| Function | What it does | From |
|---|---|---|
| IsBlank | Returns true if the string is empty or holds only whitespace. | std/strings |
| IsNumeric | Returns true if the string is a valid number, with spaces allowed around it. | std/strings |
| VerifySet | Returns the position of the first character not in a set, or 0 if all are. | std/strings |
| SpanOf | Counts the run of characters from a set that starts at a position. | std/strings |
| Function | What it does | From |
|---|---|---|
| Between | Returns the text between two delimiters. | std/strings |
| Extract | Returns the text before a match, or the whole rest if there is none. | std/strings |
| Remain | Returns the text after a match, or an empty string if there is none. | std/strings |
| ExtractChars | Returns the text before the first character from a set. | std/strings |
| RemainChars | Returns the text after the first character from a set. | std/strings |
| Function | What it does | From |
|---|---|---|
| MCase | Returns a title-case copy: each word capitalised, the rest lower case. | std/strings |
| Function | What it does | From |
|---|---|---|
| Shrink | Trims both ends and collapses each inner run of spaces to one. | std/strings |
| PadLeft | Pads on the left to a width, with spaces or a given character. | std/strings |
| PadRight | Pads on the right to a width, with spaces or a given character. | std/strings |
| PadCenter | Centres the string in a width, padding both sides. | std/strings |
| Function | What it does | From |
|---|---|---|
| Replace | Replaces every occurrence of the text with other text. | std/strings |
| ReplaceChars | Replaces each character from a set with one given character. | std/strings |
| Remove | Removes every occurrence of the text. | std/strings |
| RemoveChars | Removes every character that belongs to a set. | std/strings |
| RetainChars | Keeps only the characters that belong to a set. | std/strings |
| RemoveBetween | Removes a pair of delimiters and the text between them. | std/strings |
| InsertAt | Inserts text at a position. | std/strings |
| DeleteAt | Deletes a number of characters at a position. | std/strings |
| ClipLeft | Removes a number of characters from the start. | std/strings |
| ClipRight | Removes a number of characters from the end. | std/strings |
| Function | What it does | From |
|---|---|---|
| Split | Breaks a string into an Array at each delimiter. | std/strings |
| SplitChars | Breaks a string into an Array wherever a character from a set appears. | std/strings |
| Join | Joins an Array of strings into one, with a separator between them. | std/strings |
| SplitCsv | Parses CSV text into rows and fields, quoted fields included. | std/strings |
| SplitCsvRow | Parses one CSV record into an Array of fields. | std/strings |
| Function | What it does | From |
|---|---|---|
| Repeat | Returns a string repeated n times. | std/strings |
| Reverse | Returns the string with its characters in reverse order. | std/strings |
| Wrap | Surrounds the string with quotes or a given pair of delimiters. | std/strings |
| Unwrap | Removes one surrounding pair of quotes or delimiters, if both are there. | std/strings |
| Escape | Replaces quotes, backslashes and control characters with backslash escapes. | std/strings |
| Unescape | Turns backslash escapes back into the characters they stand for. | std/strings |
| Function | What it does | From |
|---|---|---|
| Format | Builds text from values with a format string such as "{:>6.2f}". | std/strings |
| Function | What it does | From |
|---|---|---|
| MoneyFormat | Holds an amount and prints it the way the user's locale writes money. | std/money |
| MoneySetLocale | Switches money formatting to a named locale; returns false if it is unknown. | std/money |
| MoneyLocaleName | Returns the name of the locale that money is formatted for. | std/money |
| MoneyLocaleIsValid | Returns false if the system rejected the money locale. | std/money |
| MoneyRefreshLocale | Rereads the locale's money settings after the user changes them. | std/money |
| MoneyCompose | Assembles a number, symbol and sign in one of the standard layouts. | std/money |
| MoneyLayoutFromICurrency | Converts a Windows positive-currency code into layout flags. | std/money |
| MoneyLayoutFromINegCurr | Converts a Windows negative-currency code into layout flags. | std/money |
tiko-lang · Home · Feature Index · Keyword Index · License
FreeBASIC manual pages reproduced under the GNU FDL 1.3.
- Generics
- Interfaces
- For Each
- Iterators
- Tuples
- Array Results
- Defer
- Lambdas
- Block Strings
- String Slices
- Embed
- Typed Variadics
- C Header Import
- Foreign Library Linking
- UString
- Fixed-Width Types
- Nullptr
- String Library
- Format
- Array
- Map
- Set
- LinkedList
- OrderedMap
- OrderedSet
- Sorting
- Optional
- Result
- Smart Pointers
- Date and Time (Chrono)
- Filesystem
- File Streams
- Config (INI and TOML)
- Codec
- Hash
- UUID
- OS
- Process
- Net
- HTTP and TLS
- Math Library
- Finance
- Logging
- Terminal TUI Library
- PsControls
- tkControls
- sys/win
- Goroutines and Channels
- LLVM Backend
- tiko build
- TIKO_COMPILER
- Variable Declarations
- Standard Data Types
- User Defined Types
- Procedures
- Operators
- Control Flow Statements
- Converting Data Types
- Array Functions
- String Functions
- Mathematical Functions
- Bit Manipulation
- Date and Time Functions
- File I/O Functions
- Console Functions
- User Input (Runtime Library)
- User Input Functions (Graphics Library)
- Screen Functions
- 2D Drawing Functions
- Graphics Library Reference
- Graphics Keyword List
- Memory Functions
- Threading Support Functions
- Error Handling Functions
- Operating System Functions
- Preprocessor
- Intrinsic Defines
- Miscellaneous Keywords
- Modularizing