Implemented extern functions

This commit is contained in:
2025-02-12 00:04:30 +01:00
parent 1083db6ab2
commit 3b66dae1d4
24 changed files with 304 additions and 169 deletions

54
src/scanner/scanExtern.go Normal file
View File

@ -0,0 +1,54 @@
package scanner
import (
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/fs"
"git.akyoto.dev/cli/q/src/token"
)
// scanExtern scans a block of external function declarations.
func (s *Scanner) scanExtern(file *fs.File, tokens token.List, i int) (int, error) {
i++
if tokens[i].Kind != token.Identifier {
return i, errors.New(errors.ExpectedDLLName, file, tokens[i].Position)
}
dllName := tokens[i].Text(file.Bytes)
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 {
function, j, err := scanFunctionSignature(file, tokens, i, token.NewLine)
if err != nil {
return j, err
}
i = j
function.Package = dllName
function.UniqueName = dllName + "." + function.Name
s.functions <- function
}
if tokens[i].Kind == token.BlockEnd {
closed = true
break
}
i++
}
if !closed {
return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position)
}
return i, nil
}

View File

@ -37,6 +37,8 @@ func (s *Scanner) scanFile(path string, pkg string) error {
i, err = s.scanStruct(file, tokens, i)
case token.Identifier:
i, err = s.scanFunction(file, tokens, i)
case token.Extern:
i, err = s.scanExtern(file, tokens, i)
case token.EOF:
return nil
case token.Invalid:

View File

@ -1,7 +1,6 @@
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"
@ -9,84 +8,17 @@ import (
// scanFunction scans a function.
func (s *Scanner) scanFunction(file *fs.File, tokens token.List, i int) (int, error) {
function, i, err := scanFunctionSignature(file, tokens, i, token.BlockStart)
if err != nil {
return i, err
}
var (
groupLevel = 0
blockLevel = 0
nameStart = i
paramsStart = -1
paramsEnd = -1
bodyStart = -1
typeStart = -1
typeEnd = -1
blockLevel = 0
bodyStart = -1
)
i++
// 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 {
@ -144,47 +76,7 @@ func (s *Scanner) scanFunction(file *fs.File, tokens token.List, i int) (int, er
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--
}
outputTokens := tokens[typeStart:typeEnd]
err := outputTokens.Split(func(tokens token.List) error {
function.Output = append(function.Output, core.NewOutput(tokens))
return nil
})
if err != nil {
return i, err
}
}
parameters := tokens[paramsStart:paramsEnd]
err := parameters.Split(func(tokens token.List) error {
if len(tokens) == 0 {
return errors.New(errors.MissingParameter, file, parameters[0].Position)
}
if len(tokens) == 1 {
return errors.New(errors.MissingType, file, tokens[0].End())
}
function.Input = append(function.Input, core.NewInput(tokens))
return nil
})
if err != nil {
return i, err
}
function.Body = tokens[bodyStart:i]
s.functions <- function
i++
return i, nil

View File

@ -0,0 +1,125 @@
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"
)
// scanFunctionSignature scans a function declaration without the body.
func scanFunctionSignature(file *fs.File, tokens token.List, i int, delimiter token.Kind) (*core.Function, int, error) {
var (
groupLevel = 0
nameStart = i
paramsStart = -1
paramsEnd = -1
typeStart = -1
typeEnd = -1
)
i++
// 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 nil, 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 nil, 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 nil, i, errors.New(errors.MissingGroupEnd, file, tokens[i].Position)
}
if paramsStart == -1 {
return nil, i, errors.New(errors.ExpectedFunctionParameters, file, tokens[i].Position)
}
return nil, i, nil
}
if groupLevel > 0 {
i++
continue
}
return nil, 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 != delimiter {
i++
}
typeEnd = i
}
name := tokens[nameStart].Text(file.Bytes)
function := core.NewFunction(file.Package, name, file, nil)
if typeStart != -1 {
if tokens[typeStart].Kind == token.GroupStart && tokens[typeEnd-1].Kind == token.GroupEnd {
typeStart++
typeEnd--
}
outputTokens := tokens[typeStart:typeEnd]
err := outputTokens.Split(func(tokens token.List) error {
function.Output = append(function.Output, core.NewOutput(tokens))
return nil
})
if err != nil {
return nil, i, err
}
}
parameters := tokens[paramsStart:paramsEnd]
err := parameters.Split(func(tokens token.List) error {
if len(tokens) == 0 {
return errors.New(errors.MissingParameter, file, parameters[0].Position)
}
if len(tokens) == 1 {
return errors.New(errors.MissingType, file, tokens[0].End())
}
function.Input = append(function.Input, core.NewInput(tokens))
return nil
})
return function, i, err
}