Implemented struct parser
This commit is contained in:
parent
fc1b970f7f
commit
51e3c1ba0e
4
lib/time/timespec.q
Normal file
4
lib/time/timespec.q
Normal file
@ -0,0 +1,4 @@
|
||||
struct timespec {
|
||||
seconds Int
|
||||
nanoseconds Int
|
||||
}
|
@ -23,8 +23,8 @@ func New(files ...string) *Build {
|
||||
|
||||
// Run compiles the input files.
|
||||
func (build *Build) Run() (compiler.Result, error) {
|
||||
files, functions, errors := scanner.Scan(build.Files)
|
||||
return compiler.Compile(files, functions, errors)
|
||||
files, functions, types, errors := scanner.Scan(build.Files)
|
||||
return compiler.Compile(files, functions, types, errors)
|
||||
}
|
||||
|
||||
// Executable returns the path to the executable.
|
||||
|
@ -6,13 +6,15 @@ 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/types"
|
||||
)
|
||||
|
||||
// Compile waits for the scan to finish and compiles all functions.
|
||||
func Compile(files <-chan *fs.File, functions <-chan *core.Function, errs <-chan error) (Result, error) {
|
||||
func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-chan *types.Type, errs <-chan error) (Result, error) {
|
||||
result := Result{}
|
||||
allFiles := make([]*fs.File, 0, 8)
|
||||
allFunctions := map[string]*core.Function{}
|
||||
allTypes := map[string]*types.Type{}
|
||||
|
||||
for functions != nil || files != nil || errs != nil {
|
||||
select {
|
||||
@ -25,6 +27,14 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, errs <-chan
|
||||
function.Functions = allFunctions
|
||||
allFunctions[function.UniqueName] = function
|
||||
|
||||
case typ, ok := <-structs:
|
||||
if !ok {
|
||||
structs = nil
|
||||
continue
|
||||
}
|
||||
|
||||
allTypes[typ.Name] = typ
|
||||
|
||||
case file, ok := <-files:
|
||||
if !ok {
|
||||
files = nil
|
||||
|
@ -6,6 +6,7 @@ var (
|
||||
ExpectedFunctionParameters = &Base{"Expected function parameters"}
|
||||
ExpectedFunctionDefinition = &Base{"Expected function definition"}
|
||||
ExpectedIfBeforeElse = &Base{"Expected an 'if' block before 'else'"}
|
||||
ExpectedStructName = &Base{"Expected struct name"}
|
||||
InvalidNumber = &Base{"Invalid number"}
|
||||
InvalidExpression = &Base{"Invalid expression"}
|
||||
InvalidRune = &Base{"Invalid rune"}
|
||||
|
@ -3,13 +3,15 @@ package scanner
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/core"
|
||||
"git.akyoto.dev/cli/q/src/fs"
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// Scan scans the list of files.
|
||||
func Scan(files []string) (<-chan *fs.File, <-chan *core.Function, <-chan error) {
|
||||
func Scan(files []string) (<-chan *fs.File, <-chan *core.Function, <-chan *types.Type, <-chan error) {
|
||||
scanner := Scanner{
|
||||
files: make(chan *fs.File),
|
||||
functions: make(chan *core.Function),
|
||||
types: make(chan *types.Type),
|
||||
errors: make(chan error),
|
||||
}
|
||||
|
||||
@ -18,8 +20,9 @@ func Scan(files []string) (<-chan *fs.File, <-chan *core.Function, <-chan error)
|
||||
scanner.group.Wait()
|
||||
close(scanner.files)
|
||||
close(scanner.functions)
|
||||
close(scanner.types)
|
||||
close(scanner.errors)
|
||||
}()
|
||||
|
||||
return scanner.files, scanner.functions, scanner.errors
|
||||
return scanner.files, scanner.functions, scanner.types, scanner.errors
|
||||
}
|
||||
|
@ -5,12 +5,14 @@ import (
|
||||
|
||||
"git.akyoto.dev/cli/q/src/core"
|
||||
"git.akyoto.dev/cli/q/src/fs"
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// Scanner is used to scan files before the actual compilation step.
|
||||
type Scanner struct {
|
||||
files chan *fs.File
|
||||
functions chan *core.Function
|
||||
types chan *types.Type
|
||||
errors chan error
|
||||
queued sync.Map
|
||||
group sync.WaitGroup
|
||||
|
@ -2,16 +2,10 @@ package scanner
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/config"
|
||||
"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/scope"
|
||||
"git.akyoto.dev/cli/q/src/token"
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
"git.akyoto.dev/cli/q/src/x64"
|
||||
)
|
||||
|
||||
// scanFile scans a single file.
|
||||
@ -32,250 +26,31 @@ func (s *Scanner) scanFile(path string, pkg string) error {
|
||||
}
|
||||
|
||||
s.files <- file
|
||||
i := 0
|
||||
|
||||
var (
|
||||
i = 0
|
||||
groupLevel = 0
|
||||
blockLevel = 0
|
||||
nameStart = -1
|
||||
paramsStart = -1
|
||||
paramsEnd = -1
|
||||
bodyStart = -1
|
||||
typeStart = -1
|
||||
typeEnd = -1
|
||||
)
|
||||
|
||||
for {
|
||||
for i < len(tokens) && tokens[i].Kind == token.Import {
|
||||
i++
|
||||
|
||||
if tokens[i].Kind != token.Identifier {
|
||||
panic("expected package name")
|
||||
}
|
||||
|
||||
packageName := tokens[i].Text(contents)
|
||||
|
||||
if file.Imports == nil {
|
||||
file.Imports = map[string]*fs.Import{}
|
||||
}
|
||||
|
||||
fullPath := filepath.Join(config.Library, packageName)
|
||||
|
||||
file.Imports[packageName] = &fs.Import{
|
||||
Path: packageName,
|
||||
FullPath: fullPath,
|
||||
Position: tokens[i].Position,
|
||||
}
|
||||
|
||||
s.queueDirectory(fullPath, packageName)
|
||||
i++
|
||||
|
||||
if tokens[i].Kind != token.NewLine && tokens[i].Kind != token.EOF {
|
||||
panic("expected newline or eof")
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
// Function name
|
||||
for i < len(tokens) {
|
||||
if tokens[i].Kind == token.Identifier {
|
||||
nameStart = i
|
||||
i++
|
||||
break
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.NewLine || tokens[i].Kind == token.Comment {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.Invalid {
|
||||
return errors.New(&errors.InvalidCharacter{Character: tokens[i].Text(contents)}, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.EOF {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errors.ExpectedFunctionName, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
// Function parameters
|
||||
for i < len(tokens) {
|
||||
if tokens[i].Kind == token.GroupStart {
|
||||
groupLevel++
|
||||
i++
|
||||
|
||||
if groupLevel == 1 {
|
||||
paramsStart = i
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.GroupEnd {
|
||||
groupLevel--
|
||||
|
||||
if groupLevel < 0 {
|
||||
return errors.New(errors.MissingGroupStart, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if groupLevel == 0 {
|
||||
paramsEnd = i
|
||||
i++
|
||||
break
|
||||
}
|
||||
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.Invalid {
|
||||
return errors.New(&errors.InvalidCharacter{Character: tokens[i].Text(contents)}, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.EOF {
|
||||
if groupLevel > 0 {
|
||||
return errors.New(errors.MissingGroupEnd, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if paramsStart == -1 {
|
||||
return errors.New(errors.ExpectedFunctionParameters, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if groupLevel > 0 {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
return errors.New(errors.ExpectedFunctionParameters, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
// Return type
|
||||
if i < len(tokens) && tokens[i].Kind == token.ReturnType {
|
||||
typeStart = i + 1
|
||||
|
||||
for i < len(tokens) && tokens[i].Kind != token.BlockStart {
|
||||
i++
|
||||
}
|
||||
|
||||
typeEnd = i
|
||||
}
|
||||
|
||||
// Function definition
|
||||
for i < len(tokens) {
|
||||
if tokens[i].Kind == token.ReturnType {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.BlockStart {
|
||||
blockLevel++
|
||||
i++
|
||||
|
||||
if blockLevel == 1 {
|
||||
bodyStart = i
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.BlockEnd {
|
||||
blockLevel--
|
||||
|
||||
if blockLevel < 0 {
|
||||
return errors.New(errors.MissingBlockStart, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if blockLevel == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.Invalid {
|
||||
return errors.New(&errors.InvalidCharacter{Character: tokens[i].Text(contents)}, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.EOF {
|
||||
if blockLevel > 0 {
|
||||
return errors.New(errors.MissingBlockEnd, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if bodyStart == -1 {
|
||||
return errors.New(errors.ExpectedFunctionDefinition, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if blockLevel > 0 {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
return errors.New(errors.ExpectedFunctionDefinition, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
name := tokens[nameStart].Text(contents)
|
||||
body := tokens[bodyStart:i]
|
||||
function := core.NewFunction(pkg, name, file, body)
|
||||
|
||||
if typeStart != -1 {
|
||||
if tokens[typeStart].Kind == token.GroupStart && tokens[typeEnd-1].Kind == token.GroupEnd {
|
||||
typeStart++
|
||||
typeEnd--
|
||||
}
|
||||
|
||||
function.ReturnTypes = types.ParseList(tokens[typeStart:typeEnd], contents)
|
||||
}
|
||||
|
||||
parameters := tokens[paramsStart:paramsEnd]
|
||||
count := 0
|
||||
|
||||
err := parameters.Split(func(tokens token.List) error {
|
||||
if len(tokens) < 2 {
|
||||
return errors.New(errors.MissingType, file, tokens[0].End())
|
||||
}
|
||||
|
||||
name := tokens[0].Text(contents)
|
||||
dataType := types.Parse(tokens[1:].Text(contents))
|
||||
register := x64.InputRegisters[count]
|
||||
uses := token.Count(function.Body, contents, token.Identifier, name)
|
||||
|
||||
if uses == 0 && name != "_" {
|
||||
return errors.New(&errors.UnusedVariable{Name: name}, file, tokens[0].Position)
|
||||
}
|
||||
|
||||
variable := &scope.Variable{
|
||||
Name: name,
|
||||
Type: dataType,
|
||||
Register: register,
|
||||
Alive: uses,
|
||||
}
|
||||
|
||||
function.Parameters = append(function.Parameters, variable)
|
||||
function.AddVariable(variable)
|
||||
count++
|
||||
for i < len(tokens) {
|
||||
switch tokens[i].Kind {
|
||||
case token.NewLine:
|
||||
case token.Import:
|
||||
i, err = s.scanImport(file, tokens, i)
|
||||
case token.Struct:
|
||||
i, err = s.scanStruct(file, tokens, i)
|
||||
case token.Identifier:
|
||||
i, err = s.scanFunction(file, tokens, i)
|
||||
case token.EOF:
|
||||
return nil
|
||||
})
|
||||
case token.Invalid:
|
||||
return errors.New(&errors.InvalidCharacter{Character: tokens[i].Text(file.Bytes)}, file, tokens[i].Position)
|
||||
default:
|
||||
return errors.New(&errors.InvalidInstruction{Instruction: tokens[i].Text(file.Bytes)}, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.functions <- function
|
||||
nameStart = -1
|
||||
paramsStart = -1
|
||||
bodyStart = -1
|
||||
typeStart = -1
|
||||
typeEnd = -1
|
||||
i++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
222
src/scanner/scanFunction.go
Normal file
222
src/scanner/scanFunction.go
Normal file
@ -0,0 +1,222 @@
|
||||
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/scope"
|
||||
"git.akyoto.dev/cli/q/src/token"
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
"git.akyoto.dev/cli/q/src/x64"
|
||||
)
|
||||
|
||||
// scanFunction scans a function.
|
||||
func (s *Scanner) scanFunction(file *fs.File, tokens token.List, i int) (int, error) {
|
||||
var (
|
||||
groupLevel = 0
|
||||
blockLevel = 0
|
||||
nameStart = -1
|
||||
paramsStart = -1
|
||||
paramsEnd = -1
|
||||
bodyStart = -1
|
||||
typeStart = -1
|
||||
typeEnd = -1
|
||||
)
|
||||
|
||||
// Function name
|
||||
for i < len(tokens) {
|
||||
if tokens[i].Kind == token.Identifier {
|
||||
nameStart = i
|
||||
i++
|
||||
break
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.NewLine || tokens[i].Kind == token.Comment {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.Invalid {
|
||||
return i, errors.New(&errors.InvalidCharacter{Character: tokens[i].Text(file.Bytes)}, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.EOF {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
return i, errors.New(errors.ExpectedFunctionName, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
// Function parameters
|
||||
for i < len(tokens) {
|
||||
if tokens[i].Kind == token.GroupStart {
|
||||
groupLevel++
|
||||
i++
|
||||
|
||||
if groupLevel == 1 {
|
||||
paramsStart = i
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.GroupEnd {
|
||||
groupLevel--
|
||||
|
||||
if groupLevel < 0 {
|
||||
return i, errors.New(errors.MissingGroupStart, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if groupLevel == 0 {
|
||||
paramsEnd = i
|
||||
i++
|
||||
break
|
||||
}
|
||||
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.Invalid {
|
||||
return i, errors.New(&errors.InvalidCharacter{Character: tokens[i].Text(file.Bytes)}, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.EOF {
|
||||
if groupLevel > 0 {
|
||||
return i, errors.New(errors.MissingGroupEnd, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if paramsStart == -1 {
|
||||
return i, errors.New(errors.ExpectedFunctionParameters, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
if groupLevel > 0 {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
return i, errors.New(errors.ExpectedFunctionParameters, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
// Return type
|
||||
if i < len(tokens) && tokens[i].Kind == token.ReturnType {
|
||||
typeStart = i + 1
|
||||
|
||||
for i < len(tokens) && tokens[i].Kind != token.BlockStart {
|
||||
i++
|
||||
}
|
||||
|
||||
typeEnd = i
|
||||
}
|
||||
|
||||
// Function definition
|
||||
for i < len(tokens) {
|
||||
if tokens[i].Kind == token.ReturnType {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.BlockStart {
|
||||
blockLevel++
|
||||
i++
|
||||
|
||||
if blockLevel == 1 {
|
||||
bodyStart = i
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.BlockEnd {
|
||||
blockLevel--
|
||||
|
||||
if blockLevel < 0 {
|
||||
return i, errors.New(errors.MissingBlockStart, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if blockLevel == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.Invalid {
|
||||
return i, errors.New(&errors.InvalidCharacter{Character: tokens[i].Text(file.Bytes)}, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.EOF {
|
||||
if blockLevel > 0 {
|
||||
return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if bodyStart == -1 {
|
||||
return i, errors.New(errors.ExpectedFunctionDefinition, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
if blockLevel > 0 {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
return i, errors.New(errors.ExpectedFunctionDefinition, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
name := tokens[nameStart].Text(file.Bytes)
|
||||
body := tokens[bodyStart:i]
|
||||
function := core.NewFunction(file.Package, name, file, body)
|
||||
|
||||
if typeStart != -1 {
|
||||
if tokens[typeStart].Kind == token.GroupStart && tokens[typeEnd-1].Kind == token.GroupEnd {
|
||||
typeStart++
|
||||
typeEnd--
|
||||
}
|
||||
|
||||
function.ReturnTypes = types.ParseList(tokens[typeStart:typeEnd], file.Bytes)
|
||||
}
|
||||
|
||||
parameters := tokens[paramsStart:paramsEnd]
|
||||
count := 0
|
||||
|
||||
err := parameters.Split(func(tokens token.List) error {
|
||||
if len(tokens) < 2 {
|
||||
return errors.New(errors.MissingType, file, tokens[0].End())
|
||||
}
|
||||
|
||||
name := tokens[0].Text(file.Bytes)
|
||||
dataType := types.Parse(tokens[1:].Text(file.Bytes))
|
||||
register := x64.InputRegisters[count]
|
||||
uses := token.Count(function.Body, file.Bytes, token.Identifier, name)
|
||||
|
||||
if uses == 0 && name != "_" {
|
||||
return errors.New(&errors.UnusedVariable{Name: name}, file, tokens[0].Position)
|
||||
}
|
||||
|
||||
variable := &scope.Variable{
|
||||
Name: name,
|
||||
Type: dataType,
|
||||
Register: register,
|
||||
Alive: uses,
|
||||
}
|
||||
|
||||
function.Parameters = append(function.Parameters, variable)
|
||||
function.AddVariable(variable)
|
||||
count++
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
s.functions <- function
|
||||
i++
|
||||
return i, nil
|
||||
}
|
41
src/scanner/scanImport.go
Normal file
41
src/scanner/scanImport.go
Normal file
@ -0,0 +1,41 @@
|
||||
package scanner
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/config"
|
||||
"git.akyoto.dev/cli/q/src/fs"
|
||||
"git.akyoto.dev/cli/q/src/token"
|
||||
)
|
||||
|
||||
// scanImport scans an import.
|
||||
func (s *Scanner) scanImport(file *fs.File, tokens token.List, i int) (int, error) {
|
||||
i++
|
||||
|
||||
if tokens[i].Kind != token.Identifier {
|
||||
panic("expected package name")
|
||||
}
|
||||
|
||||
packageName := tokens[i].Text(file.Bytes)
|
||||
|
||||
if file.Imports == nil {
|
||||
file.Imports = map[string]*fs.Import{}
|
||||
}
|
||||
|
||||
fullPath := filepath.Join(config.Library, packageName)
|
||||
|
||||
file.Imports[packageName] = &fs.Import{
|
||||
Path: packageName,
|
||||
FullPath: fullPath,
|
||||
Position: tokens[i].Position,
|
||||
}
|
||||
|
||||
s.queueDirectory(fullPath, packageName)
|
||||
i++
|
||||
|
||||
if tokens[i].Kind != token.NewLine && tokens[i].Kind != token.EOF {
|
||||
panic("expected newline or eof")
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
66
src/scanner/scanStruct.go
Normal file
66
src/scanner/scanStruct.go
Normal file
@ -0,0 +1,66 @@
|
||||
package scanner
|
||||
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/errors"
|
||||
"git.akyoto.dev/cli/q/src/fs"
|
||||
"git.akyoto.dev/cli/q/src/token"
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// scanStruct scans a struct.
|
||||
func (s *Scanner) scanStruct(file *fs.File, tokens token.List, i int) (int, error) {
|
||||
i++
|
||||
|
||||
if tokens[i].Kind != token.Identifier {
|
||||
return i, errors.New(errors.ExpectedStructName, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
structName := tokens[i].Text(file.Bytes)
|
||||
|
||||
typ := &types.Type{
|
||||
Name: structName,
|
||||
}
|
||||
|
||||
i++
|
||||
|
||||
if tokens[i].Kind != token.BlockStart {
|
||||
return i, errors.New(errors.MissingBlockStart, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
i++
|
||||
closed := false
|
||||
|
||||
for i < len(tokens) {
|
||||
if tokens[i].Kind == token.Identifier {
|
||||
fieldPosition := i
|
||||
fieldName := tokens[i].Text(file.Bytes)
|
||||
i++
|
||||
fieldTypeName := tokens[i].Text(file.Bytes)
|
||||
fieldType := types.Parse(fieldTypeName)
|
||||
i++
|
||||
|
||||
typ.Fields = append(typ.Fields, &types.Field{
|
||||
Type: fieldType,
|
||||
Name: fieldName,
|
||||
Position: token.Position(fieldPosition),
|
||||
Offset: typ.Size,
|
||||
})
|
||||
|
||||
typ.Size += fieldType.Size
|
||||
}
|
||||
|
||||
if tokens[i].Kind == token.BlockEnd {
|
||||
closed = true
|
||||
break
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
if !closed {
|
||||
return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
s.types <- typ
|
||||
return i, nil
|
||||
}
|
@ -70,6 +70,7 @@ const (
|
||||
Import // import
|
||||
Loop // loop
|
||||
Return // return
|
||||
Struct // struct
|
||||
Switch // switch
|
||||
___END_KEYWORDS___ // </keywords>
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ func TestFunction(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestKeyword(t *testing.T) {
|
||||
tokens := token.Tokenize([]byte("assert if import else loop return switch"))
|
||||
tokens := token.Tokenize([]byte("assert if import else loop return struct switch"))
|
||||
|
||||
expected := []token.Kind{
|
||||
token.Assert,
|
||||
@ -34,6 +34,7 @@ func TestKeyword(t *testing.T) {
|
||||
token.Else,
|
||||
token.Loop,
|
||||
token.Return,
|
||||
token.Struct,
|
||||
token.Switch,
|
||||
token.EOF,
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ func identifier(tokens List, buffer []byte, i Position) (List, Position) {
|
||||
kind = Loop
|
||||
case "return":
|
||||
kind = Return
|
||||
case "struct":
|
||||
kind = Struct
|
||||
case "switch":
|
||||
kind = Switch
|
||||
}
|
||||
|
1
tests/errors/ExpectedStructName.q
Normal file
1
tests/errors/ExpectedStructName.q
Normal file
@ -0,0 +1 @@
|
||||
struct{}
|
3
tests/errors/InvalidInstructionString.q
Normal file
3
tests/errors/InvalidInstructionString.q
Normal file
@ -0,0 +1,3 @@
|
||||
main() {
|
||||
"Hello"
|
||||
}
|
1
tests/errors/InvalidInstructionTopLevel3.q
Normal file
1
tests/errors/InvalidInstructionTopLevel3.q
Normal file
@ -0,0 +1 @@
|
||||
+
|
@ -16,14 +16,17 @@ var errs = []struct {
|
||||
}{
|
||||
{"EmptySwitch.q", errors.EmptySwitch},
|
||||
{"ExpectedFunctionDefinition.q", errors.ExpectedFunctionDefinition},
|
||||
{"ExpectedFunctionName.q", errors.ExpectedFunctionName},
|
||||
{"ExpectedFunctionName2.q", errors.ExpectedFunctionName},
|
||||
{"ExpectedFunctionParameters.q", errors.ExpectedFunctionParameters},
|
||||
{"ExpectedIfBeforeElse.q", errors.ExpectedIfBeforeElse},
|
||||
{"ExpectedIfBeforeElse2.q", errors.ExpectedIfBeforeElse},
|
||||
{"ExpectedStructName.q", errors.ExpectedStructName},
|
||||
{"InvalidInstructionExpression.q", &errors.InvalidInstruction{Instruction: "+"}},
|
||||
{"InvalidInstructionIdentifier.q", &errors.InvalidInstruction{Instruction: "abc"}},
|
||||
{"InvalidInstructionNumber.q", &errors.InvalidInstruction{Instruction: "123"}},
|
||||
{"InvalidInstructionExpression.q", &errors.InvalidInstruction{Instruction: "+"}},
|
||||
{"InvalidInstructionString.q", &errors.InvalidInstruction{Instruction: "\"Hello\""}},
|
||||
{"InvalidInstructionTopLevel.q", &errors.InvalidInstruction{Instruction: "123"}},
|
||||
{"InvalidInstructionTopLevel2.q", &errors.InvalidInstruction{Instruction: "\"Hello\""}},
|
||||
{"InvalidInstructionTopLevel3.q", &errors.InvalidInstruction{Instruction: "+"}},
|
||||
{"InvalidExpression.q", errors.InvalidExpression},
|
||||
{"InvalidCharacter.q", &errors.InvalidCharacter{Character: "@"}},
|
||||
{"InvalidCharacter2.q", &errors.InvalidCharacter{Character: "@"}},
|
||||
|
Loading…
x
Reference in New Issue
Block a user