From c8824e699aed04b905867e95b2ea35e544cdf6bd Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Wed, 31 Jul 2024 17:50:31 +0200 Subject: [PATCH] Added escape sequences --- README.md | 1 + examples/hello/hello.q | 2 +- src/build/asm/Finalize.go | 6 ++++ src/build/core/ExpressionToRegister.go | 8 +++++ src/build/core/Number.go | 2 +- src/build/core/String.go | 43 ++++++++++++++++++++++++++ src/build/core/TokenToRegister.go | 3 +- src/build/token/Tokenize.go | 2 +- tests/examples_test.go | 2 +- tests/programs/escape-rune.q | 9 ++++++ tests/programs/escape-string.q | 10 ++++++ tests/programs_test.go | 6 ++-- 12 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 src/build/core/String.go create mode 100644 tests/programs/escape-rune.q create mode 100644 tests/programs/escape-string.q diff --git a/README.md b/README.md index d28007f..8dcacc6 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ This is what generates expressions from tokens. - [x] Branches - [x] Loops - [x] Hexadecimal, octal and binary literals +- [x] Escape sequences - [ ] Data structures - [ ] Type system - [ ] Type operator: `|` (`User | Error`) diff --git a/examples/hello/hello.q b/examples/hello/hello.q index 6adfc54..2371295 100644 --- a/examples/hello/hello.q +++ b/examples/hello/hello.q @@ -1,7 +1,7 @@ import sys main() { - print("Hello", 5) + print("Hello\n", 6) } print(address, length) { diff --git a/src/build/asm/Finalize.go b/src/build/asm/Finalize.go index 7094fe2..340b6d4 100644 --- a/src/build/asm/Finalize.go +++ b/src/build/asm/Finalize.go @@ -132,6 +132,12 @@ func (a Assembler) Finalize() ([]byte, []byte) { case LABEL: labels[x.Data.(*Label).Name] = Address(len(code)) + case LOAD: + switch operands := x.Data.(type) { + case *MemoryRegister: + code = x64.LoadRegister(code, operands.Register, operands.Address.Offset, operands.Address.Length, operands.Address.Base) + } + case MODULO: code = modulo(code, x.Data) diff --git a/src/build/core/ExpressionToRegister.go b/src/build/core/ExpressionToRegister.go index a49264f..1956932 100644 --- a/src/build/core/ExpressionToRegister.go +++ b/src/build/core/ExpressionToRegister.go @@ -6,6 +6,7 @@ import ( "git.akyoto.dev/cli/q/src/build/cpu" "git.akyoto.dev/cli/q/src/build/errors" "git.akyoto.dev/cli/q/src/build/expression" + "git.akyoto.dev/cli/q/src/build/token" ) // ExpressionToRegister puts the result of an expression into the specified register. @@ -29,6 +30,13 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp return err } + if node.Token.Kind == token.Array { + array := f.VariableByName(node.Children[0].Token.Text(f.File.Bytes)) + offset, err := f.Number(node.Children[1].Token) + f.MemoryRegister(asm.LOAD, asm.Memory{Base: array.Register, Offset: byte(offset), Length: 1}, register) + return err + } + if len(node.Children) == 1 { if !node.Token.IsUnaryOperator() { 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 f6c14cd..394666f 100644 --- a/src/build/core/Number.go +++ b/src/build/core/Number.go @@ -35,7 +35,7 @@ func (f *Function) Number(t token.Token) (int, error) { case token.Rune: r := t.Bytes(f.File.Bytes) - r = r[1 : len(r)-1] + r = String(r) if len(r) == 0 { return 0, errors.New(errors.InvalidRune, f.File, t.Position+1) diff --git a/src/build/core/String.go b/src/build/core/String.go new file mode 100644 index 0000000..f614c3b --- /dev/null +++ b/src/build/core/String.go @@ -0,0 +1,43 @@ +package core + +import "bytes" + +// String replaces the escape sequences in the contents of a string token with the respective characters. +func String(data []byte) []byte { + data = data[1 : len(data)-1] + escape := bytes.IndexByte(data, '\\') + + if escape == -1 { + return data + } + + tmp := make([]byte, 0, len(data)) + + for { + tmp = append(tmp, data[:escape]...) + + switch data[escape+1] { + case '0': + tmp = append(tmp, '\000') + case 't': + tmp = append(tmp, '\t') + case 'n': + tmp = append(tmp, '\n') + case 'r': + tmp = append(tmp, '\r') + case '"': + tmp = append(tmp, '"') + case '\'': + tmp = append(tmp, '\'') + case '\\': + tmp = append(tmp, '\\') + } + + data = data[escape+2:] + escape = bytes.IndexByte(data, '\\') + + if escape == -1 { + return tmp + } + } +} diff --git a/src/build/core/TokenToRegister.go b/src/build/core/TokenToRegister.go index 0ed4e0a..6dd6c8c 100644 --- a/src/build/core/TokenToRegister.go +++ b/src/build/core/TokenToRegister.go @@ -34,7 +34,8 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error { return nil case token.String: - data := t.Bytes(f.File.Bytes)[1 : t.Length-1] + data := t.Bytes(f.File.Bytes) + data = String(data) label := f.AddBytes(data) f.RegisterLabel(asm.MOVE, register, label) return nil diff --git a/src/build/token/Tokenize.go b/src/build/token/Tokenize.go index c955e9a..be8a309 100644 --- a/src/build/token/Tokenize.go +++ b/src/build/token/Tokenize.go @@ -76,7 +76,7 @@ func Tokenize(buffer []byte) List { i++ for i < Position(len(buffer)) { - if buffer[i] == limiter { + if buffer[i] == limiter && (buffer[i-1] != '\\' || buffer[i-2] == '\\') { end = i + 1 i++ break diff --git a/tests/examples_test.go b/tests/examples_test.go index 0ff6142..116e270 100644 --- a/tests/examples_test.go +++ b/tests/examples_test.go @@ -15,7 +15,7 @@ var examples = []struct { Output string ExitCode int }{ - {"hello", "", "Hello", 0}, + {"hello", "", "Hello\n", 0}, {"factorial", "", "120", 0}, {"fibonacci", "", "55", 0}, {"gcd", "", "21", 0}, diff --git a/tests/programs/escape-rune.q b/tests/programs/escape-rune.q new file mode 100644 index 0000000..f7e0950 --- /dev/null +++ b/tests/programs/escape-rune.q @@ -0,0 +1,9 @@ +main() { + assert '\0' == 0 + assert '\t' == 9 + assert '\n' == 10 + assert '\r' == 13 + assert '\"' == 34 + assert '\'' == 39 + assert '\\' == 92 +} \ No newline at end of file diff --git a/tests/programs/escape-string.q b/tests/programs/escape-string.q new file mode 100644 index 0000000..1902f17 --- /dev/null +++ b/tests/programs/escape-string.q @@ -0,0 +1,10 @@ +main() { + str := "\0\t\n\r\"\'\\" + assert str[0] == '\0' + assert str[1] == '\t' + assert str[2] == '\n' + assert str[3] == '\r' + assert str[4] == '\"' + assert str[5] == '\'' + assert str[6] == '\\' +} \ No newline at end of file diff --git a/tests/programs_test.go b/tests/programs_test.go index 493ac51..043dbe9 100644 --- a/tests/programs_test.go +++ b/tests/programs_test.go @@ -24,11 +24,13 @@ var programs = []struct { {"reassign", "", "", 0}, {"reuse", "", "", 0}, {"return", "", "", 0}, + {"math", "", "", 0}, + {"precedence", "", "", 0}, {"binary", "", "", 0}, {"octal", "", "", 0}, {"hexadecimal", "", "", 0}, - {"math", "", "", 0}, - {"precedence", "", "", 0}, + {"escape-rune", "", "", 0}, + {"escape-string", "", "", 0}, {"bitwise-and", "", "", 0}, {"bitwise-or", "", "", 0}, {"bitwise-xor", "", "", 0},