// current token
tok int32
- sym_ *Sym // valid if tok == LNAME
- val Val // valid if tok == LLITERAL
- op Op // valid if tok == LASOP
+ sym_ *Sym // valid if tok == LNAME
+ val Val // valid if tok == LLITERAL
+ op Op // valid if tok == LASOP or LINCOP, or prec > 0
+ prec OpPrec // operator precedence; 0 if not a binary operator
}
+type OpPrec int
+
+const (
+ // Precedences of binary operators (must be > 0).
+ PCOMM OpPrec = 1 + iota
+ POROR
+ PANDAND
+ PCMP
+ PADD
+ PMUL
+)
+
const (
// The value of single-char tokens is just their character's Unicode value.
// They are all below utf8.RuneSelf. Shift other tokens up to avoid conflicts.
LANDAND
LANDNOT
LCOMM
- LDEC
LEQ
LGE
LGT
LIGNORE
- LINC
+ LINCOP
LLE
LLSH
LLT
func (l *lexer) next() {
nlsemi := l.nlsemi
l.nlsemi = false
+ l.prec = 0
l0:
// skip white space
var c1 rune
var op Op
+ var prec OpPrec
switch c {
case EOF:
}
}
- if c1 == '=' {
- op = ODIV
- goto asop
- }
+ op = ODIV
+ prec = PMUL
+ goto binop1
case ':':
c1 = l.getr()
}
case '*':
- c1 = l.getr()
- if c1 == '=' {
- op = OMUL
- goto asop
- }
+ op = OMUL
+ prec = PMUL
+ goto binop
case '%':
- c1 = l.getr()
- if c1 == '=' {
- op = OMOD
- goto asop
- }
+ op = OMOD
+ prec = PMUL
+ goto binop
case '+':
- c1 = l.getr()
- if c1 == '+' {
- l.nlsemi = true
- c = LINC
- goto lx
- }
-
- if c1 == '=' {
- op = OADD
- goto asop
- }
+ op = OADD
+ goto incop
case '-':
- c1 = l.getr()
- if c1 == '-' {
- l.nlsemi = true
- c = LDEC
- goto lx
- }
-
- if c1 == '=' {
- op = OSUB
- goto asop
- }
+ op = OSUB
+ goto incop
case '>':
c1 = l.getr()
if c1 == '>' {
c = LRSH
- c1 = l.getr()
- if c1 == '=' {
- op = ORSH
- goto asop
- }
-
- break
+ op = ORSH
+ prec = PMUL
+ goto binop
}
+ l.prec = PCMP
if c1 == '=' {
c = LGE
+ l.op = OGE
goto lx
}
-
c = LGT
+ l.op = OGT
case '<':
c1 = l.getr()
if c1 == '<' {
c = LLSH
- c1 = l.getr()
- if c1 == '=' {
- op = OLSH
- goto asop
- }
-
- break
- }
-
- if c1 == '=' {
- c = LLE
- goto lx
+ op = OLSH
+ prec = PMUL
+ goto binop
}
if c1 == '-' {
c = LCOMM
+ // Not a binary operator, but parsed as one
+ // so we can give a good error message when used
+ // in an expression context.
+ l.prec = PCOMM
+ l.op = OSEND
goto lx
}
+ l.prec = PCMP
+ if c1 == '=' {
+ c = LLE
+ l.op = OLE
+ goto lx
+ }
c = LLT
+ l.op = OLT
case '=':
c1 = l.getr()
if c1 == '=' {
c = LEQ
+ l.prec = PCMP
+ l.op = OEQ
goto lx
}
c1 = l.getr()
if c1 == '=' {
c = LNE
+ l.prec = PCMP
+ l.op = ONE
goto lx
}
c1 = l.getr()
if c1 == '&' {
c = LANDAND
+ l.prec = PANDAND
+ l.op = OANDAND
goto lx
}
if c1 == '^' {
c = LANDNOT
- c1 = l.getr()
- if c1 == '=' {
- op = OANDNOT
- goto asop
- }
-
- break
+ op = OANDNOT
+ prec = PMUL
+ goto binop
}
- if c1 == '=' {
- op = OAND
- goto asop
- }
+ op = OAND
+ prec = PMUL
+ goto binop1
case '|':
c1 = l.getr()
if c1 == '|' {
c = LOROR
+ l.prec = POROR
+ l.op = OOROR
goto lx
}
- if c1 == '=' {
- op = OOR
- goto asop
- }
+ op = OOR
+ prec = PADD
+ goto binop1
case '^':
- c1 = l.getr()
- if c1 == '=' {
- op = OXOR
- goto asop
- }
+ op = OXOR
+ prec = PADD
+ goto binop
case '(', '[', '{', ',', ';':
goto lx
lx:
if Debug['x'] != 0 {
- if c > 0xff {
+ if c >= utf8.RuneSelf {
fmt.Printf("%v lex: TOKEN %s\n", Ctxt.Line(int(lineno)), lexname(c))
} else {
fmt.Printf("%v lex: TOKEN '%c'\n", Ctxt.Line(int(lineno)), c)
l.tok = c
return
-asop:
+incop:
+ c1 = l.getr()
+ if c1 == c {
+ l.nlsemi = true
+ l.op = op
+ c = LINCOP
+ goto lx
+ }
+ prec = PADD
+ goto binop1
+
+binop:
+ c1 = l.getr()
+binop1:
+ if c1 != '=' {
+ l.ungetr(c1)
+ l.op = op
+ l.prec = prec
+ goto lx
+ }
+
l.op = op
if Debug['x'] != 0 {
fmt.Printf("lex: TOKEN ASOP %s=\n", goopnames[op])
LCONST: "CONST",
LCONTINUE: "CONTINUE",
LDDD: "...",
- LDEC: "DEC",
LDEFAULT: "DEFAULT",
LDEFER: "DEFER",
LELSE: "ELSE",
LGT: "GT",
LIF: "IF",
LIMPORT: "IMPORT",
- LINC: "INC",
+ LINCOP: "INCOP",
LINTERFACE: "INTERFACE",
LLE: "LE",
LLITERAL: "LITERAL",
}
case LASOP:
tok = goopnames[p.op] + "="
+ case LINCOP:
+ tok = goopnames[p.op] + goopnames[p.op]
default:
tok = tokstring(p.tok)
}
LANDAND: "&&",
LANDNOT: "&^",
LCOMM: "<-",
- LDEC: "--",
LEQ: "==",
LGE: ">=",
LGT: ">",
LIGNORE: "LIGNORE", // we should never see this one
- LINC: "++",
+ LINCOP: "opop",
LLE: "<=",
LLSH: "<<",
LLT: "<",
stmt.Etype = EType(op) // rathole to pass opcode
return stmt
- case LINC:
- // expr LINC
+ case LINCOP:
+ // expr LINCOP
p.next()
stmt := Nod(OASOP, lhs, Nodintconst(1))
stmt.Implicit = true
- stmt.Etype = EType(OADD)
- return stmt
-
- case LDEC:
- // expr LDEC
- p.next()
-
- stmt := Nod(OASOP, lhs, Nodintconst(1))
- stmt.Implicit = true
- stmt.Etype = EType(OSUB)
+ stmt.Etype = EType(p.op)
return stmt
case ':':
return hdr
}
-// TODO(gri) should have lexer return this info - no need for separate lookup
-// (issue 13244)
-var prectab = map[int32]struct {
- prec int // > 0 (0 indicates not found)
- op Op
-}{
- // not an expression anymore, but left in so we can give a good error
- // message when used in expression context
- LCOMM: {1, OSEND},
-
- LOROR: {2, OOROR},
-
- LANDAND: {3, OANDAND},
-
- LEQ: {4, OEQ},
- LNE: {4, ONE},
- LLE: {4, OLE},
- LGE: {4, OGE},
- LLT: {4, OLT},
- LGT: {4, OGT},
-
- '+': {5, OADD},
- '-': {5, OSUB},
- '|': {5, OOR},
- '^': {5, OXOR},
-
- '*': {6, OMUL},
- '/': {6, ODIV},
- '%': {6, OMOD},
- '&': {6, OAND},
- LLSH: {6, OLSH},
- LRSH: {6, ORSH},
- LANDNOT: {6, OANDNOT},
-}
-
// Expression = UnaryExpr | Expression binary_op Expression .
-func (p *parser) bexpr(prec int) *Node {
+func (p *parser) bexpr(prec OpPrec) *Node {
// don't trace bexpr - only leads to overly nested trace output
+ // prec is precedence of the prior/enclosing binary operator (if any),
+ // so we only want to parse tokens of greater precedence.
+
x := p.uexpr()
- for {
- t := prectab[p.tok]
- if t.prec < prec {
- return x
- }
+ for p.prec > prec {
+ op, prec1 := p.op, p.prec
p.next()
- x = Nod(t.op, x, p.bexpr(t.prec+1))
+ x = Nod(op, x, p.bexpr(prec1))
}
+ return x
}
func (p *parser) expr() *Node {
defer p.trace("expr")()
}
- return p.bexpr(1)
+ return p.bexpr(0)
}
func unparen(x *Node) *Node {