Implemented numbers with different bases

This commit is contained in:
Eduard Urbach 2024-07-29 00:30:26 +02:00
parent f1d4e65c1b
commit d5953649d9
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
11 changed files with 165 additions and 12 deletions

View File

@ -112,10 +112,10 @@ This is what generates expressions from tokens.
- [x] Variable lifetimes - [x] Variable lifetimes
- [x] Branches - [x] Branches
- [x] Loops - [x] Loops
- [x] Hexadecimal, octal and binary literals
- [ ] Data structures - [ ] Data structures
- [ ] Type system - [ ] Type system
- [ ] Type operator: `|` (`User | Error`) - [ ] Type operator: `|` (`User | Error`)
- [ ] Hexadecimal, octal and binary literals
- [ ] Error handling - [ ] Error handling
- [ ] Multiple return values - [ ] Multiple return values
- [ ] Threading library - [ ] Threading library

View File

@ -1,7 +1,7 @@
import sys import sys
alloc(length) { alloc(length) {
return sys.mmap(0, length, 3, 290) return sys.mmap(0, length, 0x1|0x2, 0x02|0x20|0x100)
} }
free(address, length) { free(address, length) {

View File

@ -1,8 +1,6 @@
package core package core
import ( import (
"fmt"
"git.akyoto.dev/cli/q/src/build/asm" "git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/ast" "git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/build/cpu" "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 len(node.Children) == 1 {
if !node.Token.IsUnaryOperator() { if !node.Token.IsUnaryOperator() {
fmt.Println(node.String(f.File.Bytes), node.Token.Kind)
return errors.New(errors.MissingOperand, f.File, node.Token.End()) return errors.New(errors.MissingOperand, f.File, node.Token.End())
} }

View File

@ -2,6 +2,7 @@ package core
import ( import (
"strconv" "strconv"
"strings"
"unicode/utf8" "unicode/utf8"
"git.akyoto.dev/cli/q/src/build/errors" "git.akyoto.dev/cli/q/src/build/errors"
@ -12,7 +13,24 @@ import (
func (f *Function) Number(t token.Token) (int, byte, error) { func (f *Function) Number(t token.Token) (int, byte, error) {
switch t.Kind { switch t.Kind {
case token.Number: 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 return number, 8, err
case token.Rune: case token.Rune:

View File

@ -94,6 +94,38 @@ func Tokenize(buffer []byte) List {
tokens = append(tokens, Token{Kind: kind, Position: start, Length: Length(end - start)}) tokens = append(tokens, Token{Kind: kind, Position: start, Length: Length(end - start)})
continue 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: default:
if isIdentifierStart(buffer[i]) { if isIdentifierStart(buffer[i]) {
position := i position := i
@ -123,11 +155,11 @@ func Tokenize(buffer []byte) List {
continue continue
} }
if isNumber(buffer[i]) { if isDigit(buffer[i]) {
position := i position := i
i++ i++
for i < Position(len(buffer)) && isNumber(buffer[i]) { for i < Position(len(buffer)) && isDigit(buffer[i]) {
i++ i++
} }
@ -235,7 +267,7 @@ func Tokenize(buffer []byte) List {
} }
func isIdentifier(c byte) bool { func isIdentifier(c byte) bool {
return isLetter(c) || isNumber(c) || c == '_' return isLetter(c) || isDigit(c) || c == '_'
} }
func isIdentifierStart(c byte) bool { func isIdentifierStart(c byte) bool {
@ -246,8 +278,20 @@ func isLetter(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
} }
func isNumber(c byte) bool { func isDigit(c byte) bool {
return (c >= '0' && c <= '9') 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 { func isOperator(c byte) bool {

View File

@ -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) { func TestOperatorAssign(t *testing.T) {
tokens := token.Tokenize([]byte(`a += b -= c *= d /= e &= f |= g ^= h <<= i >>= j`)) tokens := token.Tokenize([]byte(`a += b -= c *= d /= e &= f |= g ^= h <<= i >>= j`))

10
tests/programs/binary.q Normal file
View File

@ -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
}

View File

@ -0,0 +1,8 @@
main() {
assert 0x0 == 0
assert 0x1 == 1
assert 0xA == 10
assert 0x10 == 16
assert 0xFF == 255
assert 0x1000 == 4096
}

8
tests/programs/octal.q Normal file
View File

@ -0,0 +1,8 @@
main() {
assert 0o0 == 0
assert 0o1 == 1
assert 0o7 == 7
assert 0o10 == 8
assert 0o100 == 64
assert 0o755 == 493
}

View File

@ -22,6 +22,9 @@ var programs = []struct {
{"reuse", "", "", 0}, {"reuse", "", "", 0},
{"reassign", "", "", 0}, {"reassign", "", "", 0},
{"return", "", "", 0}, {"return", "", "", 0},
{"binary", "", "", 0},
{"octal", "", "", 0},
{"hexadecimal", "", "", 0},
{"math", "", "", 0}, {"math", "", "", 0},
{"precedence", "", "", 0}, {"precedence", "", "", 0},
{"bitwise-and", "", "", 0}, {"bitwise-and", "", "", 0},
@ -30,7 +33,7 @@ var programs = []struct {
{"shift", "", "", 0}, {"shift", "", "", 0},
{"modulo", "", "", 0}, {"modulo", "", "", 0},
{"modulo-assign", "", "", 0}, {"modulo-assign", "", "", 0},
{"division-split", "", "", 0}, {"div-split", "", "", 0},
{"negative", "", "", 0}, {"negative", "", "", 0},
{"negation", "", "", 0}, {"negation", "", "", 0},
{"square-sum", "", "", 0}, {"square-sum", "", "", 0},