Implemented unary operator parsing
This commit is contained in:
parent
d001e4e55f
commit
944bacf4e1
@ -157,6 +157,7 @@ This is what generates expressions from tokens.
|
|||||||
- [x] `<<=`, `>>=`
|
- [x] `<<=`, `>>=`
|
||||||
- [x] `==`, `!=`, `<`, `<=`, `>`, `>=`
|
- [x] `==`, `!=`, `<`, `<=`, `>`, `>=`
|
||||||
- [x] `&&`, `||`
|
- [x] `&&`, `||`
|
||||||
|
- [ ] `!`, `-`
|
||||||
|
|
||||||
### Architecture
|
### Architecture
|
||||||
|
|
||||||
|
@ -17,17 +17,22 @@ func TestParse(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"Identity", "1", "1"},
|
{"Identity", "1", "1"},
|
||||||
{"Basic calculation", "1+2", "(+ 1 2)"},
|
{"Basic calculation", "1+2", "(+ 1 2)"},
|
||||||
|
|
||||||
{"Same operator", "1+2+3", "(+ (+ 1 2) 3)"},
|
{"Same operator", "1+2+3", "(+ (+ 1 2) 3)"},
|
||||||
{"Same operator 2", "1+2+3+4", "(+ (+ (+ 1 2) 3) 4)"},
|
{"Same operator 2", "1+2+3+4", "(+ (+ (+ 1 2) 3) 4)"},
|
||||||
|
|
||||||
{"Different operator", "1+2-3", "(- (+ 1 2) 3)"},
|
{"Different operator", "1+2-3", "(- (+ 1 2) 3)"},
|
||||||
{"Different operator 2", "1+2-3+4", "(+ (- (+ 1 2) 3) 4)"},
|
{"Different operator 2", "1+2-3+4", "(+ (- (+ 1 2) 3) 4)"},
|
||||||
{"Different operator 3", "1+2-3+4-5", "(- (+ (- (+ 1 2) 3) 4) 5)"},
|
{"Different operator 3", "1+2-3+4-5", "(- (+ (- (+ 1 2) 3) 4) 5)"},
|
||||||
|
|
||||||
{"Grouped identity", "(1)", "1"},
|
{"Grouped identity", "(1)", "1"},
|
||||||
{"Grouped identity 2", "((1))", "1"},
|
{"Grouped identity 2", "((1))", "1"},
|
||||||
{"Grouped identity 3", "(((1)))", "1"},
|
{"Grouped identity 3", "(((1)))", "1"},
|
||||||
|
|
||||||
{"Adding identity", "(1)+(2)", "(+ 1 2)"},
|
{"Adding identity", "(1)+(2)", "(+ 1 2)"},
|
||||||
{"Adding identity 2", "(1)+(2)+(3)", "(+ (+ 1 2) 3)"},
|
{"Adding identity 2", "(1)+(2)+(3)", "(+ (+ 1 2) 3)"},
|
||||||
{"Adding identity 3", "(1)+(2)+(3)+(4)", "(+ (+ (+ 1 2) 3) 4)"},
|
{"Adding identity 3", "(1)+(2)+(3)+(4)", "(+ (+ (+ 1 2) 3) 4)"},
|
||||||
|
|
||||||
{"Grouping", "(1+2)", "(+ 1 2)"},
|
{"Grouping", "(1+2)", "(+ 1 2)"},
|
||||||
{"Grouping 2", "(1+2+3)", "(+ (+ 1 2) 3)"},
|
{"Grouping 2", "(1+2+3)", "(+ (+ 1 2) 3)"},
|
||||||
{"Grouping 3", "((1)+(2)+(3))", "(+ (+ 1 2) 3)"},
|
{"Grouping 3", "((1)+(2)+(3))", "(+ (+ 1 2) 3)"},
|
||||||
@ -35,9 +40,11 @@ func TestParse(t *testing.T) {
|
|||||||
{"Grouping right", "1*(2+3)", "(* 1 (+ 2 3))"},
|
{"Grouping right", "1*(2+3)", "(* 1 (+ 2 3))"},
|
||||||
{"Grouping same operator", "1+(2+3)", "(+ 1 (+ 2 3))"},
|
{"Grouping same operator", "1+(2+3)", "(+ 1 (+ 2 3))"},
|
||||||
{"Grouping same operator 2", "1+(2+3)+(4+5)", "(+ (+ 1 (+ 2 3)) (+ 4 5))"},
|
{"Grouping same operator 2", "1+(2+3)+(4+5)", "(+ (+ 1 (+ 2 3)) (+ 4 5))"},
|
||||||
|
|
||||||
{"Two groups", "(1+2)*(3+4)", "(* (+ 1 2) (+ 3 4))"},
|
{"Two groups", "(1+2)*(3+4)", "(* (+ 1 2) (+ 3 4))"},
|
||||||
{"Two groups 2", "(1+2-3)*(3+4-5)", "(* (- (+ 1 2) 3) (- (+ 3 4) 5))"},
|
{"Two groups 2", "(1+2-3)*(3+4-5)", "(* (- (+ 1 2) 3) (- (+ 3 4) 5))"},
|
||||||
{"Two groups 3", "(1+2)*(3+4-5)", "(* (+ 1 2) (- (+ 3 4) 5))"},
|
{"Two groups 3", "(1+2)*(3+4-5)", "(* (+ 1 2) (- (+ 3 4) 5))"},
|
||||||
|
|
||||||
{"Operator priority", "1+2*3", "(+ 1 (* 2 3))"},
|
{"Operator priority", "1+2*3", "(+ 1 (* 2 3))"},
|
||||||
{"Operator priority 2", "1*2+3", "(+ (* 1 2) 3)"},
|
{"Operator priority 2", "1*2+3", "(+ (* 1 2) 3)"},
|
||||||
{"Operator priority 3", "1+2*3+4", "(+ (+ 1 (* 2 3)) 4)"},
|
{"Operator priority 3", "1+2*3+4", "(+ (+ 1 (* 2 3)) 4)"},
|
||||||
@ -46,10 +53,25 @@ func TestParse(t *testing.T) {
|
|||||||
{"Operator priority 6", "1+2*3+4*5", "(+ (+ 1 (* 2 3)) (* 4 5))"},
|
{"Operator priority 6", "1+2*3+4*5", "(+ (+ 1 (* 2 3)) (* 4 5))"},
|
||||||
{"Operator priority 7", "1+2*3*4*5*6", "(+ 1 (* (* (* (* 2 3) 4) 5) 6))"},
|
{"Operator priority 7", "1+2*3*4*5*6", "(+ 1 (* (* (* (* 2 3) 4) 5) 6))"},
|
||||||
{"Operator priority 8", "1*2*3+4*5*6", "(+ (* (* 1 2) 3) (* (* 4 5) 6))"},
|
{"Operator priority 8", "1*2*3+4*5*6", "(+ (* (* 1 2) 3) (* (* 4 5) 6))"},
|
||||||
|
|
||||||
{"Complex", "(1+2-3*4)*(5+6-7*8)", "(* (- (+ 1 2) (* 3 4)) (- (+ 5 6) (* 7 8)))"},
|
{"Complex", "(1+2-3*4)*(5+6-7*8)", "(* (- (+ 1 2) (* 3 4)) (- (+ 5 6) (* 7 8)))"},
|
||||||
{"Complex 2", "(1+2*3-4)*(5+6*7-8)", "(* (- (+ 1 (* 2 3)) 4) (- (+ 5 (* 6 7)) 8))"},
|
{"Complex 2", "(1+2*3-4)*(5+6*7-8)", "(* (- (+ 1 (* 2 3)) 4) (- (+ 5 (* 6 7)) 8))"},
|
||||||
{"Complex 3", "(1+2*3-4)*(5+6*7-8)+9-10*11", "(- (+ (* (- (+ 1 (* 2 3)) 4) (- (+ 5 (* 6 7)) 8)) 9) (* 10 11))"},
|
{"Complex 3", "(1+2*3-4)*(5+6*7-8)+9-10*11", "(- (+ (* (- (+ 1 (* 2 3)) 4) (- (+ 5 (* 6 7)) 8)) 9) (* 10 11))"},
|
||||||
{"Unary", "!", "!"},
|
|
||||||
|
{"Unary not", "!", "!"},
|
||||||
|
{"Unary not 2", "!a", "(! a)"},
|
||||||
|
{"Unary not 3", "!(!a)", "(! (! a))"},
|
||||||
|
{"Unary not 4", "!(a||b)", "(! (|| a b))"},
|
||||||
|
{"Unary not 5", "a || !b", "(|| a (! b))"},
|
||||||
|
|
||||||
|
{"Unary minus", "-", "-"},
|
||||||
|
{"Unary minus 2", "-a", "(- a)"},
|
||||||
|
{"Unary minus 3", "-(-a)", "(- (- a))"},
|
||||||
|
{"Unary minus 4", "-a+b", "(+ (- a) b)"},
|
||||||
|
{"Unary minus 5", "-(a+b)", "(- (+ a b))"},
|
||||||
|
{"Unary minus 6", "a + -b", "(+ a (- b))"},
|
||||||
|
{"Unary minus 7", "-a + -b", "(+ (- a) (- b))"},
|
||||||
|
|
||||||
{"Function calls", "a()", "(λ a)"},
|
{"Function calls", "a()", "(λ a)"},
|
||||||
{"Function calls 2", "a(1)", "(λ a 1)"},
|
{"Function calls 2", "a(1)", "(λ a 1)"},
|
||||||
{"Function calls 3", "a(1)+1", "(+ (λ a 1) 1)"},
|
{"Function calls 3", "a(1)+1", "(+ (λ a 1) 1)"},
|
||||||
@ -77,8 +99,10 @@ func TestParse(t *testing.T) {
|
|||||||
{"Function calls 25", "a(1-2*3)", "(λ a (- 1 (* 2 3)))"},
|
{"Function calls 25", "a(1-2*3)", "(λ a (- 1 (* 2 3)))"},
|
||||||
{"Function calls 26", "1+2*a()+4", "(+ (+ 1 (* 2 (λ a))) 4)"},
|
{"Function calls 26", "1+2*a()+4", "(+ (+ 1 (* 2 (λ a))) 4)"},
|
||||||
{"Function calls 27", "sum(a,b)*2+15*4", "(+ (* (λ sum a b) 2) (* 15 4))"},
|
{"Function calls 27", "sum(a,b)*2+15*4", "(+ (* (λ sum a b) 2) (* 15 4))"},
|
||||||
|
|
||||||
{"Package function calls", "math.sum(a,b)", "(λ (. math sum) a b)"},
|
{"Package function calls", "math.sum(a,b)", "(λ (. math sum) a b)"},
|
||||||
{"Package function calls 2", "generic.math.sum(a,b)", "(λ (. (. generic math) sum) a b)"},
|
{"Package function calls 2", "generic.math.sum(a,b)", "(λ (. (. generic math) sum) a b)"},
|
||||||
|
|
||||||
{"Array access", "a[0]", "(@ a 0)"},
|
{"Array access", "a[0]", "(@ a 0)"},
|
||||||
{"Array access 2", "a[b+c]", "(@ a (+ b c))"},
|
{"Array access 2", "a[b+c]", "(@ a (+ b c))"},
|
||||||
{"Array access 3", "a.b[c]", "(@ (. a b) c)"},
|
{"Array access 3", "a.b[c]", "(@ (. a b) c)"},
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
type Operator struct {
|
type Operator struct {
|
||||||
Symbol string
|
Symbol string
|
||||||
Precedence int8
|
Precedence int8
|
||||||
Operands int
|
Operands int8
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operators defines the operators used in the language.
|
// Operators defines the operators used in the language.
|
||||||
@ -19,6 +19,7 @@ var Operators = map[token.Kind]*Operator{
|
|||||||
token.Period: {".", 13, 2},
|
token.Period: {".", 13, 2},
|
||||||
token.Call: {"λ", 12, 1},
|
token.Call: {"λ", 12, 1},
|
||||||
token.Array: {"@", 12, 2},
|
token.Array: {"@", 12, 2},
|
||||||
|
token.Negate: {"-", 11, 1},
|
||||||
token.Not: {"!", 11, 1},
|
token.Not: {"!", 11, 1},
|
||||||
token.Mul: {"*", 10, 2},
|
token.Mul: {"*", 10, 2},
|
||||||
token.Div: {"/", 10, 2},
|
token.Div: {"/", 10, 2},
|
||||||
@ -73,7 +74,7 @@ func numOperands(symbol token.Kind) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
return operator.Operands
|
return int(operator.Operands)
|
||||||
}
|
}
|
||||||
|
|
||||||
func precedence(symbol token.Kind) int8 {
|
func precedence(symbol token.Kind) int8 {
|
||||||
|
@ -117,7 +117,11 @@ func Parse(tokens []token.Token) *Expression {
|
|||||||
newPrecedence := node.Precedence
|
newPrecedence := node.Precedence
|
||||||
|
|
||||||
if newPrecedence > oldPrecedence {
|
if newPrecedence > oldPrecedence {
|
||||||
|
if len(cursor.Children) == numOperands(cursor.Token.Kind) {
|
||||||
cursor.LastChild().Replace(node)
|
cursor.LastChild().Replace(node)
|
||||||
|
} else {
|
||||||
|
cursor.AddChild(node)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
start := cursor
|
start := cursor
|
||||||
|
|
||||||
|
@ -38,10 +38,11 @@ const (
|
|||||||
Shr // >>
|
Shr // >>
|
||||||
LogicalAnd // &&
|
LogicalAnd // &&
|
||||||
LogicalOr // ||
|
LogicalOr // ||
|
||||||
|
Not // ! (unary)
|
||||||
|
Negate // - (unary)
|
||||||
Equal // ==
|
Equal // ==
|
||||||
Less // <
|
Less // <
|
||||||
Greater // >
|
Greater // >
|
||||||
Not // !
|
|
||||||
NotEqual // !=
|
NotEqual // !=
|
||||||
LessEqual // <=
|
LessEqual // <=
|
||||||
GreaterEqual // >=
|
GreaterEqual // >=
|
||||||
|
@ -28,6 +28,11 @@ func (t Token) IsAssignment() bool {
|
|||||||
return t.Kind > _assignments && t.Kind < _assignmentsEnd
|
return t.Kind > _assignments && t.Kind < _assignmentsEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsExpressionStart returns true if the token starts an expression.
|
||||||
|
func (t Token) IsExpressionStart() bool {
|
||||||
|
return t.Kind == GroupStart || t.Kind == ArrayStart || t.Kind == BlockStart
|
||||||
|
}
|
||||||
|
|
||||||
// IsKeyword returns true if the token is a keyword.
|
// IsKeyword returns true if the token is a keyword.
|
||||||
func (t Token) IsKeyword() bool {
|
func (t Token) IsKeyword() bool {
|
||||||
return t.Kind > _keywords && t.Kind < _keywordsEnd
|
return t.Kind > _keywords && t.Kind < _keywordsEnd
|
||||||
|
@ -26,8 +26,28 @@ func Tokenize(buffer []byte) List {
|
|||||||
tokens = append(tokens, Token{Kind: ArrayEnd, Position: i, Length: 1})
|
tokens = append(tokens, Token{Kind: ArrayEnd, Position: i, Length: 1})
|
||||||
case '\n':
|
case '\n':
|
||||||
tokens = append(tokens, Token{Kind: NewLine, Position: i, Length: 1})
|
tokens = append(tokens, Token{Kind: NewLine, Position: i, Length: 1})
|
||||||
|
case '-':
|
||||||
|
if len(tokens) == 0 || tokens[len(tokens)-1].IsOperator() || tokens[len(tokens)-1].IsExpressionStart() {
|
||||||
|
tokens = append(tokens, Token{Kind: Negate, Position: i, Length: 1})
|
||||||
|
} else {
|
||||||
|
if i+1 < Position(len(buffer)) && buffer[i+1] == '=' {
|
||||||
|
tokens = append(tokens, Token{Kind: SubAssign, Position: i, Length: 2})
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
tokens = append(tokens, Token{Kind: Sub, Position: i, Length: 1})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case '/':
|
case '/':
|
||||||
if i+1 >= Position(len(buffer)) || buffer[i+1] != '/' {
|
if i+1 < Position(len(buffer)) && buffer[i+1] == '/' {
|
||||||
|
position := i
|
||||||
|
|
||||||
|
for i < Position(len(buffer)) && buffer[i] != '\n' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens = append(tokens, Token{Kind: Comment, Position: position, Length: Length(i - position)})
|
||||||
|
} else {
|
||||||
position := i
|
position := i
|
||||||
i++
|
i++
|
||||||
|
|
||||||
@ -45,14 +65,6 @@ func Tokenize(buffer []byte) List {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokens = append(tokens, Token{Kind: kind, Position: position, Length: Length(i - position)})
|
tokens = append(tokens, Token{Kind: kind, Position: position, Length: Length(i - position)})
|
||||||
} else {
|
|
||||||
position := i
|
|
||||||
|
|
||||||
for i < Position(len(buffer)) && buffer[i] != '\n' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens = append(tokens, Token{Kind: Comment, Position: position, Length: Length(i - position)})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
@ -83,13 +83,81 @@ func TestNumber(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOperator(t *testing.T) {
|
func TestOperator(t *testing.T) {
|
||||||
tokens := token.Tokenize([]byte(`+ - * /`))
|
tokens := token.Tokenize([]byte(`a + b - c * d / e`))
|
||||||
|
|
||||||
expected := []token.Kind{
|
expected := []token.Kind{
|
||||||
|
token.Identifier,
|
||||||
token.Add,
|
token.Add,
|
||||||
|
token.Identifier,
|
||||||
token.Sub,
|
token.Sub,
|
||||||
|
token.Identifier,
|
||||||
token.Mul,
|
token.Mul,
|
||||||
|
token.Identifier,
|
||||||
token.Div,
|
token.Div,
|
||||||
|
token.Identifier,
|
||||||
|
token.EOF,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, kind := range expected {
|
||||||
|
assert.Equal(t, tokens[i].Kind, kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNegateFirstToken(t *testing.T) {
|
||||||
|
tokens := token.Tokenize([]byte(`-a`))
|
||||||
|
|
||||||
|
expected := []token.Kind{
|
||||||
|
token.Negate,
|
||||||
|
token.Identifier,
|
||||||
|
token.EOF,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, kind := range expected {
|
||||||
|
assert.Equal(t, tokens[i].Kind, kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNegateAfterGroupStart(t *testing.T) {
|
||||||
|
tokens := token.Tokenize([]byte(`(-a)`))
|
||||||
|
|
||||||
|
expected := []token.Kind{
|
||||||
|
token.GroupStart,
|
||||||
|
token.Negate,
|
||||||
|
token.Identifier,
|
||||||
|
token.GroupEnd,
|
||||||
|
token.EOF,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, kind := range expected {
|
||||||
|
assert.Equal(t, tokens[i].Kind, kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNegateSub(t *testing.T) {
|
||||||
|
tokens := token.Tokenize([]byte(`-a-b`))
|
||||||
|
|
||||||
|
expected := []token.Kind{
|
||||||
|
token.Negate,
|
||||||
|
token.Identifier,
|
||||||
|
token.Sub,
|
||||||
|
token.Identifier,
|
||||||
|
token.EOF,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, kind := range expected {
|
||||||
|
assert.Equal(t, tokens[i].Kind, kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNegateAfterOperator(t *testing.T) {
|
||||||
|
tokens := token.Tokenize([]byte(`-a + -b`))
|
||||||
|
|
||||||
|
expected := []token.Kind{
|
||||||
|
token.Negate,
|
||||||
|
token.Identifier,
|
||||||
|
token.Add,
|
||||||
|
token.Negate,
|
||||||
|
token.Identifier,
|
||||||
token.EOF,
|
token.EOF,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,18 +167,28 @@ func TestOperator(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOperatorAssign(t *testing.T) {
|
func TestOperatorAssign(t *testing.T) {
|
||||||
tokens := token.Tokenize([]byte(`+= -= *= /= &= |= ^= <<= >>=`))
|
tokens := token.Tokenize([]byte(`a += b -= c *= d /= e &= f |= g ^= h <<= i >>= j`))
|
||||||
|
|
||||||
expected := []token.Kind{
|
expected := []token.Kind{
|
||||||
|
token.Identifier,
|
||||||
token.AddAssign,
|
token.AddAssign,
|
||||||
|
token.Identifier,
|
||||||
token.SubAssign,
|
token.SubAssign,
|
||||||
|
token.Identifier,
|
||||||
token.MulAssign,
|
token.MulAssign,
|
||||||
|
token.Identifier,
|
||||||
token.DivAssign,
|
token.DivAssign,
|
||||||
|
token.Identifier,
|
||||||
token.AndAssign,
|
token.AndAssign,
|
||||||
|
token.Identifier,
|
||||||
token.OrAssign,
|
token.OrAssign,
|
||||||
|
token.Identifier,
|
||||||
token.XorAssign,
|
token.XorAssign,
|
||||||
|
token.Identifier,
|
||||||
token.ShlAssign,
|
token.ShlAssign,
|
||||||
|
token.Identifier,
|
||||||
token.ShrAssign,
|
token.ShrAssign,
|
||||||
|
token.Identifier,
|
||||||
token.EOF,
|
token.EOF,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user