Skip to content

Commit

Permalink
JS: add &&= ||= ??= operators
Browse files Browse the repository at this point in the history
  • Loading branch information
tdewolff committed Feb 18, 2021
1 parent e167502 commit da5fb70
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 53 deletions.
22 changes: 12 additions & 10 deletions js/lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ var opEqTokens = map[byte]TokenType{
}

var opOpTokens = map[byte]TokenType{
'<': LtLtToken,
'+': IncrToken,
'-': DecrToken,
'*': ExpToken,
Expand All @@ -424,6 +425,14 @@ var opOpTokens = map[byte]TokenType{
'?': NullishToken,
}

var opOpEqTokens = map[byte]TokenType{
'<': LtLtEqToken,
'*': ExpEqToken,
'&': AndEqToken,
'|': OrEqToken,
'?': NullishEqToken,
}

func (l *Lexer) consumeOperatorToken() TokenType {
c := l.r.Peek(0)
l.r.Move(1)
Expand All @@ -437,11 +446,11 @@ func (l *Lexer) consumeOperatorToken() TokenType {
return EqEqEqToken
}
return opEqTokens[c]
} else if l.r.Peek(0) == c && (c == '+' || c == '-' || c == '*' || c == '&' || c == '|' || c == '?') {
} else if l.r.Peek(0) == c && (c == '+' || c == '-' || c == '*' || c == '&' || c == '|' || c == '?' || c == '<') {
l.r.Move(1)
if c == '*' && l.r.Peek(0) == '=' {
if l.r.Peek(0) == '=' {
l.r.Move(1)
return ExpEqToken
return opOpEqTokens[c]
}
return opOpTokens[c]
} else if c == '?' && l.r.Peek(0) == '.' && (l.r.Peek(1) < '0' || l.r.Peek(1) > '9') {
Expand All @@ -450,13 +459,6 @@ func (l *Lexer) consumeOperatorToken() TokenType {
} else if c == '=' && l.r.Peek(0) == '>' {
l.r.Move(1)
return ArrowToken
} else if c == '<' && l.r.Peek(0) == '<' {
l.r.Move(1)
if l.r.Peek(0) == '=' {
l.r.Move(1)
return LtLtEqToken
}
return LtLtToken
} else if c == '>' && l.r.Peek(0) == '>' {
l.r.Move(1)
if l.r.Peek(0) == '>' {
Expand Down
1 change: 1 addition & 0 deletions js/lex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func TestTokens(t *testing.T) {
{"! ~ && || ? : ?? ?.", TTs{NotToken, BitNotToken, AndToken, OrToken, QuestionToken, ColonToken, NullishToken, OptChainToken}},
{"= += -= *= **= /= %= <<=", TTs{EqToken, AddEqToken, SubEqToken, MulEqToken, ExpEqToken, DivEqToken, ModEqToken, LtLtEqToken}},
{">>= >>>= &= |= ^= =>", TTs{GtGtEqToken, GtGtGtEqToken, BitAndEqToken, BitOrEqToken, BitXorEqToken, ArrowToken}},
{"&&= ||= ??=", TTs{AndEqToken, OrEqToken, NullishEqToken}},
{"?.5", TTs{QuestionToken, DecimalToken}},
{"?.a", TTs{OptChainToken, IdentifierToken}},
{"await break case catch class const continue", TTs{AwaitToken, BreakToken, CaseToken, CatchToken, ClassToken, ConstToken, ContinueToken}},
Expand Down
2 changes: 1 addition & 1 deletion js/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -1686,7 +1686,7 @@ func (p *Parser) parseExpression(prec OpPrec) IExpr {
func (p *Parser) parseExpressionSuffix(left IExpr, prec, precLeft OpPrec) IExpr {
for {
switch tt := p.tt; tt {
case EqToken, MulEqToken, DivEqToken, ModEqToken, ExpEqToken, AddEqToken, SubEqToken, LtLtEqToken, GtGtEqToken, GtGtGtEqToken, BitAndEqToken, BitXorEqToken, BitOrEqToken:
case EqToken, MulEqToken, DivEqToken, ModEqToken, ExpEqToken, AddEqToken, SubEqToken, LtLtEqToken, GtGtEqToken, GtGtGtEqToken, BitAndEqToken, BitXorEqToken, BitOrEqToken, AndEqToken, OrEqToken, NullishEqToken:
if OpAssign < prec {
return left
} else if precLeft < OpLHS {
Expand Down
3 changes: 3 additions & 0 deletions js/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ func TestParse(t *testing.T) {
{"x = delete a", "Stmt(x=(delete a))"},
{"x = a in b", "Stmt(x=(a in b))"},
{"x = a.replace(b, c)", "Stmt(x=((a.replace)(b, c)))"},
{"x &&= a", "Stmt(x&&=a)"},
{"x ||= a", "Stmt(x||=a)"},
{"x ??= a", "Stmt(x??=a)"},
{"class a extends async function(){}{}", "Decl(class a extends Decl(async function Params() Stmt({ })))"},
{"x = a?b:c=d", "Stmt(x=(a ? b : (c=d)))"},
{"implements = 0", "Stmt(implements=0)"},
Expand Down
90 changes: 48 additions & 42 deletions js/tokentype.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,48 +50,51 @@ const (

// Operator token values.
const (
OperatorToken TokenType = 0x0600 + iota
EqToken // =
EqEqToken // ==
EqEqEqToken // ===
NotToken // !
NotEqToken // !=
NotEqEqToken // !==
LtToken // <
LtEqToken // <=
LtLtToken // <<
LtLtEqToken // <<=
GtToken // >
GtEqToken // >=
GtGtToken // >>
GtGtEqToken // >>=
GtGtGtToken // >>>
GtGtGtEqToken // >>>=
AddToken // +
AddEqToken // +=
IncrToken // ++
SubToken // -
SubEqToken // -=
DecrToken // --
MulToken // *
MulEqToken // *=
ExpToken // **
ExpEqToken // **=
DivToken // /
DivEqToken // /=
ModToken // %
ModEqToken // %=
BitAndToken // &
BitOrToken // |
BitXorToken // ^
BitNotToken // ~
BitAndEqToken // &=
BitOrEqToken // |=
BitXorEqToken // ^=
AndToken // &&
OrToken // ||
NullishToken // ??
OptChainToken // ?.
OperatorToken TokenType = 0x0600 + iota
EqToken // =
EqEqToken // ==
EqEqEqToken // ===
NotToken // !
NotEqToken // !=
NotEqEqToken // !==
LtToken // <
LtEqToken // <=
LtLtToken // <<
LtLtEqToken // <<=
GtToken // >
GtEqToken // >=
GtGtToken // >>
GtGtEqToken // >>=
GtGtGtToken // >>>
GtGtGtEqToken // >>>=
AddToken // +
AddEqToken // +=
IncrToken // ++
SubToken // -
SubEqToken // -=
DecrToken // --
MulToken // *
MulEqToken // *=
ExpToken // **
ExpEqToken // **=
DivToken // /
DivEqToken // /=
ModToken // %
ModEqToken // %=
BitAndToken // &
BitOrToken // |
BitXorToken // ^
BitNotToken // ~
BitAndEqToken // &=
BitOrEqToken // |=
BitXorEqToken // ^=
AndToken // &&
OrToken // ||
NullishToken // ??
AndEqToken // &&=
OrEqToken // ||=
NullishEqToken // ??=
OptChainToken // ?.

// unused in lexer
PosToken // +a
Expand Down Expand Up @@ -246,6 +249,9 @@ var operatorBytes = [][]byte{
[]byte("&&"),
[]byte("||"),
[]byte("??"),
[]byte("&&="),
[]byte("||="),
[]byte("??="),
[]byte("?."),
[]byte("+"),
[]byte("-"),
Expand Down

0 comments on commit da5fb70

Please sign in to comment.