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,18 +390,16 @@ func (lexer *LexingOperation) tokenizeDashBeginning () (err error) { | ||||
| 		} | ||||
| 		lexer.addToken(token) | ||||
| 	} else if lexer.char == '>' { | ||||
| 		token := lexer.newToken() | ||||
| 		token.kind = TokenKindReturnDirection | ||||
| 		token.location.SetWidth(2) | ||||
| 
 | ||||
| 		err = lexer.nextRune()  | ||||
| 		err = lexer.nextRune() | ||||
| 		if err != nil { return } | ||||
| 
 | ||||
| 		lexer.addToken(token) | ||||
| 	} 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) | ||||
| 	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