Merge pull request 'func-section' (#1) from func-section into main
Reviewed-on: arf/arf#1
This commit is contained in:
commit
845c12c78b
@ -2,7 +2,7 @@ package arfc
|
||||
|
||||
import "os"
|
||||
import "fmt"
|
||||
import "git.tebibyte.media/sashakoshka/arf"
|
||||
import "git.tebibyte.media/arf/arf"
|
||||
|
||||
func main () {
|
||||
if len(os.Args) != 2 {
|
||||
|
21
face_test.go
21
face_test.go
@ -1,21 +0,0 @@
|
||||
package parser
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFace (test *testing.T) {
|
||||
checkTree ("../tests/parser/face",
|
||||
`:arf
|
||||
---
|
||||
face ro Destroyer:Face
|
||||
destroy
|
||||
face ro ReadWriter:Face
|
||||
read
|
||||
> into:{Byte ..}
|
||||
< read:Int
|
||||
< err:Error
|
||||
write
|
||||
> data:{Byte ..}
|
||||
< wrote:Int
|
||||
< err:Error
|
||||
`, test)
|
||||
}
|
2
go.mod
2
go.mod
@ -1,3 +1,3 @@
|
||||
module git.tebibyte.media/sashakoshka/arf
|
||||
module git.tebibyte.media/arf/arf
|
||||
|
||||
go 1.18
|
||||
|
@ -2,7 +2,7 @@ package infoerr
|
||||
|
||||
import "os"
|
||||
import "fmt"
|
||||
import "git.tebibyte.media/sashakoshka/arf/file"
|
||||
import "git.tebibyte.media/arf/arf/file"
|
||||
|
||||
type ErrorKind int
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
package lexer
|
||||
|
||||
import "io"
|
||||
import "git.tebibyte.media/sashakoshka/arf/file"
|
||||
import "git.tebibyte.media/sashakoshka/arf/types"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/file"
|
||||
import "git.tebibyte.media/arf/arf/types"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// LexingOperation holds information about an ongoing lexing operataion.
|
||||
type LexingOperation struct {
|
||||
@ -253,14 +253,26 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
|
||||
lexer.addToken(token)
|
||||
case '%':
|
||||
token := lexer.newToken()
|
||||
token.kind = TokenKindPercent
|
||||
lexer.addToken(token)
|
||||
err = lexer.nextRune()
|
||||
if err != nil { return }
|
||||
token.kind = TokenKindPercent
|
||||
if lexer.char == '=' {
|
||||
token.kind = TokenKindPercentAssignment
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(2)
|
||||
}
|
||||
lexer.addToken(token)
|
||||
case '~':
|
||||
token := lexer.newToken()
|
||||
token.kind = TokenKindTilde
|
||||
lexer.addToken(token)
|
||||
err = lexer.nextRune()
|
||||
if err != nil { return }
|
||||
token.kind = TokenKindTilde
|
||||
if lexer.char == '=' {
|
||||
token.kind = TokenKindTildeAssignment
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(2)
|
||||
}
|
||||
lexer.addToken(token)
|
||||
case '=':
|
||||
token := lexer.newToken()
|
||||
token.kind = TokenKindEqualTo
|
||||
@ -275,6 +287,11 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
|
||||
token.kind = TokenKindLShift
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(2)
|
||||
if lexer.char == '=' {
|
||||
token.kind = TokenKindLShiftAssignment
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(3)
|
||||
}
|
||||
} else if lexer.char == '=' {
|
||||
token.kind = TokenKindLessThanEqualTo
|
||||
err = lexer.nextRune()
|
||||
@ -290,6 +307,11 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
|
||||
token.kind = TokenKindRShift
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(2)
|
||||
if lexer.char == '=' {
|
||||
token.kind = TokenKindRShiftAssignment
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(3)
|
||||
}
|
||||
} else if lexer.char == '=' {
|
||||
token.kind = TokenKindGreaterThanEqualTo
|
||||
err = lexer.nextRune()
|
||||
@ -305,6 +327,10 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
|
||||
token.kind = TokenKindLogicalOr
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(2)
|
||||
} else if lexer.char == '=' {
|
||||
token.kind = TokenKindBinaryOrAssignment
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(2)
|
||||
}
|
||||
lexer.addToken(token)
|
||||
case '&':
|
||||
@ -316,6 +342,21 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
|
||||
token.kind = TokenKindLogicalAnd
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(2)
|
||||
} else if lexer.char == '=' {
|
||||
token.kind = TokenKindBinaryAndAssignment
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(2)
|
||||
}
|
||||
lexer.addToken(token)
|
||||
case '^':
|
||||
token := lexer.newToken()
|
||||
err = lexer.nextRune()
|
||||
if err != nil { return }
|
||||
token.kind = TokenKindBinaryXor
|
||||
if lexer.char == '=' {
|
||||
token.kind = TokenKindBinaryXorAssignment
|
||||
err = lexer.nextRune()
|
||||
token.location.SetWidth(2)
|
||||
}
|
||||
lexer.addToken(token)
|
||||
default:
|
||||
@ -331,11 +372,11 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
|
||||
}
|
||||
|
||||
func (lexer *LexingOperation) tokenizeDashBeginning () (err error) {
|
||||
token := lexer.newToken()
|
||||
err = lexer.nextRune()
|
||||
if err != nil { return }
|
||||
|
||||
if lexer.char == '-' {
|
||||
token := lexer.newToken()
|
||||
token.kind = TokenKindDecrement
|
||||
token.location.SetWidth(2)
|
||||
|
||||
@ -349,7 +390,6 @@ func (lexer *LexingOperation) tokenizeDashBeginning () (err error) {
|
||||
}
|
||||
lexer.addToken(token)
|
||||
} else if lexer.char == '>' {
|
||||
token := lexer.newToken()
|
||||
token.kind = TokenKindReturnDirection
|
||||
token.location.SetWidth(2)
|
||||
|
||||
@ -360,7 +400,6 @@ func (lexer *LexingOperation) tokenizeDashBeginning () (err error) {
|
||||
} else if lexer.char >= '0' && lexer.char <= '9' {
|
||||
lexer.tokenizeNumberBeginning(true)
|
||||
} else {
|
||||
token := lexer.newToken()
|
||||
token.kind = TokenKindMinus
|
||||
lexer.addToken(token)
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
package lexer
|
||||
|
||||
import "testing"
|
||||
import "git.tebibyte.media/sashakoshka/arf/file"
|
||||
import "git.tebibyte.media/sashakoshka/arf/types"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/file"
|
||||
import "git.tebibyte.media/arf/arf/types"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
func quickToken (width int, kind TokenKind, value any) (token Token) {
|
||||
token.location.SetWidth(width)
|
||||
@ -152,19 +152,27 @@ func TestTokenizeAll (test *testing.T) {
|
||||
quickToken(1, TokenKindAt, nil),
|
||||
quickToken(1, TokenKindExclamation, nil),
|
||||
quickToken(1, TokenKindPercent, nil),
|
||||
quickToken(2, TokenKindPercentAssignment, nil),
|
||||
quickToken(1, TokenKindTilde, nil),
|
||||
quickToken(2, TokenKindTildeAssignment, nil),
|
||||
quickToken(1, TokenKindEqualTo, nil),
|
||||
quickToken(2, TokenKindNotEqualTo, nil),
|
||||
quickToken(1, TokenKindLessThan, nil),
|
||||
quickToken(2, TokenKindLessThanEqualTo, nil),
|
||||
quickToken(2, TokenKindLShift, nil),
|
||||
quickToken(3, TokenKindLShiftAssignment, nil),
|
||||
quickToken(1, TokenKindGreaterThan, nil),
|
||||
quickToken(2, TokenKindGreaterThanEqualTo, nil),
|
||||
quickToken(2, TokenKindRShift, nil),
|
||||
quickToken(3, TokenKindRShiftAssignment, nil),
|
||||
quickToken(1, TokenKindBinaryOr, nil),
|
||||
quickToken(2, TokenKindBinaryOrAssignment, nil),
|
||||
quickToken(2, TokenKindLogicalOr, nil),
|
||||
quickToken(1, TokenKindBinaryAnd, nil),
|
||||
quickToken(2, TokenKindBinaryAndAssignment, nil),
|
||||
quickToken(2, TokenKindLogicalAnd, nil),
|
||||
quickToken(1, TokenKindBinaryXor, nil),
|
||||
quickToken(2, TokenKindBinaryXorAssignment, nil),
|
||||
quickToken(1, TokenKindNewline, nil),
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package lexer
|
||||
|
||||
import "strconv"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// tokenizeSymbolBeginning lexes a token that starts with a number.
|
||||
func (lexer *LexingOperation) tokenizeNumberBeginning (negative bool) (err error) {
|
||||
@ -36,10 +36,17 @@ func (lexer *LexingOperation) tokenizeNumberBeginning (negative bool) (err error
|
||||
isFloat, amountRead,
|
||||
err = lexer.tokenizeNumber(10)
|
||||
|
||||
} else if lexer.char >= '0' && lexer.char <= '9' {
|
||||
} else if lexer.char >= '0' && lexer.char <= '7' {
|
||||
intNumber, floatNumber,
|
||||
isFloat, amountRead,
|
||||
err = lexer.tokenizeNumber(8)
|
||||
} else if lexer.char != ' ' { // a space should correctly
|
||||
err = infoerr.NewError ( // terminate this
|
||||
lexer.file.Location(1),
|
||||
"unexpected rune '" + string(lexer.char) +
|
||||
"' in integer literal",
|
||||
infoerr.ErrorKindError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
intNumber, floatNumber,
|
||||
|
@ -1,7 +1,7 @@
|
||||
package lexer
|
||||
|
||||
import "strconv"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// tokenizeString tokenizes a string or rune literal.
|
||||
func (lexer *LexingOperation) tokenizeString (isRuneLiteral bool) (err error) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package lexer
|
||||
|
||||
import "fmt"
|
||||
import "git.tebibyte.media/sashakoshka/arf/file"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/file"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// TokenKind is an enum represzenting what role a token has.
|
||||
type TokenKind int
|
||||
@ -43,20 +43,28 @@ const (
|
||||
TokenKindAt
|
||||
TokenKindExclamation
|
||||
TokenKindPercent
|
||||
TokenKindPercentAssignment
|
||||
TokenKindTilde
|
||||
TokenKindTildeAssignment
|
||||
|
||||
TokenKindEqualTo
|
||||
TokenKindNotEqualTo
|
||||
TokenKindLessThanEqualTo
|
||||
TokenKindLessThan
|
||||
TokenKindLShift
|
||||
TokenKindLShiftAssignment
|
||||
TokenKindGreaterThan
|
||||
TokenKindGreaterThanEqualTo
|
||||
TokenKindRShift
|
||||
TokenKindRShiftAssignment
|
||||
TokenKindBinaryOr
|
||||
TokenKindBinaryOrAssignment
|
||||
TokenKindLogicalOr
|
||||
TokenKindBinaryAnd
|
||||
TokenKindBinaryAndAssignment
|
||||
TokenKindLogicalAnd
|
||||
TokenKindBinaryXor
|
||||
TokenKindBinaryXorAssignment
|
||||
)
|
||||
|
||||
// Token represents a single token. It holds its location in the file, as well
|
||||
@ -67,6 +75,14 @@ type Token struct {
|
||||
value any
|
||||
}
|
||||
|
||||
// NewToken provides a way for a new token to be created by other modules for
|
||||
// comparison purposes.
|
||||
func NewToken (kind TokenKind, value any) (token Token) {
|
||||
token.kind = kind
|
||||
token.value = value
|
||||
return
|
||||
}
|
||||
|
||||
// Kind returns the semantic role of the token.
|
||||
func (token Token) Kind () (kind TokenKind) {
|
||||
return token.kind
|
||||
@ -175,8 +191,12 @@ func (tokenKind TokenKind) Describe () (description string) {
|
||||
description = "Exclamation"
|
||||
case TokenKindPercent:
|
||||
description = "Percent"
|
||||
case TokenKindPercentAssignment:
|
||||
description = "PercentAssignment"
|
||||
case TokenKindTilde:
|
||||
description = "Tilde"
|
||||
case TokenKindTildeAssignment:
|
||||
description = "TildeAssignment"
|
||||
case TokenKindEqualTo:
|
||||
description = "EqualTo"
|
||||
case TokenKindNotEqualTo:
|
||||
@ -187,20 +207,32 @@ func (tokenKind TokenKind) Describe () (description string) {
|
||||
description = "LessThanEqualTo"
|
||||
case TokenKindLShift:
|
||||
description = "LShift"
|
||||
case TokenKindLShiftAssignment:
|
||||
description = "LShiftAssignment"
|
||||
case TokenKindGreaterThan:
|
||||
description = "GreaterThan"
|
||||
case TokenKindGreaterThanEqualTo:
|
||||
description = "GreaterThanEqualTo"
|
||||
case TokenKindRShift:
|
||||
description = "RShift"
|
||||
case TokenKindRShiftAssignment:
|
||||
description = "RShiftAssignment"
|
||||
case TokenKindBinaryOr:
|
||||
description = "BinaryOr"
|
||||
case TokenKindBinaryOrAssignment:
|
||||
description = "BinaryOrAssignment"
|
||||
case TokenKindLogicalOr:
|
||||
description = "LogicalOr"
|
||||
case TokenKindBinaryAnd:
|
||||
description = "BinaryAnd"
|
||||
case TokenKindBinaryAndAssignment:
|
||||
description = "BinaryAndAssignment"
|
||||
case TokenKindLogicalAnd:
|
||||
description = "LogicalAnd"
|
||||
case TokenKindBinaryXor:
|
||||
description = "BinaryXor"
|
||||
case TokenKindBinaryXorAssignment:
|
||||
description = "BinaryXorAssignment"
|
||||
}
|
||||
|
||||
return
|
||||
|
9
main.go
9
main.go
@ -1,9 +0,0 @@
|
||||
package arf
|
||||
|
||||
import "io"
|
||||
import "git.tebibyte.media/sashakoshka/arf/parser"
|
||||
|
||||
func CompileModule (modulePath string, output io.Writer) (err error) {
|
||||
_, err = parser.Parse(modulePath)
|
||||
return
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/arf/lexer"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
var validArgumentStartTokens = []lexer.TokenKind {
|
||||
lexer.TokenKindName,
|
||||
@ -12,7 +12,6 @@ var validArgumentStartTokens = []lexer.TokenKind {
|
||||
lexer.TokenKindString,
|
||||
lexer.TokenKindRune,
|
||||
|
||||
lexer.TokenKindLBrace,
|
||||
lexer.TokenKindLBracket,
|
||||
}
|
||||
|
||||
@ -29,6 +28,8 @@ func (parser *ParsingOperation) parseArgument () (argument Argument, err error)
|
||||
if err != nil { return }
|
||||
|
||||
if parser.token.Is(lexer.TokenKindColon) {
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
var what Type
|
||||
what, err = parser.parseType()
|
||||
if err != nil { return }
|
||||
@ -77,9 +78,9 @@ func (parser *ParsingOperation) parseArgument () (argument Argument, err error)
|
||||
argument.value = parser.token.Value().(rune)
|
||||
parser.nextToken()
|
||||
|
||||
// case lexer.TokenKindLBrace:
|
||||
|
||||
// case lexer.TokenKindLBracket:
|
||||
case lexer.TokenKindLBracket:
|
||||
argument.kind = ArgumentKindPhrase
|
||||
argument.value, err = parser.parseArgumentLevelPhrase()
|
||||
|
||||
default:
|
||||
panic (
|
||||
|
@ -1,7 +1,7 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/arf/lexer"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// parse body parses the body of an arf file, after the metadata header.
|
||||
func (parser *ParsingOperation) parseBody () (err error) {
|
||||
@ -57,6 +57,14 @@ func (parser *ParsingOperation) parseBody () (err error) {
|
||||
parser.tree.enumSections[section.name] = section
|
||||
if err != nil { return }
|
||||
case "func":
|
||||
var section *FuncSection
|
||||
section, err = parser.parseFuncSection()
|
||||
if parser.tree.funcSections == nil {
|
||||
parser.tree.funcSections =
|
||||
make(map[string] *FuncSection)
|
||||
}
|
||||
parser.tree.funcSections[section.name] = section
|
||||
if err != nil { return }
|
||||
default:
|
||||
err = parser.token.NewError (
|
||||
"unknown section type \"" + sectionType + "\"",
|
||||
|
@ -1,8 +1,8 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/arf/types"
|
||||
import "git.tebibyte.media/sashakoshka/arf/lexer"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/types"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// parseData parses a data section.
|
||||
func (parser *ParsingOperation) parseDataSection () (
|
||||
@ -73,7 +73,7 @@ func (parser *ParsingOperation) parseInitializationValues (
|
||||
var initializationValues ObjectInitializationValues
|
||||
initializationValues, err = parser.parseObjectInitializationValues()
|
||||
initializationArgument.kind = ArgumentKindObjectInitializationValues
|
||||
initializationArgument.value = &initializationValues
|
||||
initializationArgument.value = initializationValues
|
||||
|
||||
} else {
|
||||
|
||||
@ -82,7 +82,7 @@ func (parser *ParsingOperation) parseInitializationValues (
|
||||
var initializationValues ArrayInitializationValues
|
||||
initializationValues, err = parser.parseArrayInitializationValues()
|
||||
initializationArgument.kind = ArgumentKindArrayInitializationValues
|
||||
initializationArgument.value = &initializationValues
|
||||
initializationArgument.value = initializationValues
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -1,8 +1,8 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/arf/types"
|
||||
import "git.tebibyte.media/sashakoshka/arf/lexer"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/types"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
func (parser *ParsingOperation) parseEnumSection () (
|
||||
section *EnumSection,
|
||||
|
@ -1,8 +1,8 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/arf/types"
|
||||
import "git.tebibyte.media/sashakoshka/arf/lexer"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/types"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// parseFaceSection parses an interface section.
|
||||
func (parser *ParsingOperation) parseFaceSection () (
|
||||
@ -64,7 +64,6 @@ func (parser *ParsingOperation) parseFaceSection () (
|
||||
|
||||
if err != nil { return }
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// parseFaceBehavior parses a single interface behavior. Indentation level is
|
||||
@ -127,6 +126,4 @@ func (parser *ParsingOperation) parseFaceBehavior () (
|
||||
declaration)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
205
parser/func.go
Normal file
205
parser/func.go
Normal file
@ -0,0 +1,205 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/arf/arf/types"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// parseFunc parses a function section.
|
||||
func (parser *ParsingOperation) parseFuncSection () (
|
||||
section *FuncSection,
|
||||
err error,
|
||||
) {
|
||||
err = parser.expect(lexer.TokenKindName)
|
||||
if err != nil { return }
|
||||
|
||||
section = &FuncSection { location: parser.token.Location() }
|
||||
|
||||
// get permission
|
||||
err = parser.nextToken(lexer.TokenKindPermission)
|
||||
if err != nil { return }
|
||||
section.permission = parser.token.Value().(types.Permission)
|
||||
|
||||
// get name
|
||||
err = parser.nextToken(lexer.TokenKindName)
|
||||
if err != nil { return }
|
||||
section.name = parser.token.Value().(string)
|
||||
|
||||
// get arguments
|
||||
err = parser.nextToken(lexer.TokenKindNewline)
|
||||
if err != nil { return }
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
err = parser.parseFuncArguments(section)
|
||||
if err != nil { return }
|
||||
|
||||
// check to see if the function is external
|
||||
if !parser.token.Is(lexer.TokenKindIndent) { return }
|
||||
if parser.token.Value().(int) != 1 { return }
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
|
||||
if parser.token.Is(lexer.TokenKindName) &&
|
||||
parser.token.Value().(string) == "external" {
|
||||
|
||||
section.external = true
|
||||
err = parser.nextToken(lexer.TokenKindNewline)
|
||||
if err != nil { return }
|
||||
if err != nil { return }
|
||||
err = parser.nextToken()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// if it isn't, backtrack to the start of the line
|
||||
parser.previousToken()
|
||||
|
||||
// parse root block
|
||||
section.root, err = parser.parseBlock(1)
|
||||
if err != nil { return }
|
||||
|
||||
if len(section.root) == 0 {
|
||||
infoerr.NewError (section.location,
|
||||
"this function has nothing in it",
|
||||
infoerr.ErrorKindWarn).Print()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseFuncArguments parses a function's inputs, outputs, and reciever if that
|
||||
// exists.
|
||||
func (parser *ParsingOperation) parseFuncArguments (
|
||||
into *FuncSection,
|
||||
) (
|
||||
err error,
|
||||
) {
|
||||
for {
|
||||
// if we've left the block, stop parsing
|
||||
if !parser.token.Is(lexer.TokenKindIndent) ||
|
||||
parser.token.Value().(int) != 1 {
|
||||
|
||||
if into.receiver != nil {
|
||||
err = parser.token.NewError (
|
||||
"func section terminated without a " +
|
||||
"separator token",
|
||||
infoerr.ErrorKindError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// determine whether this is an input, output, or the method
|
||||
// reciever
|
||||
err = parser.nextToken (
|
||||
lexer.TokenKindAt,
|
||||
lexer.TokenKindLessThan,
|
||||
lexer.TokenKindGreaterThan,
|
||||
lexer.TokenKindSeparator)
|
||||
if err != nil { return }
|
||||
|
||||
startToken := parser.token
|
||||
if parser.token.Is(lexer.TokenKindSeparator) {
|
||||
// if we have encountered a separator, that means our
|
||||
// work is done here.
|
||||
err = parser.nextToken(lexer.TokenKindNewline)
|
||||
if err != nil { return }
|
||||
err = parser.nextToken()
|
||||
return
|
||||
}
|
||||
|
||||
switch startToken.Kind() {
|
||||
case lexer.TokenKindAt:
|
||||
reciever := Declaration { }
|
||||
reciever.location = parser.token.Location()
|
||||
|
||||
// get name
|
||||
err = parser.nextToken(lexer.TokenKindName)
|
||||
if err != nil { return }
|
||||
reciever.name = parser.token.Value().(string)
|
||||
|
||||
// get type
|
||||
err = parser.nextToken(lexer.TokenKindColon)
|
||||
if err != nil { return }
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
reciever.what, err = parser.parseType()
|
||||
if err != nil { return }
|
||||
|
||||
if into.receiver != nil {
|
||||
err = startToken.NewError (
|
||||
"cannot have more than one method " +
|
||||
"receiver",
|
||||
infoerr.ErrorKindError)
|
||||
return
|
||||
} else {
|
||||
into.receiver = &reciever
|
||||
}
|
||||
|
||||
err = parser.expect(lexer.TokenKindNewline)
|
||||
if err != nil { return }
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
|
||||
case lexer.TokenKindGreaterThan:
|
||||
input := Declaration { }
|
||||
input.location = parser.token.Location()
|
||||
|
||||
// get name
|
||||
err = parser.nextToken(lexer.TokenKindName)
|
||||
if err != nil { return }
|
||||
input.name = parser.token.Value().(string)
|
||||
|
||||
// get type
|
||||
err = parser.nextToken(lexer.TokenKindColon)
|
||||
if err != nil { return }
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
input.what, err = parser.parseType()
|
||||
if err != nil { return }
|
||||
|
||||
into.inputs = append(into.inputs, input)
|
||||
|
||||
err = parser.expect(lexer.TokenKindNewline)
|
||||
if err != nil { return }
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
|
||||
case lexer.TokenKindLessThan:
|
||||
output := FuncOutput { }
|
||||
output.location = parser.token.Location()
|
||||
|
||||
// get name
|
||||
err = parser.nextToken(lexer.TokenKindName)
|
||||
if err != nil { return }
|
||||
output.name = parser.token.Value().(string)
|
||||
|
||||
// get type
|
||||
err = parser.nextToken(lexer.TokenKindColon)
|
||||
if err != nil { return }
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
output.what, err = parser.parseType()
|
||||
if err != nil { return }
|
||||
|
||||
// parse default value
|
||||
if parser.token.Is(lexer.TokenKindNewline) {
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
|
||||
output.defaultValue, err =
|
||||
parser.parseInitializationValues(1)
|
||||
into.outputs = append(into.outputs, output)
|
||||
if err != nil { return }
|
||||
} else {
|
||||
output.defaultValue, err =
|
||||
parser.parseArgument()
|
||||
into.outputs = append(into.outputs, output)
|
||||
if err != nil { return }
|
||||
|
||||
err = parser.expect(lexer.TokenKindNewline)
|
||||
if err != nil { return }
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
119
parser/func_test.go
Normal file
119
parser/func_test.go
Normal file
@ -0,0 +1,119 @@
|
||||
package parser
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFunc (test *testing.T) {
|
||||
checkTree ("../tests/parser/func",
|
||||
`:arf
|
||||
---
|
||||
func ro aBasicExternal
|
||||
> someInput:Int:mut
|
||||
< someOutput:Int 4
|
||||
---
|
||||
external
|
||||
func ro bMethod
|
||||
@ bird:{Bird}
|
||||
> someInput:Int:mut
|
||||
< someOutput:Int 4
|
||||
---
|
||||
external
|
||||
func ro cBasicPhrases
|
||||
---
|
||||
[fn 329 983 7]
|
||||
[fn 329 983 7]
|
||||
[fn 329 983 57]
|
||||
[fn [gn 329 983 57] 123]
|
||||
func ro dArgumentTypes
|
||||
---
|
||||
[bird tree butterfly.wing "hello world" grass:{Int:mut 8}]
|
||||
func ro eMath
|
||||
> x:Int
|
||||
> y:Int
|
||||
< z:Int
|
||||
---
|
||||
[++ x]
|
||||
[-- y]
|
||||
[set z [+ [* 250 0] 98 x [/ 9832 y] 930]]
|
||||
[! true]
|
||||
[~ 1]
|
||||
[~= x]
|
||||
[% 873 32]
|
||||
[= 5 5]
|
||||
[!= 4 4]
|
||||
[<= 4 98]
|
||||
[< 4 98]
|
||||
[<< 15 4]
|
||||
[<<= x 4]
|
||||
[>= 98 4]
|
||||
[> 98 4]
|
||||
[>> 240 4]
|
||||
[>>= x 4]
|
||||
[| 1 2]
|
||||
[|= x 2]
|
||||
[& 6 3]
|
||||
[&= x 3]
|
||||
[&& true true]
|
||||
[|| true false]
|
||||
func ro fReturnDirection
|
||||
< err:Error
|
||||
---
|
||||
[someFunc 498 2980 90] -> thing:Int err
|
||||
[otherFunc] -> thing err:Error
|
||||
[fn 329 983 57] -> thing:Int err
|
||||
func ro gControlFlow
|
||||
---
|
||||
[defer]
|
||||
[something]
|
||||
[otherThing]
|
||||
[if condition]
|
||||
[something]
|
||||
[if condition]
|
||||
[something]
|
||||
[elseif]
|
||||
[otherThing]
|
||||
[else]
|
||||
[finalThing]
|
||||
[while [< x 432]]
|
||||
[something]
|
||||
[switch value]
|
||||
[: 324]
|
||||
[something]
|
||||
[: 93284]
|
||||
[otherThing]
|
||||
[: 9128 34738 7328]
|
||||
[multipleCases]
|
||||
[:]
|
||||
[defaultThing]
|
||||
[for index:Size element:Int someArray]
|
||||
[something]
|
||||
[someNextThing]
|
||||
[justMakingSureBlockParsingWorks]
|
||||
[if condition]
|
||||
[if condition]
|
||||
[nestedThing]
|
||||
[else]
|
||||
[otherThing]
|
||||
[else]
|
||||
[if condition]
|
||||
[nestedThing]
|
||||
[else]
|
||||
[otherThing]
|
||||
func ro hSetPhrase
|
||||
---
|
||||
[set x:Int 3]
|
||||
[set y:{Int} [loc x]]
|
||||
[set z:{Int 8}]
|
||||
398
|
||||
9
|
||||
2309
|
||||
983
|
||||
-2387
|
||||
478
|
||||
555
|
||||
123
|
||||
[set bird:Bird]
|
||||
.that
|
||||
.whenYou 99999
|
||||
.this 324
|
||||
`, test)
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/arf/lexer"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// parseMeta parsese the metadata header at the top of an arf file.
|
||||
func (parser *ParsingOperation) parseMeta () (err error) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/arf/types"
|
||||
import "git.tebibyte.media/sashakoshka/arf/lexer"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/types"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// parseObjtSection parses an object type definition. This allows for structured
|
||||
// types to be defined, and for member variables to be added and overridden.
|
||||
@ -94,8 +94,6 @@ func (parser *ParsingOperation) parseObjtMember () (
|
||||
member.what, err = parser.parseType()
|
||||
if err != nil { return }
|
||||
|
||||
println(parser.token.Describe())
|
||||
|
||||
// if there is a bit width, get it
|
||||
if parser.token.Is(lexer.TokenKindBinaryAnd) {
|
||||
err = parser.nextToken(lexer.TokenKindUInt)
|
||||
|
@ -3,9 +3,9 @@ package parser
|
||||
import "io"
|
||||
import "os"
|
||||
import "path/filepath"
|
||||
import "git.tebibyte.media/sashakoshka/arf/file"
|
||||
import "git.tebibyte.media/sashakoshka/arf/lexer"
|
||||
import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/file"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// ParsingOperation holds information about an ongoing parsing operation.
|
||||
type ParsingOperation struct {
|
||||
|
354
parser/phrase.go
Normal file
354
parser/phrase.go
Normal file
@ -0,0 +1,354 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
|
||||
// operatorTokens lists all symbolic tokens that can act as a command to a
|
||||
// phrase.
|
||||
var operatorTokens = []lexer.TokenKind {
|
||||
lexer.TokenKindColon,
|
||||
lexer.TokenKindPlus,
|
||||
lexer.TokenKindMinus,
|
||||
lexer.TokenKindIncrement,
|
||||
lexer.TokenKindDecrement,
|
||||
lexer.TokenKindAsterisk,
|
||||
lexer.TokenKindSlash,
|
||||
lexer.TokenKindExclamation,
|
||||
lexer.TokenKindPercent,
|
||||
lexer.TokenKindPercentAssignment,
|
||||
lexer.TokenKindTilde,
|
||||
lexer.TokenKindTildeAssignment,
|
||||
lexer.TokenKindEqualTo,
|
||||
lexer.TokenKindNotEqualTo,
|
||||
lexer.TokenKindLessThanEqualTo,
|
||||
lexer.TokenKindLessThan,
|
||||
lexer.TokenKindLShift,
|
||||
lexer.TokenKindLShiftAssignment,
|
||||
lexer.TokenKindGreaterThan,
|
||||
lexer.TokenKindGreaterThanEqualTo,
|
||||
lexer.TokenKindRShift,
|
||||
lexer.TokenKindRShiftAssignment,
|
||||
lexer.TokenKindBinaryOr,
|
||||
lexer.TokenKindBinaryOrAssignment,
|
||||
lexer.TokenKindLogicalOr,
|
||||
lexer.TokenKindBinaryAnd,
|
||||
lexer.TokenKindBinaryAndAssignment,
|
||||
lexer.TokenKindLogicalAnd,
|
||||
lexer.TokenKindBinaryXor,
|
||||
lexer.TokenKindBinaryXorAssignment,
|
||||
}
|
||||
|
||||
// isTokenOperator returns whether or not the token is an operator token.
|
||||
func isTokenOperator (token lexer.Token) (isOperator bool) {
|
||||
for _, kind := range operatorTokens {
|
||||
if token.Is(kind) {
|
||||
isOperator = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// validPhraseStartTokens lists all tokens that are expected when parsing the
|
||||
// first part of a phrase.
|
||||
var validPhraseStartTokens = append (
|
||||
operatorTokens,
|
||||
lexer.TokenKindLBracket,
|
||||
lexer.TokenKindName,
|
||||
lexer.TokenKindString)
|
||||
|
||||
// validBlockLevelPhraseTokens lists all tokens that are expected when parsing
|
||||
// a block level phrase.
|
||||
var validBlockLevelPhraseTokens = append (
|
||||
validArgumentStartTokens,
|
||||
lexer.TokenKindNewline,
|
||||
lexer.TokenKindReturnDirection)
|
||||
|
||||
// validDelimitedPhraseTokens is like validBlockLevelPhraseTokens, but it also
|
||||
// includes a right brace token.
|
||||
var validDelimitedPhraseTokens = append (
|
||||
validArgumentStartTokens,
|
||||
lexer.TokenKindNewline,
|
||||
lexer.TokenKindIndent,
|
||||
lexer.TokenKindRBracket,
|
||||
lexer.TokenKindReturnDirection)
|
||||
|
||||
// controlFlowKinds contains a list of all phrase kinds that must have a block
|
||||
// underneath them.
|
||||
var controlFlowKinds = []PhraseKind {
|
||||
PhraseKindIf,
|
||||
PhraseKindElse,
|
||||
PhraseKindElseIf,
|
||||
PhraseKindFor,
|
||||
PhraseKindWhile,
|
||||
PhraseKindDefer,
|
||||
PhraseKindCase,
|
||||
}
|
||||
|
||||
// parseBlock parses an indented block of phrases
|
||||
func (parser *ParsingOperation) parseBlock (
|
||||
indent int,
|
||||
) (
|
||||
block Block,
|
||||
err error,
|
||||
) {
|
||||
for {
|
||||
// if we've left the block, stop parsing
|
||||
if !parser.token.Is(lexer.TokenKindIndent) { return }
|
||||
if parser.token.Value().(int) != indent { return }
|
||||
|
||||
var phrase Phrase
|
||||
phrase, err = parser.parseBlockLevelPhrase(indent)
|
||||
block = append(block, phrase)
|
||||
if err != nil { return }
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// parseBlockLevelPhrase parses a phrase that is not being used as an argument
|
||||
// to something else. This method is allowed to do things like parse return
|
||||
// directions, and indented blocks beneath the phrase.
|
||||
func (parser *ParsingOperation) parseBlockLevelPhrase (
|
||||
indent int,
|
||||
) (
|
||||
phrase Phrase,
|
||||
err error,
|
||||
) {
|
||||
if !parser.token.Is(lexer.TokenKindIndent) { return }
|
||||
if parser.token.Value().(int) != indent { return }
|
||||
err = parser.nextToken(validPhraseStartTokens...)
|
||||
if err != nil { return }
|
||||
|
||||
expectRightBracket := false
|
||||
if parser.token.Is(lexer.TokenKindLBracket) {
|
||||
expectRightBracket = true
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
}
|
||||
|
||||
// get command
|
||||
err = parser.expect(validPhraseStartTokens...)
|
||||
if err != nil { return }
|
||||
phrase.command, phrase.kind, err = parser.parsePhraseCommand()
|
||||
if err != nil { return }
|
||||
|
||||
for {
|
||||
if expectRightBracket {
|
||||
// delimited
|
||||
// [someFunc arg1 arg2 arg3] -> someVariable
|
||||
err = parser.expect(validDelimitedPhraseTokens...)
|
||||
if err != nil { return }
|
||||
|
||||
if parser.token.Is(lexer.TokenKindRBracket) {
|
||||
// this is an ending delimiter
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
break
|
||||
|
||||
} else if parser.token.Is(lexer.TokenKindNewline) {
|
||||
// we are delimited, so we can safely skip
|
||||
// newlines
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
continue
|
||||
|
||||
} else if parser.token.Is(lexer.TokenKindIndent) {
|
||||
// we are delimited, so we can safely skip
|
||||
// indents
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// not delimited
|
||||
// someFunc arg1 arg2 arg3 -> someVariable
|
||||
err = parser.expect(validBlockLevelPhraseTokens...)
|
||||
if err != nil { return }
|
||||
|
||||
if parser.token.Is(lexer.TokenKindReturnDirection) {
|
||||
// we've reached a return direction, so that
|
||||
// means this is the end of the phrase
|
||||
break
|
||||
} else if parser.token.Is(lexer.TokenKindNewline) {
|
||||
// we've reached the end of the line, so that
|
||||
// means this is the end of the phrase.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// this is an argument
|
||||
var argument Argument
|
||||
argument, err = parser.parseArgument()
|
||||
phrase.arguments = append(phrase.arguments, argument)
|
||||
}
|
||||
|
||||
// expect newline or return direction
|
||||
err = parser.expect (
|
||||
lexer.TokenKindNewline,
|
||||
lexer.TokenKindReturnDirection)
|
||||
if err != nil { return }
|
||||
expectReturnDirection := parser.token.Is(lexer.TokenKindReturnDirection)
|
||||
|
||||
// if we have hit a return direction, parse it...
|
||||
if expectReturnDirection {
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
|
||||
for {
|
||||
err = parser.expect (
|
||||
lexer.TokenKindNewline,
|
||||
lexer.TokenKindName)
|
||||
if err != nil { return }
|
||||
// ...until we hit a newline
|
||||
if parser.token.Is(lexer.TokenKindNewline) { break }
|
||||
|
||||
var returnTo Argument
|
||||
returnTo, err = parser.parseArgument()
|
||||
if err != nil { return }
|
||||
|
||||
phrase.returnsTo = append(phrase.returnsTo, returnTo)
|
||||
}
|
||||
}
|
||||
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
|
||||
// if this is a set phrase, parse initialization values under it
|
||||
if phrase.kind == PhraseKindSet {
|
||||
var values Argument
|
||||
values, err = parser.parseInitializationValues(indent)
|
||||
|
||||
if values.kind != ArgumentKindNil {
|
||||
phrase.arguments = append(phrase.arguments, values)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// if this is a control flow phrase, parse block under it
|
||||
isControlFlow := false
|
||||
for _, kind := range controlFlowKinds {
|
||||
if phrase.kind == kind {
|
||||
isControlFlow = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !isControlFlow { return }
|
||||
|
||||
// if it is any of those, parse the block under it
|
||||
phrase.block, err = parser.parseBlock(indent + 1)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseArgumentLevelPhrase parses a phrase that is being used as an argument to
|
||||
// something. It is forbidden from using return direction, and it must be
|
||||
// delimited by brackets.
|
||||
func (parser *ParsingOperation) parseArgumentLevelPhrase () (
|
||||
phrase Phrase,
|
||||
err error,
|
||||
) {
|
||||
err = parser.expect(lexer.TokenKindLBracket)
|
||||
if err != nil { return }
|
||||
|
||||
// get command
|
||||
err = parser.nextToken(validPhraseStartTokens...)
|
||||
if err != nil { return }
|
||||
phrase.command, phrase.kind, err = parser.parsePhraseCommand()
|
||||
if err != nil { return }
|
||||
|
||||
for {
|
||||
// delimited
|
||||
// [someFunc arg1 arg2 arg3] -> someVariable
|
||||
err = parser.expect(validDelimitedPhraseTokens...)
|
||||
if err != nil { return }
|
||||
|
||||
if parser.token.Is(lexer.TokenKindRBracket) {
|
||||
// this is an ending delimiter
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
return
|
||||
|
||||
} else if parser.token.Is(lexer.TokenKindNewline) {
|
||||
// we are delimited, so we can safely skip
|
||||
// newlines
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
continue
|
||||
|
||||
} else if parser.token.Is(lexer.TokenKindIndent) {
|
||||
// we are delimited, so we can safely skip
|
||||
// indents
|
||||
err = parser.nextToken()
|
||||
if err != nil { return }
|
||||
continue
|
||||
}
|
||||
|
||||
// this is an argument
|
||||
var argument Argument
|
||||
argument, err = parser.parseArgument()
|
||||
phrase.arguments = append(phrase.arguments, argument)
|
||||
}
|
||||
}
|
||||
|
||||
// parsePhraseCommand parses the command argument of a phrase.
|
||||
func (parser *ParsingOperation) parsePhraseCommand () (
|
||||
command Argument,
|
||||
kind PhraseKind,
|
||||
err error,
|
||||
) {
|
||||
if isTokenOperator(parser.token) {
|
||||
err = parser.expect(operatorTokens...)
|
||||
if err != nil { return }
|
||||
|
||||
command.location = parser.token.Location()
|
||||
command.kind = ArgumentKindOperator
|
||||
command.value = parser.token.Kind()
|
||||
|
||||
if parser.token.Is(lexer.TokenKindColon) {
|
||||
kind = PhraseKindCase
|
||||
} else {
|
||||
kind = PhraseKindOperator
|
||||
}
|
||||
|
||||
err = parser.nextToken()
|
||||
return
|
||||
}
|
||||
|
||||
// phrase command is not an operator, it is just a normal argument
|
||||
command, err = parser.parseArgument()
|
||||
if err != nil { return }
|
||||
|
||||
// determine semantic role of phrase
|
||||
if command.kind == ArgumentKindString {
|
||||
kind = PhraseKindCallExternal
|
||||
|
||||
} else if command.kind == ArgumentKindIdentifier {
|
||||
identifier := command.value.(Identifier)
|
||||
if len(identifier.trail) == 1 {
|
||||
switch identifier.trail[0] {
|
||||
case "set":
|
||||
kind = PhraseKindSet
|
||||
case "loc":
|
||||
kind = PhraseKindReference
|
||||
case "defer":
|
||||
kind = PhraseKindDefer
|
||||
case "if":
|
||||
kind = PhraseKindIf
|
||||
case "elseif":
|
||||
kind = PhraseKindElseIf
|
||||
case "else":
|
||||
kind = PhraseKindElse
|
||||
case "switch":
|
||||
kind = PhraseKindSwitch
|
||||
case "while":
|
||||
kind = PhraseKindWhile
|
||||
case "for":
|
||||
kind = PhraseKindFor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -3,10 +3,20 @@ package parser
|
||||
import "io"
|
||||
import "strings"
|
||||
import "testing"
|
||||
// import "git.tebibyte.media/sashakoshka/arf/types"
|
||||
// import "git.tebibyte.media/arf/arf/types"
|
||||
|
||||
func checkTree (modulePath string, correct string, test *testing.T) {
|
||||
tree, err := Parse(modulePath)
|
||||
if tree == nil {
|
||||
test.Log("TREE IS NIL!")
|
||||
if err != io.EOF && err != nil {
|
||||
test.Log("returned error:")
|
||||
test.Log(err)
|
||||
}
|
||||
test.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
treeString := tree.ToString(0)
|
||||
treeRunes := []rune(treeString)
|
||||
|
||||
|
@ -2,6 +2,7 @@ package parser
|
||||
|
||||
import "fmt"
|
||||
import "sort"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
|
||||
func doIndent (indent int, input ...string) (output string) {
|
||||
for index := 0; index < indent; index ++ {
|
||||
@ -70,10 +71,15 @@ func (tree *SyntaxTree) ToString (indent int) (output string) {
|
||||
for _, name := range dataSectionKeys {
|
||||
output += tree.dataSections[name].ToString(indent)
|
||||
}
|
||||
|
||||
funcSectionKeys := sortMapKeysAlphabetically(tree.funcSections)
|
||||
for _, name := range funcSectionKeys {
|
||||
output += tree.funcSections[name].ToString(indent)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (identifier *Identifier) ToString () (output string) {
|
||||
func (identifier Identifier) ToString () (output string) {
|
||||
for index, trailItem := range identifier.trail {
|
||||
if index > 0 {
|
||||
output += "."
|
||||
@ -110,13 +116,13 @@ func (what *Type) ToString () (output string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (declaration *Declaration) ToString () (output string) {
|
||||
func (declaration Declaration) ToString () (output string) {
|
||||
output += declaration.name + ":"
|
||||
output += declaration.what.ToString()
|
||||
return
|
||||
}
|
||||
|
||||
func (attributes *ObjectInitializationValues) ToString (
|
||||
func (attributes ObjectInitializationValues) ToString (
|
||||
indent int,
|
||||
) (
|
||||
output string,
|
||||
@ -136,7 +142,7 @@ func (attributes *ObjectInitializationValues) ToString (
|
||||
return
|
||||
}
|
||||
|
||||
func (values *ArrayInitializationValues) ToString (
|
||||
func (values ArrayInitializationValues) ToString (
|
||||
indent int,
|
||||
) (
|
||||
output string,
|
||||
@ -148,39 +154,6 @@ func (values *ArrayInitializationValues) ToString (
|
||||
return
|
||||
}
|
||||
|
||||
func (phrase *Phrase) ToString (indent int, breakLine bool) (output string) {
|
||||
if breakLine {
|
||||
output += doIndent (
|
||||
indent,
|
||||
"[", phrase.command.ToString(0, false))
|
||||
output += "\n"
|
||||
for _, argument := range phrase.arguments {
|
||||
output += doIndent (
|
||||
indent,
|
||||
argument.ToString(indent + 1, true))
|
||||
}
|
||||
} else {
|
||||
output += "[" + phrase.command.ToString(0, false)
|
||||
for _, argument := range phrase.arguments {
|
||||
output += " " + argument.ToString(0, false)
|
||||
}
|
||||
}
|
||||
|
||||
output += "]"
|
||||
|
||||
if len(phrase.returnsTo) > 0 {
|
||||
output += " ->"
|
||||
for _, returnItem := range phrase.returnsTo {
|
||||
output += " " + returnItem.ToString(0, false)
|
||||
}
|
||||
}
|
||||
|
||||
if breakLine {
|
||||
output += "\n"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (argument *Argument) ToString (indent int, breakLine bool) (output string) {
|
||||
if !breakLine { indent = 0 }
|
||||
if argument.kind == ArgumentKindNil {
|
||||
@ -191,30 +164,30 @@ func (argument *Argument) ToString (indent int, breakLine bool) (output string)
|
||||
|
||||
switch argument.kind {
|
||||
case ArgumentKindPhrase:
|
||||
output += argument.value.(*Phrase).ToString (
|
||||
output += argument.value.(Phrase).ToString (
|
||||
indent,
|
||||
breakLine)
|
||||
|
||||
case ArgumentKindObjectInitializationValues:
|
||||
// this should only appear in contexts where breakLine is true
|
||||
output += argument.value.(*ObjectInitializationValues).
|
||||
output += argument.value.(ObjectInitializationValues).
|
||||
ToString(indent)
|
||||
|
||||
case ArgumentKindArrayInitializationValues:
|
||||
// this should only appear in contexts where breakLine is true
|
||||
output += argument.value.(*ArrayInitializationValues).
|
||||
output += argument.value.(ArrayInitializationValues).
|
||||
ToString(indent)
|
||||
|
||||
case ArgumentKindIdentifier:
|
||||
output += doIndent (
|
||||
indent,
|
||||
argument.value.(*Identifier).ToString())
|
||||
argument.value.(Identifier).ToString())
|
||||
if breakLine { output += "\n" }
|
||||
|
||||
case ArgumentKindDeclaration:
|
||||
output += doIndent (
|
||||
indent,
|
||||
argument.value.(*Declaration).ToString())
|
||||
argument.value.(Declaration).ToString())
|
||||
if breakLine { output += "\n" }
|
||||
|
||||
case ArgumentKindInt, ArgumentKindUInt, ArgumentKindFloat:
|
||||
@ -234,10 +207,71 @@ func (argument *Argument) ToString (indent int, breakLine bool) (output string)
|
||||
if breakLine { output += "\n" }
|
||||
|
||||
case ArgumentKindOperator:
|
||||
// TODO
|
||||
// also when parsing this argument kind, don't do it in the
|
||||
// argument parsing function. do it specifically when parsing a
|
||||
// phrase command.
|
||||
var stringValue string
|
||||
switch argument.value.(lexer.TokenKind) {
|
||||
case lexer.TokenKindColon:
|
||||
stringValue = ":"
|
||||
case lexer.TokenKindPlus:
|
||||
stringValue = "+"
|
||||
case lexer.TokenKindMinus:
|
||||
stringValue = "-"
|
||||
case lexer.TokenKindIncrement:
|
||||
stringValue = "++"
|
||||
case lexer.TokenKindDecrement:
|
||||
stringValue = "--"
|
||||
case lexer.TokenKindAsterisk:
|
||||
stringValue = "*"
|
||||
case lexer.TokenKindSlash:
|
||||
stringValue = "/"
|
||||
case lexer.TokenKindExclamation:
|
||||
stringValue = "!"
|
||||
case lexer.TokenKindPercent:
|
||||
stringValue = "%"
|
||||
case lexer.TokenKindPercentAssignment:
|
||||
stringValue = "%="
|
||||
case lexer.TokenKindTilde:
|
||||
stringValue = "~"
|
||||
case lexer.TokenKindTildeAssignment:
|
||||
stringValue = "~="
|
||||
case lexer.TokenKindEqualTo:
|
||||
stringValue = "="
|
||||
case lexer.TokenKindNotEqualTo:
|
||||
stringValue = "!="
|
||||
case lexer.TokenKindLessThanEqualTo:
|
||||
stringValue = "<="
|
||||
case lexer.TokenKindLessThan:
|
||||
stringValue = "<"
|
||||
case lexer.TokenKindLShift:
|
||||
stringValue = "<<"
|
||||
case lexer.TokenKindLShiftAssignment:
|
||||
stringValue = "<<="
|
||||
case lexer.TokenKindGreaterThan:
|
||||
stringValue = ">"
|
||||
case lexer.TokenKindGreaterThanEqualTo:
|
||||
stringValue = ">="
|
||||
case lexer.TokenKindRShift:
|
||||
stringValue = ">>"
|
||||
case lexer.TokenKindRShiftAssignment:
|
||||
stringValue = ">>="
|
||||
case lexer.TokenKindBinaryOr:
|
||||
stringValue = "|"
|
||||
case lexer.TokenKindBinaryOrAssignment:
|
||||
stringValue = "|="
|
||||
case lexer.TokenKindLogicalOr:
|
||||
stringValue = "||"
|
||||
case lexer.TokenKindBinaryAnd:
|
||||
stringValue = "&"
|
||||
case lexer.TokenKindBinaryAndAssignment:
|
||||
stringValue = "&="
|
||||
case lexer.TokenKindLogicalAnd:
|
||||
stringValue = "&&"
|
||||
case lexer.TokenKindBinaryXor:
|
||||
stringValue = "^"
|
||||
case lexer.TokenKindBinaryXorAssignment:
|
||||
stringValue = "^="
|
||||
}
|
||||
output += doIndent(indent, stringValue)
|
||||
if breakLine { output += "\n" }
|
||||
}
|
||||
|
||||
return
|
||||
@ -389,3 +423,87 @@ func (behavior *FaceBehavior) ToString (indent int) (output string) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (phrase Phrase) ToString (indent int, ownLine bool) (output string) {
|
||||
if ownLine {
|
||||
output += doIndent(indent)
|
||||
}
|
||||
|
||||
var initializationValues Argument
|
||||
|
||||
output += "[" + phrase.command.ToString(0, false)
|
||||
for _, argument := range phrase.arguments {
|
||||
isInitializationValue :=
|
||||
argument.kind == ArgumentKindObjectInitializationValues ||
|
||||
argument.kind == ArgumentKindArrayInitializationValues
|
||||
if isInitializationValue {
|
||||
initializationValues = argument
|
||||
} else {
|
||||
output += " " + argument.ToString(0, false)
|
||||
}
|
||||
}
|
||||
output += "]"
|
||||
|
||||
if len(phrase.returnsTo) > 0 {
|
||||
output += " ->"
|
||||
for _, returnItem := range phrase.returnsTo {
|
||||
output += " " + returnItem.ToString(0, false)
|
||||
}
|
||||
}
|
||||
|
||||
if ownLine {
|
||||
output += "\n"
|
||||
if initializationValues.kind != ArgumentKindNil {
|
||||
output += initializationValues.ToString(indent + 1, true)
|
||||
}
|
||||
output += phrase.block.ToString(indent + 1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (block Block) ToString (indent int) (output string) {
|
||||
for _, phrase := range block {
|
||||
output += phrase.ToString(indent, true)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (funcOutput FuncOutput) ToString () (output string) {
|
||||
output += funcOutput.Declaration.ToString()
|
||||
if funcOutput.defaultValue.kind != ArgumentKindNil {
|
||||
output += " " + funcOutput.defaultValue.ToString(0, false)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (section *FuncSection) ToString (indent int) (output string) {
|
||||
output += doIndent (
|
||||
indent,
|
||||
"func ",
|
||||
section.permission.ToString(), " ",
|
||||
section.name, "\n")
|
||||
|
||||
if section.receiver != nil {
|
||||
output += doIndent (
|
||||
indent + 1,
|
||||
"@ ", section.receiver.ToString(), "\n")
|
||||
}
|
||||
|
||||
for _, inputItem := range section.inputs {
|
||||
output += doIndent(indent + 1, "> ", inputItem.ToString(), "\n")
|
||||
}
|
||||
|
||||
for _, outputItem := range section.outputs {
|
||||
output += doIndent(indent + 1, "< ", outputItem.ToString(), "\n")
|
||||
}
|
||||
|
||||
output += doIndent(indent + 1, "---\n")
|
||||
|
||||
if section.external {
|
||||
output += doIndent(indent + 1, "external\n")
|
||||
}
|
||||
|
||||
output += section.root.ToString(indent + 1)
|
||||
return
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/arf/file"
|
||||
import "git.tebibyte.media/sashakoshka/arf/types"
|
||||
import "git.tebibyte.media/arf/arf/file"
|
||||
import "git.tebibyte.media/arf/arf/types"
|
||||
|
||||
// SyntaxTree represents an abstract syntax tree. It covers an entire module. It
|
||||
// can be expected to be syntactically correct, but it might not be semantically
|
||||
@ -16,6 +16,7 @@ type SyntaxTree struct {
|
||||
enumSections map[string] *EnumSection
|
||||
faceSections map[string] *FaceSection
|
||||
dataSections map[string] *DataSection
|
||||
funcSections map[string] *FuncSection
|
||||
}
|
||||
|
||||
// Identifier represents a chain of arguments separated by a dot.
|
||||
@ -78,15 +79,6 @@ type ArrayInitializationValues struct {
|
||||
values []Argument
|
||||
}
|
||||
|
||||
// Phrase represents a function call or operator. In ARF they are the same
|
||||
// syntactical concept.
|
||||
type Phrase struct {
|
||||
location file.Location
|
||||
command Argument
|
||||
arguments []Argument
|
||||
returnsTo []Argument
|
||||
}
|
||||
|
||||
// ArgumentKind specifies the type of thing the value of an argument should be
|
||||
// cast to.
|
||||
type ArgumentKind int
|
||||
@ -97,7 +89,7 @@ const (
|
||||
// [name argument]
|
||||
// [name argument argument]
|
||||
// etc...
|
||||
ArgumentKindPhrase = iota
|
||||
ArgumentKindPhrase
|
||||
|
||||
// {name}
|
||||
ArgumentKindDereference
|
||||
@ -185,7 +177,7 @@ type ObjtMember struct {
|
||||
defaultValue Argument
|
||||
}
|
||||
|
||||
// ObjtSection represents an object type definition
|
||||
// ObjtSection represents an object type definition.
|
||||
type ObjtSection struct {
|
||||
location file.Location
|
||||
name string
|
||||
@ -195,6 +187,7 @@ type ObjtSection struct {
|
||||
members []ObjtMember
|
||||
}
|
||||
|
||||
// EnumMember represents a member of an enum section.
|
||||
type EnumMember struct {
|
||||
location file.Location
|
||||
name string
|
||||
@ -229,3 +222,60 @@ type FaceSection struct {
|
||||
permission types.Permission
|
||||
behaviors map[string] FaceBehavior
|
||||
}
|
||||
|
||||
// PhraseKind determines what semantic role a phrase plays.
|
||||
type PhraseKind int
|
||||
|
||||
const (
|
||||
PhraseKindCall = iota
|
||||
PhraseKindCallExternal
|
||||
PhraseKindOperator
|
||||
PhraseKindSet
|
||||
PhraseKindReference
|
||||
PhraseKindDefer
|
||||
PhraseKindIf
|
||||
PhraseKindElseIf
|
||||
PhraseKindElse
|
||||
PhraseKindSwitch
|
||||
PhraseKindCase
|
||||
PhraseKindWhile
|
||||
PhraseKindFor
|
||||
)
|
||||
|
||||
// Phrase represents a function call or operator. In ARF they are the same
|
||||
// syntactical concept.
|
||||
type Phrase struct {
|
||||
location file.Location
|
||||
command Argument
|
||||
arguments []Argument
|
||||
returnsTo []Argument
|
||||
|
||||
kind PhraseKind
|
||||
|
||||
// only applicable for control flow phrases
|
||||
block Block
|
||||
}
|
||||
|
||||
// Block represents a scoped/indented block of code.
|
||||
type Block []Phrase
|
||||
|
||||
// FuncOutput represents an input a function section. It is unlike an input in
|
||||
// that it can have a default value.
|
||||
type FuncOutput struct {
|
||||
Declaration
|
||||
defaultValue Argument
|
||||
}
|
||||
|
||||
// FuncSection represents a function section.
|
||||
type FuncSection struct {
|
||||
location file.Location
|
||||
name string
|
||||
permission types.Permission
|
||||
|
||||
receiver *Declaration
|
||||
inputs []Declaration
|
||||
outputs []FuncOutput
|
||||
root Block
|
||||
|
||||
external bool
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package parser
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/arf/types"
|
||||
import "git.tebibyte.media/sashakoshka/arf/lexer"
|
||||
// import "git.tebibyte.media/sashakoshka/arf/infoerr"
|
||||
import "git.tebibyte.media/arf/arf/types"
|
||||
import "git.tebibyte.media/arf/arf/lexer"
|
||||
// import "git.tebibyte.media/arf/arf/infoerr"
|
||||
|
||||
// parseTypeSection parses a blind type definition, meaning it can inherit from
|
||||
// anything including primitives, but cannot define structure.
|
||||
|
@ -1,3 +1,3 @@
|
||||
:arf
|
||||
--- rw -> -349820394 932748397 239485.37520 "hello world!\n" 'E' helloWorld:.,..[]{}
|
||||
+ - ++ -- * / @ ! % ~ = != < <= << > >= >> | || & &&
|
||||
+ - ++ -- * / @ ! % %= ~ ~= = != < <= << <<= > >= >> >>= | |= || & &= && ^ ^=
|
||||
|
135
tests/parser/func/main.arf
Normal file
135
tests/parser/func/main.arf
Normal file
@ -0,0 +1,135 @@
|
||||
:arf
|
||||
---
|
||||
func ro aBasicExternal
|
||||
> someInput:Int:mut
|
||||
< someOutput:Int 4
|
||||
---
|
||||
external
|
||||
|
||||
func ro bMethod
|
||||
@ bird:{Bird}
|
||||
> someInput:Int:mut
|
||||
< someOutput:Int 4
|
||||
---
|
||||
external
|
||||
|
||||
func ro cBasicPhrases
|
||||
---
|
||||
fn 329 983 07
|
||||
[fn 329 983 07]
|
||||
[fn
|
||||
329
|
||||
983
|
||||
071]
|
||||
fn [gn
|
||||
329 983
|
||||
071] 123
|
||||
|
||||
func ro dArgumentTypes
|
||||
---
|
||||
[bird tree butterfly.wing "hello world"
|
||||
grass:{Int:mut 8}]
|
||||
|
||||
func ro eMath
|
||||
> x:Int
|
||||
> y:Int
|
||||
< z:Int
|
||||
---
|
||||
++ x
|
||||
-- y
|
||||
set z [+ [* 0372 00] 98 x [/ 9832 y] 930]
|
||||
|
||||
! true
|
||||
~ 0b01
|
||||
~= x
|
||||
% 873 32
|
||||
|
||||
= 5 5
|
||||
!= 4 4
|
||||
|
||||
<= 4 98
|
||||
< 4 98
|
||||
<< 0x0F 4
|
||||
<<= x 4
|
||||
|
||||
>= 98 4
|
||||
> 98 4
|
||||
>> 0xF0 4
|
||||
>>= x 4
|
||||
|
||||
| 0b01 0b10
|
||||
|= x 0b10
|
||||
& 0b110 0b011
|
||||
&= x 0b011
|
||||
|
||||
&& true true
|
||||
|| true false
|
||||
|
||||
func ro fReturnDirection
|
||||
< err:Error
|
||||
---
|
||||
someFunc 498 2980 90 -> thing:Int err
|
||||
otherFunc -> thing err:Error
|
||||
|
||||
[fn
|
||||
329
|
||||
983
|
||||
071] -> thing:Int err
|
||||
|
||||
func ro gControlFlow
|
||||
---
|
||||
defer
|
||||
something
|
||||
otherThing
|
||||
|
||||
if condition
|
||||
something
|
||||
|
||||
if condition
|
||||
something
|
||||
elseif
|
||||
[otherThing]
|
||||
else
|
||||
finalThing
|
||||
|
||||
while [< x 432]
|
||||
something
|
||||
|
||||
switch value
|
||||
: 324
|
||||
something
|
||||
[: 93284]
|
||||
otherThing
|
||||
: 9128 34738 7328
|
||||
multipleCases
|
||||
:
|
||||
[defaultThing]
|
||||
|
||||
for index:Size element:Int someArray
|
||||
something
|
||||
someNextThing
|
||||
justMakingSureBlockParsingWorks
|
||||
|
||||
[if condition]
|
||||
if condition
|
||||
nestedThing
|
||||
else
|
||||
otherThing
|
||||
else
|
||||
if condition
|
||||
nestedThing
|
||||
else
|
||||
otherThing
|
||||
|
||||
func ro hSetPhrase
|
||||
---
|
||||
set x:Int 3
|
||||
# loc is a reference, similar to * in C
|
||||
set y:{Int} [loc x]
|
||||
set z:{Int 8}
|
||||
398 9 2309 983 -2387
|
||||
478 555 123
|
||||
set bird:Bird
|
||||
.that
|
||||
.whenYou 99999
|
||||
.this 324
|
Reference in New Issue
Block a user