diff --git a/lib/io/io.q b/lib/io/io.q index 5c195a4..4b34566 100644 --- a/lib/io/io.q +++ b/lib/io/io.q @@ -1,15 +1,21 @@ import sys +const std { + in 0 + out 1 + err 2 +} + in(buffer []Int8) -> Int { - return sys.read(0, buffer, len(buffer)) + return sys.read(std.in, buffer, len(buffer)) } out(buffer []Int8) -> Int { - return sys.write(1, buffer, len(buffer)) + return sys.write(std.out, buffer, len(buffer)) } error(buffer []Int8) -> Int { - return sys.write(2, buffer, len(buffer)) + return sys.write(std.err, buffer, len(buffer)) } read(fd Int, buffer []Int8) -> Int { diff --git a/lib/mem/alloc_linux.q b/lib/mem/alloc_linux.q index 9b9878b..8ade3e6 100644 --- a/lib/mem/alloc_linux.q +++ b/lib/mem/alloc_linux.q @@ -1,7 +1,17 @@ import sys +const prot { + read 0x1 + write 0x2 +} + +const map { + private 0x02 + anonymous 0x20 +} + alloc(length Int) -> []Int8 { - x := sys.mmap(0, length+8, 0x1|0x2, 0x02|0x20) + x := sys.mmap(0, length+8, prot.read|prot.write, map.private|map.anonymous) if x < 0x1000 { return x diff --git a/lib/mem/alloc_mac.q b/lib/mem/alloc_mac.q index 128d40c..cdfbe86 100644 --- a/lib/mem/alloc_mac.q +++ b/lib/mem/alloc_mac.q @@ -1,7 +1,17 @@ import sys +const prot { + read 0x1 + write 0x2 +} + +const map { + private 0x02 + anonymous 0x1000 +} + alloc(length Int) -> []Int8 { - x := sys.mmap(0, length+8, 0x1|0x2, 0x02|0x1000) + x := sys.mmap(0, length+8, prot.read|prot.write, map.private|map.anonymous) if x < 0x1000 { return x diff --git a/lib/mem/alloc_windows.q b/lib/mem/alloc_windows.q index 50bb75d..3167a43 100644 --- a/lib/mem/alloc_windows.q +++ b/lib/mem/alloc_windows.q @@ -1,7 +1,16 @@ import sys +const page { + readwrite 0x0004 +} + +const mem { + commit 0x1000 + reserve 0x2000 +} + alloc(length Int) -> []Int8 { - x := sys.mmap(0, length+8, 0x0004, 0x3000) + x := sys.mmap(0, length+8, page.readwrite, mem.commit|mem.reserve) if x < 0x1000 { return x diff --git a/lib/sys/mem_windows.q b/lib/sys/mem_windows.q index 79b093a..97772e9 100644 --- a/lib/sys/mem_windows.q +++ b/lib/sys/mem_windows.q @@ -3,10 +3,14 @@ extern kernel32 { VirtualFree(address *Any, length Int, type Int) -> Bool } +const mem { + decommit 0x4000 +} + mmap(address Int, length Int, protection Int, flags Int) -> *Any { return kernel32.VirtualAlloc(address, length, flags, protection) } munmap(address *Any, length Int) -> Int { - return kernel32.VirtualFree(address, length, 0x4000) + return kernel32.VirtualFree(address, length, mem.decommit) } \ No newline at end of file diff --git a/src/build/Build.go b/src/build/Build.go index 9784a97..64e1190 100644 --- a/src/build/Build.go +++ b/src/build/Build.go @@ -23,8 +23,8 @@ func New(files ...string) *Build { // Run compiles the input files. func (build *Build) Run() (compiler.Result, error) { - files, functions, structs, errors := scanner.Scan(build.Files) - return compiler.Compile(files, functions, structs, errors) + constants, files, functions, structs, errors := scanner.Scan(build.Files) + return compiler.Compile(constants, files, functions, structs, errors) } // Executable returns the path to the executable. diff --git a/src/compiler/Compile.go b/src/compiler/Compile.go index d66ce52..f964d78 100644 --- a/src/compiler/Compile.go +++ b/src/compiler/Compile.go @@ -8,13 +8,14 @@ import ( ) // Compile waits for the scan to finish and compiles all functions. -func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-chan *types.Struct, errs <-chan error) (Result, error) { +func Compile(constants <-chan *core.Constant, files <-chan *fs.File, functions <-chan *core.Function, structs <-chan *types.Struct, errs <-chan error) (Result, error) { result := Result{} allFiles := make([]*fs.File, 0, 8) allFunctions := make(map[string]*core.Function, 32) allStructs := make(map[string]*types.Struct, 8) + allConstants := make(map[string]*core.Constant, 8) - for functions != nil || structs != nil || files != nil || errs != nil { + for constants != nil || files != nil || functions != nil || structs != nil || errs != nil { select { case function, ok := <-functions: if !ok { @@ -24,6 +25,7 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c function.Functions = allFunctions function.Structs = allStructs + function.Constants = allConstants allFunctions[function.UniqueName] = function case structure, ok := <-structs: @@ -42,6 +44,14 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c allFiles = append(allFiles, file) + case constant, ok := <-constants: + if !ok { + constants = nil + continue + } + + allConstants[constant.Name] = constant + case err, ok := <-errs: if !ok { errs = nil diff --git a/src/core/Constant.go b/src/core/Constant.go new file mode 100644 index 0000000..1156e05 --- /dev/null +++ b/src/core/Constant.go @@ -0,0 +1,13 @@ +package core + +import ( + "git.akyoto.dev/cli/q/src/fs" + "git.akyoto.dev/cli/q/src/token" +) + +// Constant registers a single value to be accessible under a descriptive name. +type Constant struct { + Name string + Value token.Token + File *fs.File +} diff --git a/src/core/ExpressionToRegister.go b/src/core/ExpressionToRegister.go index 4e52cc9..d1a85ec 100644 --- a/src/core/ExpressionToRegister.go +++ b/src/core/ExpressionToRegister.go @@ -86,11 +86,22 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp if node.Token.Kind == token.Period { left := node.Children[0] - name := left.Token.Text(f.File.Bytes) - variable := f.VariableByName(name) + leftText := left.Token.Text(f.File.Bytes) right := node.Children[1] - name = right.Token.Text(f.File.Bytes) - field := variable.Type.(*types.Pointer).To.(*types.Struct).FieldByName(name) + rightText := right.Token.Text(f.File.Bytes) + variable := f.VariableByName(leftText) + + if variable == nil { + constant, isConst := f.Constants[f.Package+"."+leftText+"."+rightText] + + if isConst { + return f.TokenToRegister(constant.Value, register) + } + + return nil, errors.New(&errors.UnknownIdentifier{Name: leftText}, f.File, left.Token.Position) + } + + field := variable.Type.(*types.Pointer).To.(*types.Struct).FieldByName(rightText) f.MemoryRegister(asm.LOAD, asm.Memory{Base: variable.Register, Offset: int8(field.Offset), Length: byte(field.Type.Size())}, register) return field.Type, nil } diff --git a/src/core/Function.go b/src/core/Function.go index 5565dea..956956c 100644 --- a/src/core/Function.go +++ b/src/core/Function.go @@ -21,6 +21,7 @@ type Function struct { OutputTypes []types.Type Functions map[string]*Function Structs map[string]*types.Struct + Constants map[string]*Constant DLLs dll.List Err error deferred []func() diff --git a/src/errors/Common.go b/src/errors/Common.go index c313c86..04638d8 100644 --- a/src/errors/Common.go +++ b/src/errors/Common.go @@ -2,6 +2,7 @@ package errors var ( EmptySwitch = &Base{"Empty switch"} + ExpectedConstName = &Base{"Expected a name for the const group"} ExpectedFunctionParameters = &Base{"Expected function parameters"} ExpectedFunctionDefinition = &Base{"Expected function definition"} ExpectedIfBeforeElse = &Base{"Expected an 'if' block before 'else'"} diff --git a/src/scanner/Scan.go b/src/scanner/Scan.go index fdb3354..f745619 100644 --- a/src/scanner/Scan.go +++ b/src/scanner/Scan.go @@ -10,8 +10,9 @@ import ( ) // Scan scans the list of files. -func Scan(files []string) (<-chan *fs.File, <-chan *core.Function, <-chan *types.Struct, <-chan error) { +func Scan(files []string) (chan *core.Constant, <-chan *fs.File, <-chan *core.Function, <-chan *types.Struct, <-chan error) { scanner := Scanner{ + constants: make(chan *core.Constant), files: make(chan *fs.File), functions: make(chan *core.Function), structs: make(chan *types.Struct), @@ -22,11 +23,12 @@ func Scan(files []string) (<-chan *fs.File, <-chan *core.Function, <-chan *types scanner.queueDirectory(filepath.Join(config.Library, "mem"), "mem") scanner.queue(files...) scanner.group.Wait() + close(scanner.constants) close(scanner.files) close(scanner.functions) close(scanner.structs) close(scanner.errors) }() - return scanner.files, scanner.functions, scanner.structs, scanner.errors + return scanner.constants, scanner.files, scanner.functions, scanner.structs, scanner.errors } diff --git a/src/scanner/Scanner.go b/src/scanner/Scanner.go index 97847bd..9bb05fa 100644 --- a/src/scanner/Scanner.go +++ b/src/scanner/Scanner.go @@ -10,6 +10,7 @@ import ( // Scanner is used to scan files before the actual compilation step. type Scanner struct { + constants chan *core.Constant files chan *fs.File functions chan *core.Function structs chan *types.Struct diff --git a/src/scanner/scanConst.go b/src/scanner/scanConst.go new file mode 100644 index 0000000..f382813 --- /dev/null +++ b/src/scanner/scanConst.go @@ -0,0 +1,47 @@ +package scanner + +import ( + "git.akyoto.dev/cli/q/src/core" + "git.akyoto.dev/cli/q/src/errors" + "git.akyoto.dev/cli/q/src/fs" + "git.akyoto.dev/cli/q/src/token" +) + +// scanConst scans a block of constants. +func (s *Scanner) scanConst(file *fs.File, tokens token.List, i int) (int, error) { + i++ + + if tokens[i].Kind != token.Identifier { + return i, errors.New(errors.ExpectedConstName, file, tokens[i].Position) + } + + groupName := tokens[i].Text(file.Bytes) + i++ + + if tokens[i].Kind != token.BlockStart { + return i, errors.New(errors.MissingBlockStart, file, tokens[i].Position) + } + + i++ + + for i < len(tokens) { + if tokens[i].Kind == token.Identifier { + name := tokens[i].Text(file.Bytes) + i++ + + s.constants <- &core.Constant{ + Name: file.Package + "." + groupName + "." + name, + Value: tokens[i], + File: file, + } + } + + if tokens[i].Kind == token.BlockEnd { + return i, nil + } + + i++ + } + + return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position) +} diff --git a/src/scanner/scanExtern.go b/src/scanner/scanExtern.go index e4dc19d..4e4e7e8 100644 --- a/src/scanner/scanExtern.go +++ b/src/scanner/scanExtern.go @@ -22,7 +22,6 @@ func (s *Scanner) scanExtern(file *fs.File, tokens token.List, i int) (int, erro } i++ - closed := false for i < len(tokens) { if tokens[i].Kind == token.Identifier { @@ -39,16 +38,11 @@ func (s *Scanner) scanExtern(file *fs.File, tokens token.List, i int) (int, erro } if tokens[i].Kind == token.BlockEnd { - closed = true - break + return i, nil } i++ } - if !closed { - return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position) - } - - return i, nil + return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position) } diff --git a/src/scanner/scanFile.go b/src/scanner/scanFile.go index 2aae3af..65601f0 100644 --- a/src/scanner/scanFile.go +++ b/src/scanner/scanFile.go @@ -39,6 +39,8 @@ func (s *Scanner) scanFile(path string, pkg string) error { i, err = s.scanFunction(file, tokens, i) case token.Extern: i, err = s.scanExtern(file, tokens, i) + case token.Const: + i, err = s.scanConst(file, tokens, i) case token.EOF: return nil case token.Invalid: diff --git a/src/token/Kind.go b/src/token/Kind.go index 6e729e4..200d131 100644 --- a/src/token/Kind.go +++ b/src/token/Kind.go @@ -65,6 +65,7 @@ const ( ___END_OPERATORS___ // ___KEYWORDS___ // Assert // assert + Const // const Else // else Extern // extern If // if diff --git a/src/token/Tokenize_test.go b/src/token/Tokenize_test.go index 022115d..b38a5d7 100644 --- a/src/token/Tokenize_test.go +++ b/src/token/Tokenize_test.go @@ -25,10 +25,11 @@ func TestFunction(t *testing.T) { } func TestKeyword(t *testing.T) { - tokens := token.Tokenize([]byte("assert if import else extern loop return struct switch")) + tokens := token.Tokenize([]byte("assert const if import else extern loop return struct switch")) expected := []token.Kind{ token.Assert, + token.Const, token.If, token.Import, token.Else, diff --git a/src/token/identifier.go b/src/token/identifier.go index 6b6823b..6973fc6 100644 --- a/src/token/identifier.go +++ b/src/token/identifier.go @@ -15,6 +15,8 @@ func identifier(tokens List, buffer []byte, i Position) (List, Position) { switch string(identifier) { case "assert": kind = Assert + case "const": + kind = Const case "if": kind = If case "else": diff --git a/tests/programs/const.q b/tests/programs/const.q new file mode 100644 index 0000000..c947f1a --- /dev/null +++ b/tests/programs/const.q @@ -0,0 +1,12 @@ +const num { + one 1 + two 2 + three 3 +} + +main() { + assert num.one == 1 + assert num.two == 2 + assert num.three == 3 + assert num.one + num.two == num.three +} \ No newline at end of file diff --git a/tests/programs_test.go b/tests/programs_test.go index f3a13cb..23ec235 100644 --- a/tests/programs_test.go +++ b/tests/programs_test.go @@ -31,6 +31,7 @@ var programs = []struct { {"binary", "", "", 0}, {"octal", "", "", 0}, {"hexadecimal", "", "", 0}, + {"const", "", "", 0}, {"array", "", "", 0}, {"escape-rune", "", "", 0}, {"escape-string", "", "", 0},