func-section #1

Merged
sashakoshka merged 52 commits from func-section into main 2022-09-04 00:09:14 +00:00
27 changed files with 1200 additions and 149 deletions

View File

@ -2,7 +2,7 @@ package arfc
import "os" import "os"
import "fmt" import "fmt"
import "git.tebibyte.media/sashakoshka/arf" import "git.tebibyte.media/arf/arf"
func main () { func main () {
if len(os.Args) != 2 { if len(os.Args) != 2 {

View File

@ -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
View File

@ -1,3 +1,3 @@
module git.tebibyte.media/sashakoshka/arf module git.tebibyte.media/arf/arf
go 1.18 go 1.18

View File

@ -2,7 +2,7 @@ package infoerr
import "os" import "os"
import "fmt" import "fmt"
import "git.tebibyte.media/sashakoshka/arf/file" import "git.tebibyte.media/arf/arf/file"
type ErrorKind int type ErrorKind int

View File

@ -1,9 +1,9 @@
package lexer package lexer
import "io" import "io"
import "git.tebibyte.media/sashakoshka/arf/file" import "git.tebibyte.media/arf/arf/file"
import "git.tebibyte.media/sashakoshka/arf/types" import "git.tebibyte.media/arf/arf/types"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
// LexingOperation holds information about an ongoing lexing operataion. // LexingOperation holds information about an ongoing lexing operataion.
type LexingOperation struct { type LexingOperation struct {
@ -253,14 +253,26 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
lexer.addToken(token) lexer.addToken(token)
case '%': case '%':
token := lexer.newToken() token := lexer.newToken()
token.kind = TokenKindPercent
lexer.addToken(token)
err = lexer.nextRune() 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 '~': case '~':
token := lexer.newToken() token := lexer.newToken()
token.kind = TokenKindTilde
lexer.addToken(token)
err = lexer.nextRune() 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 '=': case '=':
token := lexer.newToken() token := lexer.newToken()
token.kind = TokenKindEqualTo token.kind = TokenKindEqualTo
@ -275,6 +287,11 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
token.kind = TokenKindLShift token.kind = TokenKindLShift
err = lexer.nextRune() err = lexer.nextRune()
token.location.SetWidth(2) token.location.SetWidth(2)
if lexer.char == '=' {
token.kind = TokenKindLShiftAssignment
err = lexer.nextRune()
token.location.SetWidth(3)
}
} else if lexer.char == '=' { } else if lexer.char == '=' {
token.kind = TokenKindLessThanEqualTo token.kind = TokenKindLessThanEqualTo
err = lexer.nextRune() err = lexer.nextRune()
@ -290,6 +307,11 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
token.kind = TokenKindRShift token.kind = TokenKindRShift
err = lexer.nextRune() err = lexer.nextRune()
token.location.SetWidth(2) token.location.SetWidth(2)
if lexer.char == '=' {
token.kind = TokenKindRShiftAssignment
err = lexer.nextRune()
token.location.SetWidth(3)
}
} else if lexer.char == '=' { } else if lexer.char == '=' {
token.kind = TokenKindGreaterThanEqualTo token.kind = TokenKindGreaterThanEqualTo
err = lexer.nextRune() err = lexer.nextRune()
@ -305,6 +327,10 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
token.kind = TokenKindLogicalOr token.kind = TokenKindLogicalOr
err = lexer.nextRune() err = lexer.nextRune()
token.location.SetWidth(2) token.location.SetWidth(2)
} else if lexer.char == '=' {
token.kind = TokenKindBinaryOrAssignment
err = lexer.nextRune()
token.location.SetWidth(2)
} }
lexer.addToken(token) lexer.addToken(token)
case '&': case '&':
@ -316,6 +342,21 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
token.kind = TokenKindLogicalAnd token.kind = TokenKindLogicalAnd
err = lexer.nextRune() err = lexer.nextRune()
token.location.SetWidth(2) 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) lexer.addToken(token)
default: default:
@ -331,11 +372,11 @@ func (lexer *LexingOperation) tokenizeSymbolBeginning () (err error) {
} }
func (lexer *LexingOperation) tokenizeDashBeginning () (err error) { func (lexer *LexingOperation) tokenizeDashBeginning () (err error) {
token := lexer.newToken()
err = lexer.nextRune() err = lexer.nextRune()
if err != nil { return } if err != nil { return }
if lexer.char == '-' { if lexer.char == '-' {
token := lexer.newToken()
token.kind = TokenKindDecrement token.kind = TokenKindDecrement
token.location.SetWidth(2) token.location.SetWidth(2)
@ -349,18 +390,16 @@ func (lexer *LexingOperation) tokenizeDashBeginning () (err error) {
} }
lexer.addToken(token) lexer.addToken(token)
} else if lexer.char == '>' { } else if lexer.char == '>' {
token := lexer.newToken()
token.kind = TokenKindReturnDirection token.kind = TokenKindReturnDirection
token.location.SetWidth(2) token.location.SetWidth(2)
err = lexer.nextRune() err = lexer.nextRune()
if err != nil { return } if err != nil { return }
lexer.addToken(token) lexer.addToken(token)
} else if lexer.char >= '0' && lexer.char <= '9' { } else if lexer.char >= '0' && lexer.char <= '9' {
lexer.tokenizeNumberBeginning(true) lexer.tokenizeNumberBeginning(true)
} else { } else {
token := lexer.newToken()
token.kind = TokenKindMinus token.kind = TokenKindMinus
lexer.addToken(token) lexer.addToken(token)
} }

View File

@ -1,9 +1,9 @@
package lexer package lexer
import "testing" import "testing"
import "git.tebibyte.media/sashakoshka/arf/file" import "git.tebibyte.media/arf/arf/file"
import "git.tebibyte.media/sashakoshka/arf/types" import "git.tebibyte.media/arf/arf/types"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
func quickToken (width int, kind TokenKind, value any) (token Token) { func quickToken (width int, kind TokenKind, value any) (token Token) {
token.location.SetWidth(width) token.location.SetWidth(width)
@ -152,19 +152,27 @@ func TestTokenizeAll (test *testing.T) {
quickToken(1, TokenKindAt, nil), quickToken(1, TokenKindAt, nil),
quickToken(1, TokenKindExclamation, nil), quickToken(1, TokenKindExclamation, nil),
quickToken(1, TokenKindPercent, nil), quickToken(1, TokenKindPercent, nil),
quickToken(2, TokenKindPercentAssignment, nil),
quickToken(1, TokenKindTilde, nil), quickToken(1, TokenKindTilde, nil),
quickToken(2, TokenKindTildeAssignment, nil),
quickToken(1, TokenKindEqualTo, nil), quickToken(1, TokenKindEqualTo, nil),
quickToken(2, TokenKindNotEqualTo, nil), quickToken(2, TokenKindNotEqualTo, nil),
quickToken(1, TokenKindLessThan, nil), quickToken(1, TokenKindLessThan, nil),
quickToken(2, TokenKindLessThanEqualTo, nil), quickToken(2, TokenKindLessThanEqualTo, nil),
quickToken(2, TokenKindLShift, nil), quickToken(2, TokenKindLShift, nil),
quickToken(3, TokenKindLShiftAssignment, nil),
quickToken(1, TokenKindGreaterThan, nil), quickToken(1, TokenKindGreaterThan, nil),
quickToken(2, TokenKindGreaterThanEqualTo, nil), quickToken(2, TokenKindGreaterThanEqualTo, nil),
quickToken(2, TokenKindRShift, nil), quickToken(2, TokenKindRShift, nil),
quickToken(3, TokenKindRShiftAssignment, nil),
quickToken(1, TokenKindBinaryOr, nil), quickToken(1, TokenKindBinaryOr, nil),
quickToken(2, TokenKindBinaryOrAssignment, nil),
quickToken(2, TokenKindLogicalOr, nil), quickToken(2, TokenKindLogicalOr, nil),
quickToken(1, TokenKindBinaryAnd, nil), quickToken(1, TokenKindBinaryAnd, nil),
quickToken(2, TokenKindBinaryAndAssignment, nil),
quickToken(2, TokenKindLogicalAnd, nil), quickToken(2, TokenKindLogicalAnd, nil),
quickToken(1, TokenKindBinaryXor, nil),
quickToken(2, TokenKindBinaryXorAssignment, nil),
quickToken(1, TokenKindNewline, nil), quickToken(1, TokenKindNewline, nil),
) )
} }

View File

@ -1,7 +1,7 @@
package lexer package lexer
import "strconv" 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. // tokenizeSymbolBeginning lexes a token that starts with a number.
func (lexer *LexingOperation) tokenizeNumberBeginning (negative bool) (err error) { func (lexer *LexingOperation) tokenizeNumberBeginning (negative bool) (err error) {
@ -36,10 +36,17 @@ func (lexer *LexingOperation) tokenizeNumberBeginning (negative bool) (err error
isFloat, amountRead, isFloat, amountRead,
err = lexer.tokenizeNumber(10) err = lexer.tokenizeNumber(10)
} else if lexer.char >= '0' && lexer.char <= '9' { } else if lexer.char >= '0' && lexer.char <= '7' {
intNumber, floatNumber, intNumber, floatNumber,
isFloat, amountRead, isFloat, amountRead,
err = lexer.tokenizeNumber(8) 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 { } else {
intNumber, floatNumber, intNumber, floatNumber,

View File

@ -1,7 +1,7 @@
package lexer package lexer
import "strconv" import "strconv"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
// tokenizeString tokenizes a string or rune literal. // tokenizeString tokenizes a string or rune literal.
func (lexer *LexingOperation) tokenizeString (isRuneLiteral bool) (err error) { func (lexer *LexingOperation) tokenizeString (isRuneLiteral bool) (err error) {

View File

@ -1,8 +1,8 @@
package lexer package lexer
import "fmt" import "fmt"
import "git.tebibyte.media/sashakoshka/arf/file" import "git.tebibyte.media/arf/arf/file"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
// TokenKind is an enum represzenting what role a token has. // TokenKind is an enum represzenting what role a token has.
type TokenKind int type TokenKind int
@ -43,20 +43,28 @@ const (
TokenKindAt TokenKindAt
TokenKindExclamation TokenKindExclamation
TokenKindPercent TokenKindPercent
TokenKindPercentAssignment
TokenKindTilde TokenKindTilde
TokenKindTildeAssignment
TokenKindEqualTo TokenKindEqualTo
TokenKindNotEqualTo TokenKindNotEqualTo
TokenKindLessThanEqualTo TokenKindLessThanEqualTo
TokenKindLessThan TokenKindLessThan
TokenKindLShift TokenKindLShift
TokenKindLShiftAssignment
TokenKindGreaterThan TokenKindGreaterThan
TokenKindGreaterThanEqualTo TokenKindGreaterThanEqualTo
TokenKindRShift TokenKindRShift
TokenKindRShiftAssignment
TokenKindBinaryOr TokenKindBinaryOr
TokenKindBinaryOrAssignment
TokenKindLogicalOr TokenKindLogicalOr
TokenKindBinaryAnd TokenKindBinaryAnd
TokenKindBinaryAndAssignment
TokenKindLogicalAnd TokenKindLogicalAnd
TokenKindBinaryXor
TokenKindBinaryXorAssignment
) )
// Token represents a single token. It holds its location in the file, as well // Token represents a single token. It holds its location in the file, as well
@ -67,6 +75,14 @@ type Token struct {
value any 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. // Kind returns the semantic role of the token.
func (token Token) Kind () (kind TokenKind) { func (token Token) Kind () (kind TokenKind) {
return token.kind return token.kind
@ -175,8 +191,12 @@ func (tokenKind TokenKind) Describe () (description string) {
description = "Exclamation" description = "Exclamation"
case TokenKindPercent: case TokenKindPercent:
description = "Percent" description = "Percent"
case TokenKindPercentAssignment:
description = "PercentAssignment"
case TokenKindTilde: case TokenKindTilde:
description = "Tilde" description = "Tilde"
case TokenKindTildeAssignment:
description = "TildeAssignment"
case TokenKindEqualTo: case TokenKindEqualTo:
description = "EqualTo" description = "EqualTo"
case TokenKindNotEqualTo: case TokenKindNotEqualTo:
@ -187,20 +207,32 @@ func (tokenKind TokenKind) Describe () (description string) {
description = "LessThanEqualTo" description = "LessThanEqualTo"
case TokenKindLShift: case TokenKindLShift:
description = "LShift" description = "LShift"
case TokenKindLShiftAssignment:
description = "LShiftAssignment"
case TokenKindGreaterThan: case TokenKindGreaterThan:
description = "GreaterThan" description = "GreaterThan"
case TokenKindGreaterThanEqualTo: case TokenKindGreaterThanEqualTo:
description = "GreaterThanEqualTo" description = "GreaterThanEqualTo"
case TokenKindRShift: case TokenKindRShift:
description = "RShift" description = "RShift"
case TokenKindRShiftAssignment:
description = "RShiftAssignment"
case TokenKindBinaryOr: case TokenKindBinaryOr:
description = "BinaryOr" description = "BinaryOr"
case TokenKindBinaryOrAssignment:
description = "BinaryOrAssignment"
case TokenKindLogicalOr: case TokenKindLogicalOr:
description = "LogicalOr" description = "LogicalOr"
case TokenKindBinaryAnd: case TokenKindBinaryAnd:
description = "BinaryAnd" description = "BinaryAnd"
case TokenKindBinaryAndAssignment:
description = "BinaryAndAssignment"
case TokenKindLogicalAnd: case TokenKindLogicalAnd:
description = "LogicalAnd" description = "LogicalAnd"
case TokenKindBinaryXor:
description = "BinaryXor"
case TokenKindBinaryXorAssignment:
description = "BinaryXorAssignment"
} }
return return

View File

@ -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
}

View File

@ -1,7 +1,7 @@
package parser package parser
import "git.tebibyte.media/sashakoshka/arf/lexer" import "git.tebibyte.media/arf/arf/lexer"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
var validArgumentStartTokens = []lexer.TokenKind { var validArgumentStartTokens = []lexer.TokenKind {
lexer.TokenKindName, lexer.TokenKindName,
@ -12,7 +12,6 @@ var validArgumentStartTokens = []lexer.TokenKind {
lexer.TokenKindString, lexer.TokenKindString,
lexer.TokenKindRune, lexer.TokenKindRune,
lexer.TokenKindLBrace,
lexer.TokenKindLBracket, lexer.TokenKindLBracket,
} }
@ -29,6 +28,8 @@ func (parser *ParsingOperation) parseArgument () (argument Argument, err error)
if err != nil { return } if err != nil { return }
if parser.token.Is(lexer.TokenKindColon) { if parser.token.Is(lexer.TokenKindColon) {
err = parser.nextToken()
if err != nil { return }
var what Type var what Type
what, err = parser.parseType() what, err = parser.parseType()
if err != nil { return } if err != nil { return }
@ -77,9 +78,9 @@ func (parser *ParsingOperation) parseArgument () (argument Argument, err error)
argument.value = parser.token.Value().(rune) argument.value = parser.token.Value().(rune)
parser.nextToken() parser.nextToken()
// case lexer.TokenKindLBrace: case lexer.TokenKindLBracket:
argument.kind = ArgumentKindPhrase
// case lexer.TokenKindLBracket: argument.value, err = parser.parseArgumentLevelPhrase()
default: default:
panic ( panic (

View File

@ -1,7 +1,7 @@
package parser package parser
import "git.tebibyte.media/sashakoshka/arf/lexer" import "git.tebibyte.media/arf/arf/lexer"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
// parse body parses the body of an arf file, after the metadata header. // parse body parses the body of an arf file, after the metadata header.
func (parser *ParsingOperation) parseBody () (err error) { func (parser *ParsingOperation) parseBody () (err error) {
@ -57,6 +57,14 @@ func (parser *ParsingOperation) parseBody () (err error) {
parser.tree.enumSections[section.name] = section parser.tree.enumSections[section.name] = section
if err != nil { return } if err != nil { return }
case "func": 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: default:
err = parser.token.NewError ( err = parser.token.NewError (
"unknown section type \"" + sectionType + "\"", "unknown section type \"" + sectionType + "\"",

View File

@ -1,8 +1,8 @@
package parser package parser
import "git.tebibyte.media/sashakoshka/arf/types" import "git.tebibyte.media/arf/arf/types"
import "git.tebibyte.media/sashakoshka/arf/lexer" import "git.tebibyte.media/arf/arf/lexer"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
// parseData parses a data section. // parseData parses a data section.
func (parser *ParsingOperation) parseDataSection () ( func (parser *ParsingOperation) parseDataSection () (
@ -73,7 +73,7 @@ func (parser *ParsingOperation) parseInitializationValues (
var initializationValues ObjectInitializationValues var initializationValues ObjectInitializationValues
initializationValues, err = parser.parseObjectInitializationValues() initializationValues, err = parser.parseObjectInitializationValues()
initializationArgument.kind = ArgumentKindObjectInitializationValues initializationArgument.kind = ArgumentKindObjectInitializationValues
initializationArgument.value = &initializationValues initializationArgument.value = initializationValues
} else { } else {
@ -82,7 +82,7 @@ func (parser *ParsingOperation) parseInitializationValues (
var initializationValues ArrayInitializationValues var initializationValues ArrayInitializationValues
initializationValues, err = parser.parseArrayInitializationValues() initializationValues, err = parser.parseArrayInitializationValues()
initializationArgument.kind = ArgumentKindArrayInitializationValues initializationArgument.kind = ArgumentKindArrayInitializationValues
initializationArgument.value = &initializationValues initializationArgument.value = initializationValues
} }
return return

View File

@ -1,8 +1,8 @@
package parser package parser
import "git.tebibyte.media/sashakoshka/arf/types" import "git.tebibyte.media/arf/arf/types"
import "git.tebibyte.media/sashakoshka/arf/lexer" import "git.tebibyte.media/arf/arf/lexer"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
func (parser *ParsingOperation) parseEnumSection () ( func (parser *ParsingOperation) parseEnumSection () (
section *EnumSection, section *EnumSection,

View File

@ -1,8 +1,8 @@
package parser package parser
import "git.tebibyte.media/sashakoshka/arf/types" import "git.tebibyte.media/arf/arf/types"
import "git.tebibyte.media/sashakoshka/arf/lexer" import "git.tebibyte.media/arf/arf/lexer"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
// parseFaceSection parses an interface section. // parseFaceSection parses an interface section.
func (parser *ParsingOperation) parseFaceSection () ( func (parser *ParsingOperation) parseFaceSection () (
@ -64,7 +64,6 @@ func (parser *ParsingOperation) parseFaceSection () (
if err != nil { return } if err != nil { return }
} }
return
} }
// parseFaceBehavior parses a single interface behavior. Indentation level is // parseFaceBehavior parses a single interface behavior. Indentation level is
@ -127,6 +126,4 @@ func (parser *ParsingOperation) parseFaceBehavior () (
declaration) declaration)
} }
} }
return
} }

205
parser/func.go Normal file
View 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
View 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)
}

View File

@ -1,7 +1,7 @@
package parser package parser
import "git.tebibyte.media/sashakoshka/arf/lexer" import "git.tebibyte.media/arf/arf/lexer"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
// parseMeta parsese the metadata header at the top of an arf file. // parseMeta parsese the metadata header at the top of an arf file.
func (parser *ParsingOperation) parseMeta () (err error) { func (parser *ParsingOperation) parseMeta () (err error) {

View File

@ -1,8 +1,8 @@
package parser package parser
import "git.tebibyte.media/sashakoshka/arf/types" import "git.tebibyte.media/arf/arf/types"
import "git.tebibyte.media/sashakoshka/arf/lexer" import "git.tebibyte.media/arf/arf/lexer"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
// parseObjtSection parses an object type definition. This allows for structured // parseObjtSection parses an object type definition. This allows for structured
// types to be defined, and for member variables to be added and overridden. // 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() member.what, err = parser.parseType()
if err != nil { return } if err != nil { return }
println(parser.token.Describe())
// if there is a bit width, get it // if there is a bit width, get it
if parser.token.Is(lexer.TokenKindBinaryAnd) { if parser.token.Is(lexer.TokenKindBinaryAnd) {
err = parser.nextToken(lexer.TokenKindUInt) err = parser.nextToken(lexer.TokenKindUInt)

View File

@ -3,9 +3,9 @@ package parser
import "io" import "io"
import "os" import "os"
import "path/filepath" import "path/filepath"
import "git.tebibyte.media/sashakoshka/arf/file" import "git.tebibyte.media/arf/arf/file"
import "git.tebibyte.media/sashakoshka/arf/lexer" import "git.tebibyte.media/arf/arf/lexer"
import "git.tebibyte.media/sashakoshka/arf/infoerr" import "git.tebibyte.media/arf/arf/infoerr"
// ParsingOperation holds information about an ongoing parsing operation. // ParsingOperation holds information about an ongoing parsing operation.
type ParsingOperation struct { type ParsingOperation struct {

354
parser/phrase.go Normal file
View 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
}

View File

@ -3,10 +3,20 @@ package parser
import "io" import "io"
import "strings" import "strings"
import "testing" 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) { func checkTree (modulePath string, correct string, test *testing.T) {
tree, err := Parse(modulePath) 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) treeString := tree.ToString(0)
treeRunes := []rune(treeString) treeRunes := []rune(treeString)

View File

@ -2,6 +2,7 @@ package parser
import "fmt" import "fmt"
import "sort" import "sort"
import "git.tebibyte.media/arf/arf/lexer"
func doIndent (indent int, input ...string) (output string) { func doIndent (indent int, input ...string) (output string) {
for index := 0; index < indent; index ++ { for index := 0; index < indent; index ++ {
@ -70,10 +71,15 @@ func (tree *SyntaxTree) ToString (indent int) (output string) {
for _, name := range dataSectionKeys { for _, name := range dataSectionKeys {
output += tree.dataSections[name].ToString(indent) output += tree.dataSections[name].ToString(indent)
} }
funcSectionKeys := sortMapKeysAlphabetically(tree.funcSections)
for _, name := range funcSectionKeys {
output += tree.funcSections[name].ToString(indent)
}
return return
} }
func (identifier *Identifier) ToString () (output string) { func (identifier Identifier) ToString () (output string) {
for index, trailItem := range identifier.trail { for index, trailItem := range identifier.trail {
if index > 0 { if index > 0 {
output += "." output += "."
@ -110,13 +116,13 @@ func (what *Type) ToString () (output string) {
return return
} }
func (declaration *Declaration) ToString () (output string) { func (declaration Declaration) ToString () (output string) {
output += declaration.name + ":" output += declaration.name + ":"
output += declaration.what.ToString() output += declaration.what.ToString()
return return
} }
func (attributes *ObjectInitializationValues) ToString ( func (attributes ObjectInitializationValues) ToString (
indent int, indent int,
) ( ) (
output string, output string,
@ -136,7 +142,7 @@ func (attributes *ObjectInitializationValues) ToString (
return return
} }
func (values *ArrayInitializationValues) ToString ( func (values ArrayInitializationValues) ToString (
indent int, indent int,
) ( ) (
output string, output string,
@ -148,39 +154,6 @@ func (values *ArrayInitializationValues) ToString (
return 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) { func (argument *Argument) ToString (indent int, breakLine bool) (output string) {
if !breakLine { indent = 0 } if !breakLine { indent = 0 }
if argument.kind == ArgumentKindNil { if argument.kind == ArgumentKindNil {
@ -191,30 +164,30 @@ func (argument *Argument) ToString (indent int, breakLine bool) (output string)
switch argument.kind { switch argument.kind {
case ArgumentKindPhrase: case ArgumentKindPhrase:
output += argument.value.(*Phrase).ToString ( output += argument.value.(Phrase).ToString (
indent, indent,
breakLine) breakLine)
case ArgumentKindObjectInitializationValues: case ArgumentKindObjectInitializationValues:
// this should only appear in contexts where breakLine is true // this should only appear in contexts where breakLine is true
output += argument.value.(*ObjectInitializationValues). output += argument.value.(ObjectInitializationValues).
ToString(indent) ToString(indent)
case ArgumentKindArrayInitializationValues: case ArgumentKindArrayInitializationValues:
// this should only appear in contexts where breakLine is true // this should only appear in contexts where breakLine is true
output += argument.value.(*ArrayInitializationValues). output += argument.value.(ArrayInitializationValues).
ToString(indent) ToString(indent)
case ArgumentKindIdentifier: case ArgumentKindIdentifier:
output += doIndent ( output += doIndent (
indent, indent,
argument.value.(*Identifier).ToString()) argument.value.(Identifier).ToString())
if breakLine { output += "\n" } if breakLine { output += "\n" }
case ArgumentKindDeclaration: case ArgumentKindDeclaration:
output += doIndent ( output += doIndent (
indent, indent,
argument.value.(*Declaration).ToString()) argument.value.(Declaration).ToString())
if breakLine { output += "\n" } if breakLine { output += "\n" }
case ArgumentKindInt, ArgumentKindUInt, ArgumentKindFloat: case ArgumentKindInt, ArgumentKindUInt, ArgumentKindFloat:
@ -234,10 +207,71 @@ func (argument *Argument) ToString (indent int, breakLine bool) (output string)
if breakLine { output += "\n" } if breakLine { output += "\n" }
case ArgumentKindOperator: case ArgumentKindOperator:
// TODO var stringValue string
// also when parsing this argument kind, don't do it in the switch argument.value.(lexer.TokenKind) {
// argument parsing function. do it specifically when parsing a case lexer.TokenKindColon:
// phrase command. 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 return
@ -389,3 +423,87 @@ func (behavior *FaceBehavior) ToString (indent int) (output string) {
return 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
}

View File

@ -1,7 +1,7 @@
package parser package parser
import "git.tebibyte.media/sashakoshka/arf/file" import "git.tebibyte.media/arf/arf/file"
import "git.tebibyte.media/sashakoshka/arf/types" import "git.tebibyte.media/arf/arf/types"
// SyntaxTree represents an abstract syntax tree. It covers an entire module. It // 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 // can be expected to be syntactically correct, but it might not be semantically
@ -16,6 +16,7 @@ type SyntaxTree struct {
enumSections map[string] *EnumSection enumSections map[string] *EnumSection
faceSections map[string] *FaceSection faceSections map[string] *FaceSection
dataSections map[string] *DataSection dataSections map[string] *DataSection
funcSections map[string] *FuncSection
} }
// Identifier represents a chain of arguments separated by a dot. // Identifier represents a chain of arguments separated by a dot.
@ -78,15 +79,6 @@ type ArrayInitializationValues struct {
values []Argument 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 // ArgumentKind specifies the type of thing the value of an argument should be
// cast to. // cast to.
type ArgumentKind int type ArgumentKind int
@ -97,7 +89,7 @@ const (
// [name argument] // [name argument]
// [name argument argument] // [name argument argument]
// etc... // etc...
ArgumentKindPhrase = iota ArgumentKindPhrase
// {name} // {name}
ArgumentKindDereference ArgumentKindDereference
@ -185,7 +177,7 @@ type ObjtMember struct {
defaultValue Argument defaultValue Argument
} }
// ObjtSection represents an object type definition // ObjtSection represents an object type definition.
type ObjtSection struct { type ObjtSection struct {
location file.Location location file.Location
name string name string
@ -195,6 +187,7 @@ type ObjtSection struct {
members []ObjtMember members []ObjtMember
} }
// EnumMember represents a member of an enum section.
type EnumMember struct { type EnumMember struct {
location file.Location location file.Location
name string name string
@ -229,3 +222,60 @@ type FaceSection struct {
permission types.Permission permission types.Permission
behaviors map[string] FaceBehavior 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
}

View File

@ -1,8 +1,8 @@
package parser package parser
import "git.tebibyte.media/sashakoshka/arf/types" import "git.tebibyte.media/arf/arf/types"
import "git.tebibyte.media/sashakoshka/arf/lexer" import "git.tebibyte.media/arf/arf/lexer"
// import "git.tebibyte.media/sashakoshka/arf/infoerr" // import "git.tebibyte.media/arf/arf/infoerr"
// parseTypeSection parses a blind type definition, meaning it can inherit from // parseTypeSection parses a blind type definition, meaning it can inherit from
// anything including primitives, but cannot define structure. // anything including primitives, but cannot define structure.

View File

@ -1,3 +1,3 @@
:arf :arf
--- rw -> -349820394 932748397 239485.37520 "hello world!\n" 'E' helloWorld:.,..[]{} --- rw -> -349820394 932748397 239485.37520 "hello world!\n" 'E' helloWorld:.,..[]{}
+ - ++ -- * / @ ! % ~ = != < <= << > >= >> | || & && + - ++ -- * / @ ! % %= ~ ~= = != < <= << <<= > >= >> >>= | |= || & &= && ^ ^=

135
tests/parser/func/main.arf Normal file
View 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