GoToSocial/vendor/github.com/chenzhuoyu/iasm/expr/parser.go

314 lines
7.1 KiB
Go

package expr
import (
`strconv`
`unicode`
`unsafe`
)
type _TokenKind uint8
const (
_T_end _TokenKind = iota + 1
_T_int
_T_punc
_T_name
)
const (
_OP2 = 0x80
_POW = _OP2 | '*'
_SHL = _OP2 | '<'
_SHR = _OP2 | '>'
)
type _Slice struct {
p unsafe.Pointer
n int
c int
}
type _Token struct {
pos int
ptr *rune
u64 uint64
tag _TokenKind
}
func (self _Token) str() (v string) {
return string(self.rbuf())
}
func (self _Token) rbuf() (v []rune) {
(*_Slice)(unsafe.Pointer(&v)).c = int(self.u64)
(*_Slice)(unsafe.Pointer(&v)).n = int(self.u64)
(*_Slice)(unsafe.Pointer(&v)).p = unsafe.Pointer(self.ptr)
return
}
func tokenEnd(p int) _Token {
return _Token {
pos: p,
tag: _T_end,
}
}
func tokenInt(p int, v uint64) _Token {
return _Token {
pos: p,
u64: v,
tag: _T_int,
}
}
func tokenPunc(p int, v rune) _Token {
return _Token {
pos: p,
tag: _T_punc,
u64: uint64(v),
}
}
func tokenName(p int, v []rune) _Token {
return _Token {
pos: p,
ptr: &v[0],
tag: _T_name,
u64: uint64(len(v)),
}
}
// Repository represents a repository of Term's.
type Repository interface {
Get(name string) (Term, error)
}
// Parser parses an expression string to it's AST representation.
type Parser struct {
pos int
src []rune
}
var binaryOps = [...]func(*Expr, *Expr) *Expr {
'+' : (*Expr).Add,
'-' : (*Expr).Sub,
'*' : (*Expr).Mul,
'/' : (*Expr).Div,
'%' : (*Expr).Mod,
'&' : (*Expr).And,
'^' : (*Expr).Xor,
'|' : (*Expr).Or,
_SHL : (*Expr).Shl,
_SHR : (*Expr).Shr,
_POW : (*Expr).Pow,
}
var precedence = [...]map[int]bool {
{_SHL: true, _SHR: true},
{'|' : true},
{'^' : true},
{'&' : true},
{'+' : true, '-': true},
{'*' : true, '/': true, '%': true},
{_POW: true},
}
func (self *Parser) ch() rune {
return self.src[self.pos]
}
func (self *Parser) eof() bool {
return self.pos >= len(self.src)
}
func (self *Parser) rch() (v rune) {
v, self.pos = self.src[self.pos], self.pos + 1
return
}
func (self *Parser) hex(ss []rune) bool {
if len(ss) == 1 && ss[0] == '0' {
return unicode.ToLower(self.ch()) == 'x'
} else if len(ss) <= 1 || unicode.ToLower(ss[1]) != 'x' {
return unicode.IsDigit(self.ch())
} else {
return ishexdigit(self.ch())
}
}
func (self *Parser) int(p int, ss []rune) (_Token, error) {
var err error
var val uint64
/* find all the digits */
for !self.eof() && self.hex(ss) {
ss = append(ss, self.rch())
}
/* parse the value */
if val, err = strconv.ParseUint(string(ss), 0, 64); err != nil {
return _Token{}, err
} else {
return tokenInt(p, val), nil
}
}
func (self *Parser) name(p int, ss []rune) _Token {
for !self.eof() && isident(self.ch()) { ss = append(ss, self.rch()) }
return tokenName(p, ss)
}
func (self *Parser) read(p int, ch rune) (_Token, error) {
if isdigit(ch) {
return self.int(p, []rune { ch })
} else if isident0(ch) {
return self.name(p, []rune { ch }), nil
} else if isop2ch(ch) && !self.eof() && self.ch() == ch {
return tokenPunc(p, _OP2 | self.rch()), nil
} else if isop1ch(ch) {
return tokenPunc(p, ch), nil
} else {
return _Token{}, newSyntaxError(self.pos, "invalid character " + strconv.QuoteRuneToASCII(ch))
}
}
func (self *Parser) next() (_Token, error) {
for {
var p int
var c rune
/* check for EOF */
if self.eof() {
return tokenEnd(self.pos), nil
}
/* read the next char */
p = self.pos
c = self.rch()
/* parse the token if not a space */
if !unicode.IsSpace(c) {
return self.read(p, c)
}
}
}
func (self *Parser) grab(tk _Token, repo Repository) (*Expr, error) {
if repo == nil {
return nil, newSyntaxError(tk.pos, "unresolved symbol: " + tk.str())
} else if term, err := repo.Get(tk.str()); err != nil {
return nil, err
} else {
return Ref(term), nil
}
}
func (self *Parser) nest(nest int, repo Repository) (*Expr, error) {
var err error
var ret *Expr
var ntk _Token
/* evaluate the nested expression */
if ret, err = self.expr(0, nest + 1, repo); err != nil {
return nil, err
}
/* must follows with a ')' */
if ntk, err = self.next(); err != nil {
return nil, err
} else if ntk.tag != _T_punc || ntk.u64 != ')' {
return nil, newSyntaxError(ntk.pos, "')' expected")
} else {
return ret, nil
}
}
func (self *Parser) unit(nest int, repo Repository) (*Expr, error) {
if tk, err := self.next(); err != nil {
return nil, err
} else if tk.tag == _T_int {
return Int(int64(tk.u64)), nil
} else if tk.tag == _T_name {
return self.grab(tk, repo)
} else if tk.tag == _T_punc && tk.u64 == '(' {
return self.nest(nest, repo)
} else if tk.tag == _T_punc && tk.u64 == '+' {
return self.unit(nest, repo)
} else if tk.tag == _T_punc && tk.u64 == '-' {
return neg2(self.unit(nest, repo))
} else if tk.tag == _T_punc && tk.u64 == '~' {
return not2(self.unit(nest, repo))
} else {
return nil, newSyntaxError(tk.pos, "integer, unary operator or nested expression expected")
}
}
func (self *Parser) term(prec int, nest int, repo Repository) (*Expr, error) {
var err error
var val *Expr
/* parse the LHS operand */
if val, err = self.expr(prec + 1, nest, repo); err != nil {
return nil, err
}
/* parse all the operators of the same precedence */
for {
var op int
var rv *Expr
var tk _Token
/* peek the next token */
pp := self.pos
tk, err = self.next()
/* check for errors */
if err != nil {
return nil, err
}
/* encountered EOF */
if tk.tag == _T_end {
return val, nil
}
/* must be an operator */
if tk.tag != _T_punc {
return nil, newSyntaxError(tk.pos, "operators expected")
}
/* check for the operator precedence */
if op = int(tk.u64); !precedence[prec][op] {
self.pos = pp
return val, nil
}
/* evaluate the RHS operand, and combine the value */
if rv, err = self.expr(prec + 1, nest, repo); err != nil {
return nil, err
} else {
val = binaryOps[op](val, rv)
}
}
}
func (self *Parser) expr(prec int, nest int, repo Repository) (*Expr, error) {
if prec >= len(precedence) {
return self.unit(nest, repo)
} else {
return self.term(prec, nest, repo)
}
}
// Parse parses the expression, and returns it's AST tree.
func (self *Parser) Parse(repo Repository) (*Expr, error) {
return self.expr(0, 0, repo)
}
// SetSource resets the expression parser and sets the expression source.
func (self *Parser) SetSource(src string) *Parser {
self.pos = 0
self.src = []rune(src)
return self
}