Skip to content

Commit

Permalink
Bin, oct and hex literals (#183)
Browse files Browse the repository at this point in the history
* Add support for bin, oct and hex literals

* Update some places in the std lib where octal literals are better

* Add docs for number formats
  • Loading branch information
marcauberer authored Aug 8, 2022
1 parent d6b0fd9 commit c32c5ce
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 88 deletions.
2 changes: 1 addition & 1 deletion .run/Spice_run.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Spice_run" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O0 -ir ../../media/test-project/os-test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="Spice_run" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="Spice_run">
<configuration default="false" name="Spice_run" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O2 -ir ../../media/test-project/os-test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="Spice_run" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="Spice_run">
<envs>
<env name="RUN_TESTS" value="OFF" />
</envs>
Expand Down
47 changes: 47 additions & 0 deletions docs/docs/language/number-formats.md
Original file line number Diff line number Diff line change
@@ -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;
```
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 1 addition & 7 deletions media/test-project/os-test.spice
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,5 @@ f<int> main() {
}*/

f<int> 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);
}
19 changes: 10 additions & 9 deletions src/Spice.g4
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
70 changes: 62 additions & 8 deletions src/parser/AstBuilderVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1197,24 +1197,21 @@ std::any AstBuilderVisitor::visitPrimitiveValue(SpiceParser::PrimitiveValueConte
primitiveValueNode->data.doubleValue = std::stod(t->toString());
} else if (auto t = dynamic_cast<antlr4::tree::TerminalNode *>(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<antlr4::tree::TerminalNode *>(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<antlr4::tree::TerminalNode *>(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<antlr4::tree::TerminalNode *>(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<antlr4::tree::TerminalNode *>(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<antlr4::tree::TerminalNode *>(subTree); t->getSymbol()->getType() == SpiceParser::TRUE) {
primitiveValueNode->type = PrimitiveValueNode::TY_BOOL;
primitiveValueNode->data.boolValue = true;
Expand Down Expand Up @@ -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<int32_t(const std::string &, int)> 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<int16_t(const std::string &, int)> 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<int64_t(const std::string &, int)> 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 <typename T> T AstBuilderVisitor::parseNumeric(const std::string &input, std::function<T(const std::string &, int)> 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);
}
7 changes: 7 additions & 0 deletions src/parser/AstBuilderVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <SpiceVisitor.h>

#include <functional>
#include <utility>

// Forward declarations
Expand Down Expand Up @@ -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 <typename T> T static parseNumeric(const std::string &input, std::function<T(const std::string &, int)> cb);
static void replaceEscapeChars(std::string &string);
};
34 changes: 17 additions & 17 deletions std/io/dir_linux.spice
Original file line number Diff line number Diff line change
@@ -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
Expand Down
36 changes: 18 additions & 18 deletions std/io/dir_windows.spice
Original file line number Diff line number Diff line change
@@ -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
Expand Down
28 changes: 14 additions & 14 deletions std/io/file_linux.spice
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -50,7 +50,7 @@ public f<int> 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
Expand Down Expand Up @@ -84,7 +84,7 @@ public f<int> File.readChar() {

/**
* Writes a single character to the file.
*
*
* @return Result code of the write operation: 0 = successful, -1 = failed
*/
public f<int> File.writeChar(char value) {
Expand Down Expand Up @@ -169,7 +169,7 @@ public f<long> 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<bool> fileExists(string path) {
Expand All @@ -178,7 +178,7 @@ public f<bool> fileExists(string path) {

/**
* Checks if the read permissions to a file are given.
*
*
* @return Readable / not readable
*/
public f<bool> fileReadable(string path) {
Expand All @@ -187,7 +187,7 @@ public f<bool> fileReadable(string path) {

/**
* Checks if the write permissions to a file are given.
*
*
* @return Writable / not writable
*/
public f<bool> fileWritable(string path) {
Expand All @@ -196,7 +196,7 @@ public f<bool> fileWritable(string path) {

/**
* Checks if the execute permissions to a file are given.
*
*
* @return Executable / not executable
*/
public f<bool> fileExecutable(string path) {
Expand Down
Loading

0 comments on commit c32c5ce

Please sign in to comment.