Implemented const keyword
This commit is contained in:
parent
be028a52d1
commit
0a1a8f741d
12
lib/io/io.q
12
lib/io/io.q
@ -1,15 +1,21 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
const std {
|
||||||
|
in 0
|
||||||
|
out 1
|
||||||
|
err 2
|
||||||
|
}
|
||||||
|
|
||||||
in(buffer []Int8) -> Int {
|
in(buffer []Int8) -> Int {
|
||||||
return sys.read(0, buffer, len(buffer))
|
return sys.read(std.in, buffer, len(buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
out(buffer []Int8) -> Int {
|
out(buffer []Int8) -> Int {
|
||||||
return sys.write(1, buffer, len(buffer))
|
return sys.write(std.out, buffer, len(buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
error(buffer []Int8) -> Int {
|
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 {
|
read(fd Int, buffer []Int8) -> Int {
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
const prot {
|
||||||
|
read 0x1
|
||||||
|
write 0x2
|
||||||
|
}
|
||||||
|
|
||||||
|
const map {
|
||||||
|
private 0x02
|
||||||
|
anonymous 0x20
|
||||||
|
}
|
||||||
|
|
||||||
alloc(length Int) -> []Int8 {
|
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 {
|
if x < 0x1000 {
|
||||||
return x
|
return x
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
const prot {
|
||||||
|
read 0x1
|
||||||
|
write 0x2
|
||||||
|
}
|
||||||
|
|
||||||
|
const map {
|
||||||
|
private 0x02
|
||||||
|
anonymous 0x1000
|
||||||
|
}
|
||||||
|
|
||||||
alloc(length Int) -> []Int8 {
|
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 {
|
if x < 0x1000 {
|
||||||
return x
|
return x
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
const page {
|
||||||
|
readwrite 0x0004
|
||||||
|
}
|
||||||
|
|
||||||
|
const mem {
|
||||||
|
commit 0x1000
|
||||||
|
reserve 0x2000
|
||||||
|
}
|
||||||
|
|
||||||
alloc(length Int) -> []Int8 {
|
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 {
|
if x < 0x1000 {
|
||||||
return x
|
return x
|
||||||
|
@ -3,10 +3,14 @@ extern kernel32 {
|
|||||||
VirtualFree(address *Any, length Int, type Int) -> Bool
|
VirtualFree(address *Any, length Int, type Int) -> Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mem {
|
||||||
|
decommit 0x4000
|
||||||
|
}
|
||||||
|
|
||||||
mmap(address Int, length Int, protection Int, flags Int) -> *Any {
|
mmap(address Int, length Int, protection Int, flags Int) -> *Any {
|
||||||
return kernel32.VirtualAlloc(address, length, flags, protection)
|
return kernel32.VirtualAlloc(address, length, flags, protection)
|
||||||
}
|
}
|
||||||
|
|
||||||
munmap(address *Any, length Int) -> Int {
|
munmap(address *Any, length Int) -> Int {
|
||||||
return kernel32.VirtualFree(address, length, 0x4000)
|
return kernel32.VirtualFree(address, length, mem.decommit)
|
||||||
}
|
}
|
@ -23,8 +23,8 @@ func New(files ...string) *Build {
|
|||||||
|
|
||||||
// Run compiles the input files.
|
// Run compiles the input files.
|
||||||
func (build *Build) Run() (compiler.Result, error) {
|
func (build *Build) Run() (compiler.Result, error) {
|
||||||
files, functions, structs, errors := scanner.Scan(build.Files)
|
constants, files, functions, structs, errors := scanner.Scan(build.Files)
|
||||||
return compiler.Compile(files, functions, structs, errors)
|
return compiler.Compile(constants, files, functions, structs, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executable returns the path to the executable.
|
// Executable returns the path to the executable.
|
||||||
|
@ -8,13 +8,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Compile waits for the scan to finish and compiles all functions.
|
// 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{}
|
result := Result{}
|
||||||
allFiles := make([]*fs.File, 0, 8)
|
allFiles := make([]*fs.File, 0, 8)
|
||||||
allFunctions := make(map[string]*core.Function, 32)
|
allFunctions := make(map[string]*core.Function, 32)
|
||||||
allStructs := make(map[string]*types.Struct, 8)
|
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 {
|
select {
|
||||||
case function, ok := <-functions:
|
case function, ok := <-functions:
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -24,6 +25,7 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c
|
|||||||
|
|
||||||
function.Functions = allFunctions
|
function.Functions = allFunctions
|
||||||
function.Structs = allStructs
|
function.Structs = allStructs
|
||||||
|
function.Constants = allConstants
|
||||||
allFunctions[function.UniqueName] = function
|
allFunctions[function.UniqueName] = function
|
||||||
|
|
||||||
case structure, ok := <-structs:
|
case structure, ok := <-structs:
|
||||||
@ -42,6 +44,14 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c
|
|||||||
|
|
||||||
allFiles = append(allFiles, file)
|
allFiles = append(allFiles, file)
|
||||||
|
|
||||||
|
case constant, ok := <-constants:
|
||||||
|
if !ok {
|
||||||
|
constants = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
allConstants[constant.Name] = constant
|
||||||
|
|
||||||
case err, ok := <-errs:
|
case err, ok := <-errs:
|
||||||
if !ok {
|
if !ok {
|
||||||
errs = nil
|
errs = nil
|
||||||
|
13
src/core/Constant.go
Normal file
13
src/core/Constant.go
Normal file
@ -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
|
||||||
|
}
|
@ -86,11 +86,22 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
|
|
||||||
if node.Token.Kind == token.Period {
|
if node.Token.Kind == token.Period {
|
||||||
left := node.Children[0]
|
left := node.Children[0]
|
||||||
name := left.Token.Text(f.File.Bytes)
|
leftText := left.Token.Text(f.File.Bytes)
|
||||||
variable := f.VariableByName(name)
|
|
||||||
right := node.Children[1]
|
right := node.Children[1]
|
||||||
name = right.Token.Text(f.File.Bytes)
|
rightText := right.Token.Text(f.File.Bytes)
|
||||||
field := variable.Type.(*types.Pointer).To.(*types.Struct).FieldByName(name)
|
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)
|
f.MemoryRegister(asm.LOAD, asm.Memory{Base: variable.Register, Offset: int8(field.Offset), Length: byte(field.Type.Size())}, register)
|
||||||
return field.Type, nil
|
return field.Type, nil
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ type Function struct {
|
|||||||
OutputTypes []types.Type
|
OutputTypes []types.Type
|
||||||
Functions map[string]*Function
|
Functions map[string]*Function
|
||||||
Structs map[string]*types.Struct
|
Structs map[string]*types.Struct
|
||||||
|
Constants map[string]*Constant
|
||||||
DLLs dll.List
|
DLLs dll.List
|
||||||
Err error
|
Err error
|
||||||
deferred []func()
|
deferred []func()
|
||||||
|
@ -2,6 +2,7 @@ package errors
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
EmptySwitch = &Base{"Empty switch"}
|
EmptySwitch = &Base{"Empty switch"}
|
||||||
|
ExpectedConstName = &Base{"Expected a name for the const group"}
|
||||||
ExpectedFunctionParameters = &Base{"Expected function parameters"}
|
ExpectedFunctionParameters = &Base{"Expected function parameters"}
|
||||||
ExpectedFunctionDefinition = &Base{"Expected function definition"}
|
ExpectedFunctionDefinition = &Base{"Expected function definition"}
|
||||||
ExpectedIfBeforeElse = &Base{"Expected an 'if' block before 'else'"}
|
ExpectedIfBeforeElse = &Base{"Expected an 'if' block before 'else'"}
|
||||||
|
@ -10,8 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Scan scans the list of files.
|
// 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{
|
scanner := Scanner{
|
||||||
|
constants: make(chan *core.Constant),
|
||||||
files: make(chan *fs.File),
|
files: make(chan *fs.File),
|
||||||
functions: make(chan *core.Function),
|
functions: make(chan *core.Function),
|
||||||
structs: make(chan *types.Struct),
|
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.queueDirectory(filepath.Join(config.Library, "mem"), "mem")
|
||||||
scanner.queue(files...)
|
scanner.queue(files...)
|
||||||
scanner.group.Wait()
|
scanner.group.Wait()
|
||||||
|
close(scanner.constants)
|
||||||
close(scanner.files)
|
close(scanner.files)
|
||||||
close(scanner.functions)
|
close(scanner.functions)
|
||||||
close(scanner.structs)
|
close(scanner.structs)
|
||||||
close(scanner.errors)
|
close(scanner.errors)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return scanner.files, scanner.functions, scanner.structs, scanner.errors
|
return scanner.constants, scanner.files, scanner.functions, scanner.structs, scanner.errors
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
// Scanner is used to scan files before the actual compilation step.
|
// Scanner is used to scan files before the actual compilation step.
|
||||||
type Scanner struct {
|
type Scanner struct {
|
||||||
|
constants chan *core.Constant
|
||||||
files chan *fs.File
|
files chan *fs.File
|
||||||
functions chan *core.Function
|
functions chan *core.Function
|
||||||
structs chan *types.Struct
|
structs chan *types.Struct
|
||||||
|
47
src/scanner/scanConst.go
Normal file
47
src/scanner/scanConst.go
Normal file
@ -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)
|
||||||
|
}
|
@ -22,7 +22,6 @@ func (s *Scanner) scanExtern(file *fs.File, tokens token.List, i int) (int, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
i++
|
i++
|
||||||
closed := false
|
|
||||||
|
|
||||||
for i < len(tokens) {
|
for i < len(tokens) {
|
||||||
if tokens[i].Kind == token.Identifier {
|
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 {
|
if tokens[i].Kind == token.BlockEnd {
|
||||||
closed = true
|
return i, nil
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
if !closed {
|
|
||||||
return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position)
|
return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position)
|
||||||
}
|
|
||||||
|
|
||||||
return i, nil
|
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@ func (s *Scanner) scanFile(path string, pkg string) error {
|
|||||||
i, err = s.scanFunction(file, tokens, i)
|
i, err = s.scanFunction(file, tokens, i)
|
||||||
case token.Extern:
|
case token.Extern:
|
||||||
i, err = s.scanExtern(file, tokens, i)
|
i, err = s.scanExtern(file, tokens, i)
|
||||||
|
case token.Const:
|
||||||
|
i, err = s.scanConst(file, tokens, i)
|
||||||
case token.EOF:
|
case token.EOF:
|
||||||
return nil
|
return nil
|
||||||
case token.Invalid:
|
case token.Invalid:
|
||||||
|
@ -65,6 +65,7 @@ const (
|
|||||||
___END_OPERATORS___ // </operators>
|
___END_OPERATORS___ // </operators>
|
||||||
___KEYWORDS___ // <keywords>
|
___KEYWORDS___ // <keywords>
|
||||||
Assert // assert
|
Assert // assert
|
||||||
|
Const // const
|
||||||
Else // else
|
Else // else
|
||||||
Extern // extern
|
Extern // extern
|
||||||
If // if
|
If // if
|
||||||
|
@ -25,10 +25,11 @@ func TestFunction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyword(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{
|
expected := []token.Kind{
|
||||||
token.Assert,
|
token.Assert,
|
||||||
|
token.Const,
|
||||||
token.If,
|
token.If,
|
||||||
token.Import,
|
token.Import,
|
||||||
token.Else,
|
token.Else,
|
||||||
|
@ -15,6 +15,8 @@ func identifier(tokens List, buffer []byte, i Position) (List, Position) {
|
|||||||
switch string(identifier) {
|
switch string(identifier) {
|
||||||
case "assert":
|
case "assert":
|
||||||
kind = Assert
|
kind = Assert
|
||||||
|
case "const":
|
||||||
|
kind = Const
|
||||||
case "if":
|
case "if":
|
||||||
kind = If
|
kind = If
|
||||||
case "else":
|
case "else":
|
||||||
|
12
tests/programs/const.q
Normal file
12
tests/programs/const.q
Normal file
@ -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
|
||||||
|
}
|
@ -31,6 +31,7 @@ var programs = []struct {
|
|||||||
{"binary", "", "", 0},
|
{"binary", "", "", 0},
|
||||||
{"octal", "", "", 0},
|
{"octal", "", "", 0},
|
||||||
{"hexadecimal", "", "", 0},
|
{"hexadecimal", "", "", 0},
|
||||||
|
{"const", "", "", 0},
|
||||||
{"array", "", "", 0},
|
{"array", "", "", 0},
|
||||||
{"escape-rune", "", "", 0},
|
{"escape-rune", "", "", 0},
|
||||||
{"escape-string", "", "", 0},
|
{"escape-string", "", "", 0},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user