diff --git a/.run/Spice_run.run.xml b/.run/Spice_run.run.xml index 31edb635f..673269bd1 100644 --- a/.run/Spice_run.run.xml +++ b/.run/Spice_run.run.xml @@ -1,5 +1,5 @@ - + diff --git a/docs/docs/language/number-formats.md b/docs/docs/language/number-formats.md new file mode 100644 index 000000000..c4dbb7349 --- /dev/null +++ b/docs/docs/language/number-formats.md @@ -0,0 +1,47 @@ +--- +title: Number formats +--- + +Spice supports four different number formats to cover as many use-cases as possible. + +## Decimal format (base 10) + +Decimal numbers can be written in two ways in Spice. The implicit way: + +```spice +int i32 = 123; +short i16 = 123s; +long i64 = 123l; +``` + +or the explicit way: + +```spice +int i32 = 0d123; +short i16 = 0d123s; +long i64 = 0d123l; +``` + +## Binary format (base 2) + +```spice +int i32 = 0b1010; +short i16 = 0b1010s; +long i64 = 0b1010l; +``` + +## Hexadecimal format (base 16) + +```spice +int i32 = 0xA76E; +short i16 = 0xA76Es; +long i64 = 0xA76El; +``` + +## Octal format (base 8) + +```spice +int i32 = 0o1274; +short i16 = 0o1274s; +long i64 = 0o1274l; +``` \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 58bbb8898..20826a88c 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -88,6 +88,7 @@ nav: - language/constructors-destructors.md - language/generics.md #- language/threads.md + - language/number-formats.md - language/operator-precedence.md - contributing.md diff --git a/media/test-project/os-test.spice b/media/test-project/os-test.spice index 806ffc44f..aacb8d5d2 100644 --- a/media/test-project/os-test.spice +++ b/media/test-project/os-test.spice @@ -11,11 +11,5 @@ f main() { }*/ f main() { - int[10][10] a; - for int i = 0; i < 10; i++ { - for int j = 0; j < 10; j++ { - a[i][j] = i * j; - } - } - printf("Cell [1,3]: %d", a[1][3]); + printf("Test: %d\n", 0o0000777); } \ No newline at end of file diff --git a/src/Spice.g4 b/src/Spice.g4 index de8fe65dd..661a29294 100644 --- a/src/Spice.g4 +++ b/src/Spice.g4 @@ -170,18 +170,19 @@ DOT: '.'; ELLIPSIS: '...'; // Regex tokens +DOUBLE_LIT: [0-9]*[.][0-9]+; +INT_LIT: NUM_LIT; +SHORT_LIT: NUM_LIT 's'; +LONG_LIT: NUM_LIT 'l'; CHAR_LIT: '\'' (~['\\\r\n] | '\\' (. | EOF)) '\''; STRING_LIT: '"' (~["\\\r\n] | '\\' (. | EOF))* '"'; -INT_LIT: NONZERO_DIGIT DIGIT* | ZERO; -DOUBLE_LIT: DIGIT+ DOT DIGIT+; -SHORT_LIT: INT_LIT 's'; -LONG_LIT: INT_LIT 'l'; -IDENTIFIER: NONDIGIT (NONDIGIT | DIGIT)*; +IDENTIFIER: [a-zA-Z_][a-zA-Z0-9_]*; -fragment ZERO: [0]; -fragment DIGIT: [0-9]; -fragment NONZERO_DIGIT: [1-9]; -fragment NONDIGIT: [a-zA-Z_]; +fragment NUM_LIT: DEC_LIT | BIN_LIT | HEX_LIT | OCT_LIT; +fragment DEC_LIT: ([0][dD])?[0-9]+; +fragment BIN_LIT: [0][bB][01]+; +fragment HEX_LIT: [0][xXhH][0-9a-fA-F]+; +fragment OCT_LIT: [0][oO][0-7]+; // Skipped tokens BLOCK_COMMENT: '/*' .*? '*/' -> skip; diff --git a/src/parser/AstBuilderVisitor.cpp b/src/parser/AstBuilderVisitor.cpp index f26dfed89..91992632b 100644 --- a/src/parser/AstBuilderVisitor.cpp +++ b/src/parser/AstBuilderVisitor.cpp @@ -1197,24 +1197,21 @@ std::any AstBuilderVisitor::visitPrimitiveValue(SpiceParser::PrimitiveValueConte primitiveValueNode->data.doubleValue = std::stod(t->toString()); } else if (auto t = dynamic_cast(subTree); t->getSymbol()->getType() == SpiceParser::INT_LIT) { primitiveValueNode->type = PrimitiveValueNode::TY_INT; - primitiveValueNode->data.intValue = std::stoi(t->toString()); + primitiveValueNode->data.intValue = parseInt(t->toString()); } else if (auto t = dynamic_cast(subTree); t->getSymbol()->getType() == SpiceParser::SHORT_LIT) { primitiveValueNode->type = PrimitiveValueNode::TY_SHORT; - primitiveValueNode->data.shortValue = (short)std::stoi(t->toString()); + primitiveValueNode->data.shortValue = parseShort(t->toString()); } else if (auto t = dynamic_cast(subTree); t->getSymbol()->getType() == SpiceParser::LONG_LIT) { primitiveValueNode->type = PrimitiveValueNode::TY_LONG; - primitiveValueNode->data.longValue = std::stoll(t->toString()); + primitiveValueNode->data.longValue = parseLong(t->toString()); } else if (auto t = dynamic_cast(subTree); t->getSymbol()->getType() == SpiceParser::CHAR_LIT) { primitiveValueNode->type = PrimitiveValueNode::TY_CHAR; - primitiveValueNode->data.charValue = ctx->CHAR_LIT()->toString()[1]; + primitiveValueNode->data.charValue = parseChar(ctx->CHAR_LIT()->toString()); } else if (auto t = dynamic_cast(subTree); t->getSymbol()->getType() == SpiceParser::STRING_LIT) { primitiveValueNode->type = PrimitiveValueNode::TY_STRING; - std::string strValue = ctx->STRING_LIT()->toString(); - strValue = strValue.substr(1, strValue.size() - 2); - replaceEscapeChars(strValue); - primitiveValueNode->data.stringValue = strValue; + primitiveValueNode->data.stringValue = parseString(ctx->STRING_LIT()->toString()); } else if (auto t = dynamic_cast(subTree); t->getSymbol()->getType() == SpiceParser::TRUE) { primitiveValueNode->type = PrimitiveValueNode::TY_BOOL; primitiveValueNode->data.boolValue = true; @@ -1479,4 +1476,61 @@ void AstBuilderVisitor::replaceEscapeChars(std::string &string) { CommonUtil::replaceAll(string, "\\v", "\v"); CommonUtil::replaceAll(string, "\\'", "\'"); CommonUtil::replaceAll(string, "\\?", "\?"); +} + +int32_t AstBuilderVisitor::parseInt(const std::string &input) { + std::function cb = [](const std::string &substr, int base) { + return std::stoi(substr, nullptr, base); + }; + return parseNumeric(input, cb); +} +int16_t AstBuilderVisitor::parseShort(const std::string &input) { + std::function cb = [](const std::string &substr, int base) { + return (int16_t)std::stoi(substr, nullptr, base); + }; + return parseNumeric(input, cb); +} + +int64_t AstBuilderVisitor::parseLong(const std::string &input) { + std::function cb = [](const std::string &substr, int base) { + return std::stoll(substr, nullptr, base); + }; + return parseNumeric(input, cb); +} + +int8_t AstBuilderVisitor::parseChar(const std::string &input) { return input[1]; } + +std::string AstBuilderVisitor::parseString(std::string input) { + input = input.substr(1, input.size() - 2); + replaceEscapeChars(input); + return input; +} + +template T AstBuilderVisitor::parseNumeric(const std::string &input, std::function cb) { + if (input.length() >= 3) { + char c1 = input[0]; + char c2 = input[1]; + std::string substr = input.substr(2); + if (c1 == '0') { + switch (c2) { + case 'd': + case 'D': + return cb(substr, 10); + case 'b': + case 'B': + return cb(substr, 2); + case 'h': + case 'H': + case 'x': + case 'X': + return cb(substr, 16); + case 'o': + case 'O': + return cb(substr, 8); + default: + return cb(input, 10); + } + } + } + return cb(input, 10); } \ No newline at end of file diff --git a/src/parser/AstBuilderVisitor.h b/src/parser/AstBuilderVisitor.h index 078ed3c16..5391df73f 100644 --- a/src/parser/AstBuilderVisitor.h +++ b/src/parser/AstBuilderVisitor.h @@ -4,6 +4,7 @@ #include +#include #include // Forward declarations @@ -86,5 +87,11 @@ class AstBuilderVisitor : public SpiceVisitor { std::string fileName; // Private methods + static int32_t parseInt(const std::string &input); + static int16_t parseShort(const std::string &input); + static int64_t parseLong(const std::string &input); + static int8_t parseChar(const std::string &input); + static std::string parseString(std::string input); + template T static parseNumeric(const std::string &input, std::function cb); static void replaceEscapeChars(std::string &string); }; \ No newline at end of file diff --git a/std/io/dir_linux.spice b/std/io/dir_linux.spice index a7a1c7741..a787a74c6 100644 --- a/std/io/dir_linux.spice +++ b/std/io/dir_linux.spice @@ -1,32 +1,32 @@ //import "std/os/os" as os; // File permission modes -public const int MODE_ALL_RWX = 511; // Decimal for octal: 0000777 -public const int MODE_ALL_RW = 438; // Decimal for octal: 0000666 -public const int MODE_ALL_R = 292; // Decimal for octal: 0000444 +public const int MODE_ALL_RWX = 0o0000777; +public const int MODE_ALL_RW = 0o0000666; +public const int MODE_ALL_R = 0o0000444; -public const int MODE_OWNER_RWX = 448; // Decimal for octal: 0000700 -public const int MODE_OWNER_R = 256; // Decimal for octal: 0000400 -public const int MODE_OWNER_W = 128; // Decimal for octal: 0000200 -public const int MODE_OWNER_X = 64; // Decimal for octal: 0000100 +public const int MODE_OWNER_RWX = 0o0000700; +public const int MODE_OWNER_R = 0o0000400; +public const int MODE_OWNER_W = 0o0000200; +public const int MODE_OWNER_X = 0o0000100; -public const int MODE_GROUP_RWX = 56; // Decimal for octal: 0000070 -public const int MODE_GROUP_R = 32; // Decimal for octal: 0000040 -public const int MODE_GROUP_W = 16; // Decimal for octal: 0000020 -public const int MODE_GROUP_X = 8; // Decimal for octal: 0000010 +public const int MODE_GROUP_RWX = 0o0000070; +public const int MODE_GROUP_R = 0o0000040; +public const int MODE_GROUP_W = 0o0000020; +public const int MODE_GROUP_X = 0o0000010; -public const int MODE_OTHER_RWX = 7; // Decimal for octal: 0000007 -public const int MODE_OTHER_R = 4; // Decimal for octal: 0000004 -public const int MODE_OTHER_W = 2; // Decimal for octal: 0000002 -public const int MODE_OTHER_X = 1; // Decimal for octal: 0000001 +public const int MODE_OTHER_RWX = 0o0000007; +public const int MODE_OTHER_R = 0o0000004; +public const int MODE_OTHER_W = 0o0000002; +public const int MODE_OTHER_X = 0o0000001; const int F_OK = 0; // File existence const int X_OK = 1; // Can execute const int W_OK = 2; // Can write const int R_OK = 4; // Can read -const int IF_MT = 61440; // Decimal for octal: 0170000 -const int IF_DIR = 16384; // Decimal for octal: 0040000 +const int IF_MT = 0o0170000; +const int IF_DIR = 0o0040000; type FileStat struct { // Total size: 46 bytes unsigned long st_dev // ID of the device containing file diff --git a/std/io/dir_windows.spice b/std/io/dir_windows.spice index 2159ed92c..ce9e26429 100644 --- a/std/io/dir_windows.spice +++ b/std/io/dir_windows.spice @@ -1,24 +1,24 @@ //import "std/os/os" as os; // File permission modes -public const int MODE_ALL_RWX = 511; // Decimal for octal: 0000777 -public const int MODE_ALL_RW = 438; // Decimal for octal: 0000666 -public const int MODE_ALL_R = 292; // Decimal for octal: 0000444 - -public const int MODE_OWNER_RWX = 448; // Decimal for octal: 0000700 -public const int MODE_OWNER_R = 256; // Decimal for octal: 0000400 -public const int MODE_OWNER_W = 128; // Decimal for octal: 0000200 -public const int MODE_OWNER_X = 64; // Decimal for octal: 0000100 - -public const int MODE_GROUP_RWX = 56; // Decimal for octal: 0000070 -public const int MODE_GROUP_R = 32; // Decimal for octal: 0000040 -public const int MODE_GROUP_W = 16; // Decimal for octal: 0000020 -public const int MODE_GROUP_X = 8; // Decimal for octal: 0000010 - -public const int MODE_OTHER_RWX = 7; // Decimal for octal: 0000007 -public const int MODE_OTHER_R = 4; // Decimal for octal: 0000004 -public const int MODE_OTHER_W = 2; // Decimal for octal: 0000002 -public const int MODE_OTHER_X = 1; // Decimal for octal: 0000001 +public const int MODE_ALL_RWX = 0o0000777; +public const int MODE_ALL_RW = 0o0000666; +public const int MODE_ALL_R = 0o0000444; + +public const int MODE_OWNER_RWX = 0o0000700; +public const int MODE_OWNER_R = 0o0000400; +public const int MODE_OWNER_W = 0o0000200; +public const int MODE_OWNER_X = 0o0000100; + +public const int MODE_GROUP_RWX = 0o0000070; +public const int MODE_GROUP_R = 0o0000040; +public const int MODE_GROUP_W = 0o0000020; +public const int MODE_GROUP_X = 0o0000010; + +public const int MODE_OTHER_RWX = 0o0000007; +public const int MODE_OTHER_R = 0o0000004; +public const int MODE_OTHER_W = 0o0000002; +public const int MODE_OTHER_X = 0o0000001; const int F_OK = 0; // File existence const int X_OK = 1; // Can execute diff --git a/std/io/file_linux.spice b/std/io/file_linux.spice index 56003a7c0..20d7c02c2 100644 --- a/std/io/file_linux.spice +++ b/std/io/file_linux.spice @@ -1,13 +1,13 @@ // File open modes -public const string MODE_READ = "r"; -public const string MODE_WRITE = "w"; -public const string MODE_APPEND = "a"; -public const string MODE_READ_WRITE = "r+"; -public const string MODE_READ_WRITE_OVERWRITE = "w+"; -public const string MODE_READ_WRITE_APPEND = "a+"; +public const string MODE_READ = "r"; +public const string MODE_WRITE = "w"; +public const string MODE_APPEND = "a"; +public const string MODE_READ_WRITE = "r+"; +public const string MODE_READ_WRITE_OVERWRITE = "w+"; +public const string MODE_READ_WRITE_APPEND = "a+"; -const int MODE_CREATE = 64; // Decimal for octal: 100 -const int MODE_RDWR = 2; // Decimal for octal: 2 +const int MODE_CREATE = 0o100; +const int MODE_RDWR = 0o002; const int F_OK = 0; // File existence const int X_OK = 1; // Can execute @@ -50,7 +50,7 @@ public f createFile(string path) { /** * Opens a (new) file at the specified path with the specified mode. - * + * * There are predefined constants for the mode available: * MODE_READ, MODE_WRITE, MODE_APPEND, * MODE_READ_WRITE, MODE_READ_WRITE_OVERWRITE, MODE_READ_WRITE_APPEND @@ -84,7 +84,7 @@ public f File.readChar() { /** * Writes a single character to the file. - * + * * @return Result code of the write operation: 0 = successful, -1 = failed */ public f File.writeChar(char value) { @@ -169,7 +169,7 @@ public f getFileSize(string path) { /** * Checks if a file exists. The function also returns true if the specified path points to a directory. - * + * * @return Existing / not existing */ public f fileExists(string path) { @@ -178,7 +178,7 @@ public f fileExists(string path) { /** * Checks if the read permissions to a file are given. - * + * * @return Readable / not readable */ public f fileReadable(string path) { @@ -187,7 +187,7 @@ public f fileReadable(string path) { /** * Checks if the write permissions to a file are given. - * + * * @return Writable / not writable */ public f fileWritable(string path) { @@ -196,7 +196,7 @@ public f fileWritable(string path) { /** * Checks if the execute permissions to a file are given. - * + * * @return Executable / not executable */ public f fileExecutable(string path) { diff --git a/std/io/file_windows.spice b/std/io/file_windows.spice index 56003a7c0..2fa892745 100644 --- a/std/io/file_windows.spice +++ b/std/io/file_windows.spice @@ -1,13 +1,13 @@ // File open modes -public const string MODE_READ = "r"; -public const string MODE_WRITE = "w"; -public const string MODE_APPEND = "a"; -public const string MODE_READ_WRITE = "r+"; -public const string MODE_READ_WRITE_OVERWRITE = "w+"; -public const string MODE_READ_WRITE_APPEND = "a+"; +public const string MODE_READ = "r"; +public const string MODE_WRITE = "w"; +public const string MODE_APPEND = "a"; +public const string MODE_READ_WRITE = "r+"; +public const string MODE_READ_WRITE_OVERWRITE = "w+"; +public const string MODE_READ_WRITE_APPEND = "a+"; -const int MODE_CREATE = 64; // Decimal for octal: 100 -const int MODE_RDWR = 2; // Decimal for octal: 2 +const int MODE_CREATE = 0x100; +const int MODE_RDWR = 0x002; const int F_OK = 0; // File existence const int X_OK = 1; // Can execute @@ -50,7 +50,7 @@ public f createFile(string path) { /** * Opens a (new) file at the specified path with the specified mode. - * + * * There are predefined constants for the mode available: * MODE_READ, MODE_WRITE, MODE_APPEND, * MODE_READ_WRITE, MODE_READ_WRITE_OVERWRITE, MODE_READ_WRITE_APPEND @@ -84,7 +84,7 @@ public f File.readChar() { /** * Writes a single character to the file. - * + * * @return Result code of the write operation: 0 = successful, -1 = failed */ public f File.writeChar(char value) { @@ -169,7 +169,7 @@ public f getFileSize(string path) { /** * Checks if a file exists. The function also returns true if the specified path points to a directory. - * + * * @return Existing / not existing */ public f fileExists(string path) { @@ -178,7 +178,7 @@ public f fileExists(string path) { /** * Checks if the read permissions to a file are given. - * + * * @return Readable / not readable */ public f fileReadable(string path) { @@ -187,7 +187,7 @@ public f fileReadable(string path) { /** * Checks if the write permissions to a file are given. - * + * * @return Writable / not writable */ public f fileWritable(string path) { @@ -196,7 +196,7 @@ public f fileWritable(string path) { /** * Checks if the execute permissions to a file are given. - * + * * @return Executable / not executable */ public f fileExecutable(string path) {