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
|
||||
|
||||
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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
@ -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.
|
||||
|
@ -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
|
||||
|
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 {
|
||||
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
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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'"}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
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++
|
||||
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)
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -65,6 +65,7 @@ const (
|
||||
___END_OPERATORS___ // </operators>
|
||||
___KEYWORDS___ // <keywords>
|
||||
Assert // assert
|
||||
Const // const
|
||||
Else // else
|
||||
Extern // extern
|
||||
If // if
|
||||
|
@ -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,
|
||||
|
@ -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":
|
||||
|
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},
|
||||
{"octal", "", "", 0},
|
||||
{"hexadecimal", "", "", 0},
|
||||
{"const", "", "", 0},
|
||||
{"array", "", "", 0},
|
||||
{"escape-rune", "", "", 0},
|
||||
{"escape-string", "", "", 0},
|
||||
|
Loading…
x
Reference in New Issue
Block a user