diff --git a/README.md b/README.md index e79d420..f03c58a 100644 --- a/README.md +++ b/README.md @@ -112,10 +112,10 @@ This is what generates expressions from tokens. - [x] Variable lifetimes - [x] Branches - [x] Loops +- [x] Hexadecimal, octal and binary literals - [ ] Data structures - [ ] Type system - [ ] Type operator: `|` (`User | Error`) -- [ ] Hexadecimal, octal and binary literals - [ ] Error handling - [ ] Multiple return values - [ ] Threading library diff --git a/lib/mem/alloc.q b/lib/mem/alloc.q index e03ece3..3a6347c 100644 --- a/lib/mem/alloc.q +++ b/lib/mem/alloc.q @@ -1,7 +1,7 @@ import sys alloc(length) { - return sys.mmap(0, length, 3, 290) + return sys.mmap(0, length, 0x1|0x2, 0x02|0x20|0x100) } free(address, length) { diff --git a/src/build/core/ExpressionToRegister.go b/src/build/core/ExpressionToRegister.go index a0ab61f..4c1d69c 100644 --- a/src/build/core/ExpressionToRegister.go +++ b/src/build/core/ExpressionToRegister.go @@ -1,8 +1,6 @@ package core import ( - "fmt" - "git.akyoto.dev/cli/q/src/build/asm" "git.akyoto.dev/cli/q/src/build/ast" "git.akyoto.dev/cli/q/src/build/cpu" @@ -28,7 +26,6 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp if len(node.Children) == 1 { if !node.Token.IsUnaryOperator() { - fmt.Println(node.String(f.File.Bytes), node.Token.Kind) return errors.New(errors.MissingOperand, f.File, node.Token.End()) } diff --git a/src/build/core/Number.go b/src/build/core/Number.go index a3d2f47..2cc05a1 100644 --- a/src/build/core/Number.go +++ b/src/build/core/Number.go @@ -2,6 +2,7 @@ package core import ( "strconv" + "strings" "unicode/utf8" "git.akyoto.dev/cli/q/src/build/errors" @@ -12,7 +13,24 @@ import ( func (f *Function) Number(t token.Token) (int, byte, error) { switch t.Kind { case token.Number: - number, err := strconv.Atoi(t.Text(f.File.Bytes)) + digits := t.Text(f.File.Bytes) + + if strings.HasPrefix(digits, "0x") { + number, err := strconv.ParseInt(digits[2:], 16, 64) + return int(number), 8, err + } + + if strings.HasPrefix(digits, "0o") { + number, err := strconv.ParseInt(digits[2:], 8, 64) + return int(number), 8, err + } + + if strings.HasPrefix(digits, "0b") { + number, err := strconv.ParseInt(digits[2:], 2, 64) + return int(number), 8, err + } + + number, err := strconv.Atoi(digits) return number, 8, err case token.Rune: diff --git a/src/build/token/Tokenize.go b/src/build/token/Tokenize.go index 9e0f4fa..929eff3 100644 --- a/src/build/token/Tokenize.go +++ b/src/build/token/Tokenize.go @@ -94,6 +94,38 @@ func Tokenize(buffer []byte) List { tokens = append(tokens, Token{Kind: kind, Position: start, Length: Length(end - start)}) continue + case '0': + position := i + i++ + + if i >= Position(len(buffer)) { + tokens = append(tokens, Token{Kind: Number, Position: position, Length: 1}) + break + } + + filter := isDigit + + switch buffer[i] { + case 'x': + i++ + filter = isHexDigit + + case 'b': + i++ + filter = isBinaryDigit + + case 'o': + i++ + filter = isOctalDigit + } + + for i < Position(len(buffer)) && filter(buffer[i]) { + i++ + } + + tokens = append(tokens, Token{Kind: Number, Position: position, Length: Length(i - position)}) + continue + default: if isIdentifierStart(buffer[i]) { position := i @@ -123,11 +155,11 @@ func Tokenize(buffer []byte) List { continue } - if isNumber(buffer[i]) { + if isDigit(buffer[i]) { position := i i++ - for i < Position(len(buffer)) && isNumber(buffer[i]) { + for i < Position(len(buffer)) && isDigit(buffer[i]) { i++ } @@ -235,7 +267,7 @@ func Tokenize(buffer []byte) List { } func isIdentifier(c byte) bool { - return isLetter(c) || isNumber(c) || c == '_' + return isLetter(c) || isDigit(c) || c == '_' } func isIdentifierStart(c byte) bool { @@ -246,8 +278,20 @@ func isLetter(c byte) bool { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') } -func isNumber(c byte) bool { - return (c >= '0' && c <= '9') +func isDigit(c byte) bool { + return c >= '0' && c <= '9' +} + +func isHexDigit(c byte) bool { + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') +} + +func isBinaryDigit(c byte) bool { + return c == '0' || c == '1' +} + +func isOctalDigit(c byte) bool { + return c >= '0' && c <= '7' } func isOperator(c byte) bool { diff --git a/src/build/token/Tokenize_test.go b/src/build/token/Tokenize_test.go index 86ad7be..efc743c 100644 --- a/src/build/token/Tokenize_test.go +++ b/src/build/token/Tokenize_test.go @@ -179,6 +179,71 @@ func TestNegateNumber(t *testing.T) { } } +func TestBinaryNumber(t *testing.T) { + tokens := token.Tokenize([]byte(`0b1010`)) + + expected := []token.Kind{ + token.Number, + token.EOF, + } + + for i, kind := range expected { + assert.Equal(t, tokens[i].Kind, kind) + } +} + +func TestOctalNumber(t *testing.T) { + tokens := token.Tokenize([]byte(`0o755`)) + + expected := []token.Kind{ + token.Number, + token.EOF, + } + + for i, kind := range expected { + assert.Equal(t, tokens[i].Kind, kind) + } +} + +func TestHexadecimalNumber(t *testing.T) { + tokens := token.Tokenize([]byte(`0xCAFE`)) + + expected := []token.Kind{ + token.Number, + token.EOF, + } + + for i, kind := range expected { + assert.Equal(t, tokens[i].Kind, kind) + } +} + +func TestStandaloneZero(t *testing.T) { + tokens := token.Tokenize([]byte(`0`)) + + expected := []token.Kind{ + token.Number, + token.EOF, + } + + for i, kind := range expected { + assert.Equal(t, tokens[i].Kind, kind) + } +} + +func TestLeadingZero(t *testing.T) { + tokens := token.Tokenize([]byte(`0123`)) + + expected := []token.Kind{ + token.Number, + token.EOF, + } + + for i, kind := range expected { + assert.Equal(t, tokens[i].Kind, kind) + } +} + func TestOperatorAssign(t *testing.T) { tokens := token.Tokenize([]byte(`a += b -= c *= d /= e &= f |= g ^= h <<= i >>= j`)) diff --git a/tests/programs/binary.q b/tests/programs/binary.q new file mode 100644 index 0000000..0c35196 --- /dev/null +++ b/tests/programs/binary.q @@ -0,0 +1,10 @@ +main() { + assert 0b0 == 0 + assert 0b1 == 1 + assert 0b10 == 2 + assert 0b11 == 3 + assert 0b100 == 4 + assert 0b101 == 5 + assert 0b110 == 6 + assert 0b111 == 7 +} \ No newline at end of file diff --git a/tests/programs/division-split.q b/tests/programs/div-split.q similarity index 100% rename from tests/programs/division-split.q rename to tests/programs/div-split.q diff --git a/tests/programs/hexadecimal.q b/tests/programs/hexadecimal.q new file mode 100644 index 0000000..78de4d5 --- /dev/null +++ b/tests/programs/hexadecimal.q @@ -0,0 +1,8 @@ +main() { + assert 0x0 == 0 + assert 0x1 == 1 + assert 0xA == 10 + assert 0x10 == 16 + assert 0xFF == 255 + assert 0x1000 == 4096 +} \ No newline at end of file diff --git a/tests/programs/octal.q b/tests/programs/octal.q new file mode 100644 index 0000000..b58fa8e --- /dev/null +++ b/tests/programs/octal.q @@ -0,0 +1,8 @@ +main() { + assert 0o0 == 0 + assert 0o1 == 1 + assert 0o7 == 7 + assert 0o10 == 8 + assert 0o100 == 64 + assert 0o755 == 493 +} \ No newline at end of file diff --git a/tests/programs_test.go b/tests/programs_test.go index 932c085..2bead24 100644 --- a/tests/programs_test.go +++ b/tests/programs_test.go @@ -22,6 +22,9 @@ var programs = []struct { {"reuse", "", "", 0}, {"reassign", "", "", 0}, {"return", "", "", 0}, + {"binary", "", "", 0}, + {"octal", "", "", 0}, + {"hexadecimal", "", "", 0}, {"math", "", "", 0}, {"precedence", "", "", 0}, {"bitwise-and", "", "", 0}, @@ -30,7 +33,7 @@ var programs = []struct { {"shift", "", "", 0}, {"modulo", "", "", 0}, {"modulo-assign", "", "", 0}, - {"division-split", "", "", 0}, + {"div-split", "", "", 0}, {"negative", "", "", 0}, {"negation", "", "", 0}, {"square-sum", "", "", 0},