fspl/parser/fspl/expression.go

720 lines
20 KiB
Go

package fsplParser
import "git.tebibyte.media/fspl/fspl/lexer"
import "git.tebibyte.media/fspl/fspl/errors"
import "git.tebibyte.media/fspl/fspl/entity"
var descriptionExpression = "expression"
var startTokensExpression = []lexer.TokenKind {
lexer.Ident,
lexer.LParen,
lexer.LBracket,
lexer.LBrace,
lexer.Int,
lexer.Float,
lexer.String,
}
func (this *treeParser) parseExpression () (entity.Expression, error) {
// Steps that this function takes to parse expressions:
// 1. Run a decision tree to parse the expression.
// 2. After that, test for infix operators (. and =). We will not need
// to consume more tokens to do this, as if they exist they will be
// the very next token AKA the current one after parsing the
// expression.
// 3. Then, use the previously parsed expression and use it as the left
// side, and go on to parse the right side. Assignment (=) is the
// only infix expression where both left and right sides are
// expressions, so it gets to call parseExpression() and recurse. The
// implied associativity matters here.
// 4. If not assignment, then this has to do with member access (the
// right side is not an expression), so we repeat from step 2 in order
// to chain member accesses and method calls. Step 2 will test
// whether or not we need to continue repeating afterward. The
// implied associativity matters here.
// 5. Return the expression, whether it was directly parsed or is a tree
// of infix operator(s).
// run decision tree
expression, err := this.parseExpressionRoot()
if err != nil { return nil, err }
// test for infix operators
for this.Token.Is(lexer.Dot) || (this.Token.Is(lexer.Symbol) && this.Token.ValueIs("=")) {
pos := this.Pos()
switch this.Kind() {
case lexer.Dot:
// Dot: member access or method call
// this control path must not return, staying within the
// loop
source := expression
err := this.ExpectNextDesc (
"member access or method call",
lexer.Ident, lexer.LBracket)
if err != nil { return nil, err }
switch this.Kind() {
case lexer.Ident:
// Ident: member access
expression = &entity.MemberAccess {
Pos: pos.Union(this.Pos()),
Source: source,
Member: this.Value(),
}
this.Next()
case lexer.LBracket:
// LBracket: method call
expression, err = this.parseMethodCallCore(pos, source)
if err != nil { return nil, err }
}
case lexer.Symbol:
// Symbol '=': assignment
// this control path must return, breaking out of the
// loop.
expression := &entity.Assignment {
Pos: pos,
Location: expression,
}
this.Next()
value, err := this.parseExpression()
if err != nil { return nil, err }
expression.Value = value
return expression, nil
}
}
return expression, nil
}
func (this *treeParser) parseExpressionRoot () (entity.Expression, error) {
err := this.ExpectDesc(descriptionExpression, startTokensExpression...)
if err != nil { return nil, err }
switch this.Kind() {
case lexer.Ident: return this.parseExpressionRootIdent()
case lexer.LParen: return this.parseExpressionRootLParen()
case lexer.LBracket: return this.parseExpressionRootLBracket()
case lexer.LBrace: return this.parseBlock()
case lexer.Int: return this.parseLiteralInt()
case lexer.Float: return this.parseLiteralFloat()
case lexer.String: return this.parseLiteralString()
}
panic(this.bug())
}
func (this *treeParser) parseExpressionRootIdent () (entity.Expression, error) {
err := this.Expect(lexer.Ident)
if err != nil { return nil, err }
name := this.Value()
pos := this.Pos()
switch name {
case "true", "false": return this.parseLiteralBoolean ()
case "nil": return this.parseLiteralNil()
case "if": return this.parseIfElse()
case "match": return this.parseMatch()
case "switch": return this.parseSwitch()
case "loop": return this.parseLoop()
case "for": return this.parseFor()
default:
this.Next()
switch this.Kind() {
case lexer.Colon:
// Colon: declaration
return this.parseDeclarationCore(pos, name)
case lexer.DoubleColon:
// DoubleColon: call
err := this.ExpectNext(lexer.LBracket)
if err != nil { return nil, err }
this.Next()
return this.parseCallCore(pos, name)
default:
// *: variable
return &entity.Variable {
Pos: pos,
Name: name,
}, nil
}
}
}
func (this *treeParser) parseExpressionRootLParen () (entity.Expression, error) {
err := this.Expect(lexer.LParen)
if err != nil { return nil, err }
pos := this.Pos()
err = this.ExpectNextDesc (
"struct or array literal",
appendCopy(startTokensExpression, lexer.Dot)...)
if err != nil { return nil, err }
switch this.Kind() {
case lexer.Dot: return this.parseLiteralStructCore(pos)
default: return this.parseLiteralArrayCore(pos)
}
panic(this.bug())
}
func (this *treeParser) parseExpressionRootLBracket () (entity.Expression, error) {
err := this.Expect(lexer.LBracket)
if err != nil { return nil, err }
pos := this.Pos()
err = this.ExpectNext(lexer.Ident, lexer.Dot, lexer.Star, lexer.Symbol)
if err != nil { return nil, err }
switch this.Kind() {
case lexer.Ident: return this.parseExpressionRootLBracketIdent(pos)
case lexer.Dot: return this.parseDereferenceOrSubscriptCore(pos)
case lexer.Star: return this.parseOperationCore(pos)
case lexer.Symbol: return this.parseExpressionRootLBracketSymbol(pos)
}
panic(this.bug())
}
func (this *treeParser) parseExpressionRootLBracketIdent (pos errors.Position) (entity.Expression, error) {
err := this.Expect(lexer.Ident)
if err != nil { return nil, err }
name := this.Value()
switch name {
case "break", "return": return this.parseReturnOrBreakCore(pos)
default: return this.parseCallCore(pos, "")
}
}
func (this *treeParser) parseDereferenceOrSubscriptCore (pos errors.Position) (entity.Expression, error) {
err := this.Expect(lexer.Dot)
if err != nil { return nil, err }
this.Next()
argument, err := this.parseExpression()
if err != nil { return nil, err }
err = this.ExpectDesc (
"element offset expression or end of dereference",
appendCopy(startTokensExpression, lexer.RBracket)...)
if err != nil { return nil, err }
if this.Token.Is(lexer.RBracket) {
// RBracket: dereference
pos = pos.Union(this.Pos())
this.Next()
return &entity.Dereference {
Pos: pos,
Pointer: argument,
}, nil
} else {
// startTokensExpression...: subscript
offset, err := this.parseExpression()
if err != nil { return nil, err }
err = this.Expect(lexer.RBracket)
if err != nil { return nil, err }
pos = pos.Union(this.Pos())
this.Next()
return &entity.Subscript {
Pos: pos,
Slice: argument,
Offset: offset,
}, nil
}
}
func (this *treeParser) parseExpressionRootLBracketSymbol (pos errors.Position) (entity.Expression, error) {
err := this.ExpectValue (
lexer.Symbol,
appendCopy(valuesOperator, "\\", "#", "@", "~", "~~")...)
if err != nil { return nil, err }
switch this.Value() {
case "\\": return this.parseSliceCore(pos)
case "#": return this.parseLengthCore(pos)
case "@": return this.parseReferenceCore(pos)
case "~": return this.parseValueOrBitCastCore(pos)
case "~~": return this.parseValueOrBitCastCore(pos)
default: return this.parseOperationCore(pos)
}
}
func (this *treeParser) parseCallCore (pos errors.Position, unitNickname string) (*entity.Call, error) {
err := this.Expect(lexer.Ident)
if err != nil { return nil, err }
call := &entity.Call {
Pos: pos,
UnitNickname: unitNickname,
Name: this.Value(),
}
this.Next()
for {
err = this.ExpectDesc (
"argument or end of call",
appendCopy(startTokensExpression, lexer.RBracket)...)
if err != nil { return nil, err }
if this.Token.Is(lexer.RBracket) { break }
argument, err := this.parseExpression()
if err != nil { return nil, err }
call.Arguments = append(call.Arguments, argument)
}
call.Pos = call.Position().Union(this.Pos())
this.Next()
return call, nil
}
func (this *treeParser) parseMethodCallCore (pos errors.Position, source entity.Expression) (*entity.MethodCall, error) {
err := this.Expect(lexer.LBracket)
if err != nil { return nil, err }
err = this.ExpectNext(lexer.Ident)
if err != nil { return nil, err }
call := &entity.MethodCall {
Source: source,
Pos: pos,
Name: this.Value(),
}
this.Next()
for {
err = this.ExpectDesc (
"argument or end of method call",
appendCopy(startTokensExpression, lexer.RBracket)...)
if err != nil { return nil, err }
if this.Token.Is(lexer.RBracket) { break }
argument, err := this.parseExpression()
if err != nil { return nil, err }
call.Arguments = append(call.Arguments, argument)
}
call.Pos = call.Position().Union(this.Pos())
this.Next()
return call, nil
}
func (this *treeParser) parseReturnOrBreakCore (pos errors.Position) (entity.Expression, error) {
err := this.ExpectValue(lexer.Ident, "break", "return")
if err != nil { return nil, err }
name := this.Value()
err = this.ExpectNextDesc (
"expression or end of " + name,
appendCopy(startTokensExpression, lexer.RBracket)...)
if err != nil { return nil, err }
var value entity.Expression
if this.Token.Is(startTokensExpression...) {
// startTokensExpression...: value expression
value, err = this.parseExpression()
if err != nil { return nil, err }
}
err = this.ExpectDesc("end of " + name, lexer.RBracket)
if err != nil { return nil, err }
pos = pos.Union(this.Pos())
this.Next()
switch name {
case "break":
return &entity.Break {
Pos: pos,
Value: value,
}, nil
case "return":
return &entity.Return {
Pos: pos,
Value: value,
}, nil
}
panic(this.bug())
}
func (this *treeParser) parseSliceCore (pos errors.Position) (*entity.Slice, error) {
err := this.ExpectValue(lexer.Symbol , "\\")
if err != nil { return nil, err }
slice := &entity.Slice {
Pos: pos,
}
err = this.ExpectNextDesc(descriptionExpression, startTokensExpression...)
if err != nil { return nil, err }
slice.Slice, err = this.parseExpression()
err = this.ExpectDesc (
"offset expression or '/'",
appendCopy(startTokensExpression, lexer.Symbol)...)
if err != nil { return nil, err }
if this.Token.Is(startTokensExpression...) {
// startTokensExpression...: starting offset
slice.Start, err = this.parseExpression()
if err != nil { return nil, err }
}
err = this.ExpectValue(lexer.Symbol, "/")
if err != nil { return nil, err }
this.ExpectNextDesc (
"offset expression or operation end",
appendCopy(startTokensExpression, lexer.RBracket)...)
if this.Token.Is(startTokensExpression...) {
// startTokensExpression...: ending offset
slice.End, err = this.parseExpression()
if err != nil { return nil, err }
}
err = this.Expect(lexer.RBracket)
if err != nil { return nil, err }
slice.Pos = slice.Position().Union(this.Pos())
this.Next()
return slice, nil
}
func (this *treeParser) parseLengthCore (pos errors.Position) (*entity.Length, error) {
err := this.ExpectValue(lexer.Symbol , "#")
if err != nil { return nil, err }
length := &entity.Length {
Pos: pos,
}
this.Next()
length.Slice, err = this.parseExpression()
if err != nil { return nil, err }
err = this.Expect(lexer.RBracket)
if err != nil { return nil, err }
length.Pos = length.Position().Union(this.Pos())
this.Next()
return length, nil
}
func (this *treeParser) parseReferenceCore (pos errors.Position) (*entity.Reference, error) {
err := this.ExpectValue(lexer.Symbol , "@")
if err != nil { return nil, err }
reference := &entity.Reference {
Pos: pos,
}
this.Next()
reference.Value, err = this.parseExpression()
if err != nil { return nil, err }
err = this.Expect(lexer.RBracket)
if err != nil { return nil, err }
reference.Pos = reference.Position().Union(this.Pos())
this.Next()
return reference, nil
}
func (this *treeParser) parseValueOrBitCastCore (pos errors.Position) (entity.Expression, error) {
err := this.ExpectValue(lexer.Symbol , "~", "~~")
if err != nil { return nil, err }
tokValue := this.Value()
this.Next()
ty, err := this.parseType()
if err != nil { return nil, err }
err = this.ExpectDesc(descriptionExpression, startTokensExpression...)
if err != nil { return nil, err }
value, err := this.parseExpression()
err = this.Expect(lexer.RBracket)
if err != nil { return nil, err }
pos = pos.Union(this.Pos())
this.Next()
switch tokValue {
case "~":
return &entity.ValueCast {
Pos: pos,
Ty: ty,
Value: value,
}, nil
case "~~":
return &entity.BitCast {
Pos: pos,
Ty: ty,
Value: value,
}, nil
}
panic(this.bug())
}
var valuesOperator = []string {
"++", "+", "--", "-", "*", "/", "%", "!!", "||", "&&", "^^",
"!", "|", "&", "^", "<<", ">>", "<", ">", "<=", ">=", "=",
}
func (this *treeParser) parseOperationCore (pos errors.Position) (*entity.Operation, error) {
// TODO: maybe come up with a more elegant way of writing this?
// possibly make a verion of expectValue that matches a list of kinds
// and a list of values. could also make a version that accepts any kind
// of token, and always supplement that with a normal expect.
if this.Token.Is(lexer.Star) {
err := this.ExpectValue(lexer.Star, valuesOperator...)
if err != nil { return nil, err }
} else {
err := this.ExpectValue(lexer.Symbol, valuesOperator...)
if err != nil { return nil, err }
}
operation := &entity.Operation {
Pos: pos,
Operator: entity.OperatorFromString(this.Value()),
}
this.Next()
for {
err := this.ExpectDesc (
"expression or operation end",
appendCopy(startTokensExpression, lexer.RBracket)...)
if err != nil { return nil, err }
if this.Token.Is(lexer.RBracket) { break }
argument, err := this.parseExpression()
if err != nil { return nil, err }
operation.Arguments = append(operation.Arguments, argument)
}
operation.Pos = operation.Position().Union(this.Pos())
this.Next()
return operation, nil
}
func (this *treeParser) parseBlock () (*entity.Block, error) {
err := this.ExpectDesc("Block", lexer.LBrace)
if err != nil { return nil, err }
block := &entity.Block {
Pos: this.Pos(),
}
this.Next()
for {
err := this.ExpectDesc (
descriptionExpression,
appendCopy(startTokensExpression, lexer.RBrace)...)
if err != nil { return nil, err }
if this.Kind() == lexer.RBrace{ break }
step, err := this.parseExpression()
if err != nil { return nil, err }
block.Steps = append(block.Steps, step)
}
block.Pos = block.Position().Union(this.Pos())
this.Next()
return block, nil
}
var descriptionDeclaration = "declaration"
var startTokensDeclaration = []lexer.TokenKind { lexer.Ident }
func (this *treeParser) parseDeclaration () (*entity.Declaration, error) {
err := this.ExpectDesc(descriptionDeclaration, startTokensDeclaration...)
if err != nil { return nil, err }
name := this.Value()
pos := this.Pos()
this.Next()
return this.parseDeclarationCore(pos, name)
}
func (this *treeParser) parseDeclarationCore (pos errors.Position, name string) (*entity.Declaration, error) {
err := this.Expect(lexer.Colon)
if err != nil { return nil, err }
this.Next()
ty, err := this.parseType()
if err != nil { return nil, err }
return &entity.Declaration {
Pos: pos,
Name: name,
Ty: ty,
}, nil
}
func (this *treeParser) parseIfElse () (*entity.IfElse, error) {
err := this.ExpectValue(lexer.Ident, "if")
if err != nil { return nil, err }
ifElse := &entity.IfElse {
Pos: this.Pos(),
}
err = this.ExpectNextDesc("condition", startTokensExpression...)
if err != nil { return nil, err }
ifElse.Condition, err = this.parseExpression()
if err != nil { return nil, err }
err = this.ExpectValue(lexer.Ident, "then")
if err != nil { return nil, err }
this.ExpectNextDesc("branch expression", startTokensExpression...)
ifElse.True, err = this.parseExpression()
if err != nil { return nil, err }
if this.Token.Is(lexer.Ident) && this.Token.ValueIs("else") {
// Ident "else": false branch expression
this.ExpectNextDesc("branch expression", startTokensExpression...)
ifElse.False, err = this.parseExpression()
if err != nil { return nil, err }
}
return ifElse, nil
}
func (this *treeParser) parseMatch () (*entity.Match, error) {
err := this.ExpectValue(lexer.Ident, "match")
if err != nil { return nil, err }
match := &entity.Match {
Pos: this.Pos(),
}
err = this.ExpectNextDesc(descriptionExpression, startTokensExpression...)
if err != nil { return nil, err }
match.Value, err = this.parseExpression()
if err != nil { return nil, err }
for this.Is(lexer.Symbol) && this.ValueIs("|") {
cas, err := this.parseMatchCase()
if err != nil { return nil, err }
match.Cases = append(match.Cases, cas)
}
if this.Is(lexer.Star) {
defaul, err := this.parseDefaultCase()
if err != nil { return nil, err }
match.Default = defaul
}
return match, nil
}
func (this *treeParser) parseMatchCase () (*entity.MatchCase, error) {
err := this.ExpectValue(lexer.Symbol, "|")
if err != nil { return nil, err }
cas := &entity.MatchCase {
Pos: this.Pos(),
}
this.Next()
cas.Declaration, err = this.parseDeclaration()
if err != nil { return nil, err }
cas.Expression, err = this.parseExpression()
if err != nil { return nil, err }
return cas, nil
}
func (this *treeParser) parseSwitch () (*entity.Switch, error) {
err := this.ExpectValue(lexer.Ident, "switch")
if err != nil { return nil, err }
switc := &entity.Switch {
Pos: this.Pos(),
}
err = this.ExpectNextDesc(descriptionExpression, startTokensExpression...)
if err != nil { return nil, err }
switc.Value, err = this.parseExpression()
if err != nil { return nil, err }
for this.Is(lexer.Symbol) && this.ValueIs("|") {
cas, err := this.parseSwitchCase()
if err != nil { return nil, err }
switc.Cases = append(switc.Cases, cas)
}
if this.Is(lexer.Star) {
defaul, err := this.parseDefaultCase()
if err != nil { return nil, err }
switc.Default = defaul
}
return switc, nil
}
func (this *treeParser) parseSwitchCase () (*entity.SwitchCase, error) {
err := this.ExpectValue(lexer.Symbol, "|")
if err != nil { return nil, err }
cas := &entity.SwitchCase {
Pos: this.Pos(),
}
this.Next()
cas.Key, err = this.parseExpression()
if err != nil { return nil, err }
cas.Expression, err = this.parseExpression()
if err != nil { return nil, err }
return cas, nil
}
func (this *treeParser) parseDefaultCase () (*entity.DefaultCase, error) {
err := this.Expect(lexer.Star)
if err != nil { return nil, err }
cas := &entity.DefaultCase {
Pos: this.Pos(),
}
this.Next()
cas.Expression, err = this.parseExpression()
if err != nil { return nil, err }
return cas, nil
}
func (this *treeParser) parseLoop () (*entity.Loop, error) {
err := this.ExpectValue(lexer.Ident, "loop")
if err != nil { return nil, err }
pos := this.Pos()
this.Next()
body, err := this.parseExpression()
if err != nil { return nil, err }
return &entity.Loop {
Pos: pos,
Body: body,
}, nil
}
func (this *treeParser) parseFor () (*entity.For, error) {
err := this.ExpectValue(lexer.Ident, "for")
if err != nil { return nil, err }
this.Next()
loop := &entity.For {
Pos: this.Pos(),
}
firstDeclaration, err := this.parseDeclaration()
if err != nil { return nil, err }
if this.ValueIs("in") {
loop.Element = firstDeclaration
} else {
loop.Index = firstDeclaration
element, err := this.parseDeclaration()
if err != nil { return nil, err }
loop.Element = element
}
err = this.ExpectValue(lexer.Ident, "in")
if err != nil { return nil, err }
this.Next()
over, err := this.parseExpression()
if err != nil { return nil, err }
loop.Over = over
body, err := this.parseExpression()
if err != nil { return nil, err }
loop.Body = body
return loop, nil
}