ceremonyclient/bedlam/compiler/parser.go
Cassandra Heart e51992f3e8
OT
2025-03-23 21:11:16 -05:00

1806 lines
35 KiB
Go

//
// Copyright (c) 2019-2024 Markku Rossi
//
// All rights reserved.
//
package compiler
import (
"errors"
"fmt"
"io"
"runtime"
"strings"
"source.quilibrium.com/quilibrium/monorepo/bedlam/compiler/ast"
"source.quilibrium.com/quilibrium/monorepo/bedlam/compiler/utils"
)
// Parser implements QCL parser.
type Parser struct {
compiler *Compiler
logger *utils.Logger
lexer *Lexer
pkg *ast.Package
}
// NewParser creates a new parser.
func NewParser(source string, compiler *Compiler, logger *utils.Logger,
in io.Reader) *Parser {
return &Parser{
compiler: compiler,
logger: logger,
lexer: NewLexer(source, in),
}
}
// Parse parses a package.
func (p *Parser) Parse(pkg *ast.Package) (*ast.Package, error) {
name, doc, err := p.parsePackage()
if err != nil {
return nil, err
}
if pkg == nil {
p.pkg = ast.NewPackage(name, p.lexer.Source(), doc)
} else {
// This source file must be in the same package.
if name != pkg.Name {
return nil, fmt.Errorf("found packages %s and %s", name, pkg.Name)
}
p.pkg = pkg
}
token, err := p.lexer.Get()
if err != nil {
if err != io.EOF {
return nil, err
}
return p.pkg, nil
}
if token.Type == TSymImport {
_, err = p.needToken('(')
if err != nil {
return nil, err
}
for {
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
if t.Type == ')' {
break
}
var alias string
if t.Type == TIdentifier {
alias = t.StrVal
t, err = p.lexer.Get()
if err != nil {
return nil, err
}
}
if t.Type != TConstant {
return nil, p.errUnexpected(t, TConstant)
}
str, ok := t.ConstVal.(string)
if !ok {
return nil, p.errUnexpected(t, TConstant)
}
_, ok = p.pkg.Imports[str]
if ok {
return nil, p.errf(t.From,
"package %s imported more than once", str)
}
if len(alias) == 0 {
parts := strings.Split(str, "/")
alias = parts[len(parts)-1]
}
p.pkg.Imports[alias] = str
}
} else {
p.lexer.Unget(token)
}
for {
err = p.parseToplevel()
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
}
return p.pkg, nil
}
var leaves = map[string]bool{
"errorLoc": true,
"errf": true,
"errUnexpected": true,
"needToken": true,
}
func (p *Parser) errorLoc(msg string) {
if !p.compiler.params.QCLCErrorLoc {
return
}
for i := 3; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
return
}
f := runtime.FuncForPC(pc)
if f != nil {
name := f.Name()
idx := strings.LastIndexByte(name, '.')
if idx >= 0 {
name = name[idx+1:]
}
if leaves[name] {
continue
}
}
fmt.Printf("%s:%d: MCPLC error:\n\u2514\u2574%s\n", file, line, msg)
return
}
}
func (p *Parser) errf(loc utils.Point, format string, a ...interface{}) error {
msg := fmt.Sprintf(format, a...)
p.errorLoc(msg)
p.lexer.FlushEOL()
line, ok := p.lexer.history[loc.Line]
if ok {
var indicator []rune
for i := 0; i < loc.Col; i++ {
var r rune
if line[i] == '\t' {
r = '\t'
} else {
r = ' '
}
indicator = append(indicator, r)
}
indicator = append(indicator, '^')
p.logger.Errorf(loc, "%s\n%s\n%s\n",
msg, string(line), string(indicator))
return errors.New(msg)
}
p.logger.Errorf(loc, "%s", msg)
return errors.New(msg)
}
func (p *Parser) errUnexpected(offending *Token, expected TokenType) error {
return p.errf(offending.From, "unexpected token '%s': expected '%s'",
offending, expected)
}
func (p *Parser) needToken(tt TokenType) (*Token, error) {
token, err := p.lexer.Get()
if err != nil {
return nil, err
}
if token.Type != tt {
p.lexer.Unget(token)
return nil, p.errUnexpected(token, tt)
}
return token, nil
}
func (p *Parser) sameLine(current utils.Point) bool {
t, err := p.lexer.Get()
if err != nil {
return false
}
p.lexer.Unget(t)
return t.From.Line == current.Line
}
func (p *Parser) parsePackage() (string, ast.Annotations, error) {
t, err := p.needToken(TSymPackage)
if err != nil {
return "", nil, err
}
t, err = p.needToken(TIdentifier)
if err != nil {
return "", nil, err
}
parts := strings.Split(t.StrVal, "/")
return parts[len(parts)-1], p.lexer.Annotations(t.From), nil
}
func (p *Parser) parseToplevel() error {
token, err := p.lexer.Get()
if err != nil {
return err
}
switch token.Type {
case TSymConst:
// XXX not fully according to syntax:
//
// ConstDecl = 'const', ( ConstSpec | '(', { ConstSpec }, ')' );
// ConstSpec = IdentifierList, [ Type ], '=', ExpressionList;
// ExpressionList = Expression, { ',', Expression };
return p.parseGlobalVar(true, p.lexer.Annotations(token.From))
case TSymVar:
// XXX not fully according to syntax:
//
// VarDecl = 'var', ( VarSpec | '(', { VarSpec }, ')' );
// VarSpec = IdentifierList, ( Type, [ '=', ExpressionList ]
// | '=', ExpressionList );
return p.parseGlobalVar(false, p.lexer.Annotations(token.From))
case TSymType:
return p.parseTypeDecl(p.lexer.Annotations(token.From))
case TSymFunc:
// FunctionDecl | MethodDecl
//
// FunctionDecl = "func" FunctionName Signature [ FunctionBody ] .
// FunctionName = identifier .
// FunctionBody = Block .
//
// MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
// MethodName = identifier .
// Receiver = Parameters .
n, err := p.lexer.Get()
if err != nil {
return err
}
p.lexer.Unget(n)
switch n.Type {
case TIdentifier:
f, err := p.parseFunc(p.lexer.Annotations(token.From))
if err != nil {
return err
}
_, ok := p.pkg.Functions[f.Name]
if ok {
return p.errf(f.Location(), "function %s already defined",
f.Name)
}
p.pkg.Functions[f.Name] = f
case '(':
loc := n.From
_, err = p.needToken('(')
if err != nil {
return err
}
n, err := p.lexer.Get()
if err != nil {
return err
}
if n.Type != TIdentifier {
return p.errUnexpected(n, TIdentifier)
}
ti, err := p.parseType()
if err != nil {
return err
}
_, err = p.needToken(')')
if err != nil {
return err
}
f, err := p.parseFunc(p.lexer.Annotations(token.From))
if err != nil {
return err
}
f.This = &ast.Variable{
Point: loc,
Name: n.StrVal,
Type: ti,
}
return p.addMethod(ti, f)
default:
return p.errf(n.From, "unexpected '%s', expecting name or (", n)
}
default:
return p.errf(token.From, "unexpected token '%s'", token.Type)
}
return nil
}
func (p *Parser) addMethod(ti *ast.TypeInfo, f *ast.Func) error {
t := ti
if t.Type == ast.TypePointer {
t = t.ElementType
}
if t.Type != ast.TypeName {
return p.errf(ti.Point,
"invalid receiver type %s (%s is not a defined type)", ti, ti)
}
for _, pkgType := range p.pkg.Types {
if pkgType.TypeName == t.Name.Name {
if pkgType.Methods == nil {
pkgType.Methods = make(map[string]*ast.Func)
}
_, ok := pkgType.Methods[f.Name]
if ok {
return p.errf(f.Point, "(%s).%s redeclared in this block",
ti, f.Name)
}
pkgType.Methods[f.Name] = f
return nil
}
}
return p.errf(ti.Location(), "type %s.%s not found", p.pkg.Name, t.Name)
}
func (p *Parser) parseGlobalVar(isConst bool,
annotations ast.Annotations) error {
token, err := p.lexer.Get()
if err != nil {
return err
}
switch token.Type {
case TIdentifier:
return p.parseGlobalVarDef(token, isConst, annotations)
case '(':
for {
t, err := p.lexer.Get()
if err != nil {
return err
}
if t.Type == ')' {
return nil
}
err = p.parseGlobalVarDef(t, isConst,
p.lexer.Annotations(t.From))
if err != nil {
return err
}
}
default:
return p.errf(token.From, "unexpected token '%s'", token.Type)
}
}
func (p *Parser) parseGlobalVarDef(token *Token, isConst bool,
annotations ast.Annotations) error {
if token.Type != TIdentifier {
return p.errf(token.From, "unexpected token '%s'", token.Type)
}
t, err := p.lexer.Get()
if err != nil {
return err
}
var varType *ast.TypeInfo
var init ast.AST
if t.Type == '=' {
init, err = p.parseExpr(false)
if err != nil {
return err
}
} else {
p.lexer.Unget(t)
varType, err = p.parseType()
if err != nil {
return err
}
t, err = p.lexer.Get()
if err != nil {
return nil
}
if t.Type == '=' {
init, err = p.parseExpr(false)
if err != nil {
return err
}
} else {
p.lexer.Unget(t)
}
}
if isConst {
p.pkg.Constants = append(p.pkg.Constants, &ast.ConstantDef{
Point: token.From,
Name: token.StrVal,
Type: varType,
Init: init,
Annotations: annotations,
})
} else {
p.pkg.Variables = append(p.pkg.Variables, &ast.VariableDef{
Point: token.From,
Names: []string{token.StrVal},
Type: varType,
Init: init,
Annotations: annotations,
})
}
return nil
}
func (p *Parser) parseTypeDecl(annotations ast.Annotations) error {
name, err := p.needToken(TIdentifier)
if err != nil {
return err
}
t, err := p.lexer.Get()
if err != nil {
return err
}
switch t.Type {
case TSymStruct:
loc := t.From
_, err := p.needToken('{')
if err != nil {
}
var fields []ast.StructField
for {
t, err := p.lexer.Get()
if err != nil {
return err
}
if t.Type == '}' {
break
}
var names []*Token
for {
if t.Type != TIdentifier {
return p.errf(t.From, "unexpected token '%s'", t.Type)
}
names = append(names, t)
t, err = p.lexer.Get()
if err != nil {
return err
}
if t.Type != ',' {
p.lexer.Unget(t)
break
}
t, err = p.lexer.Get()
if err != nil {
return err
}
}
typeInfo, err := p.parseType()
if err != nil {
return err
}
// Expand names.
for _, n := range names {
fields = append(fields, ast.StructField{
Point: n.From,
Name: n.StrVal,
Type: typeInfo,
})
}
}
typeInfo := &ast.TypeInfo{
Point: loc,
Type: ast.TypeStruct,
TypeName: name.StrVal,
StructFields: fields,
Annotations: annotations,
}
p.pkg.Types = append(p.pkg.Types, typeInfo)
return nil
case '=':
ti, err := p.parseType()
if err != nil {
return err
}
typeInfo := &ast.TypeInfo{
Point: ti.Point,
Type: ast.TypeAlias,
TypeName: name.StrVal,
AliasType: ti,
Annotations: annotations,
}
p.pkg.Types = append(p.pkg.Types, typeInfo)
return nil
default:
// TypeDef = identifier Type .
p.lexer.Unget(t)
typeInfo, err := p.parseType()
if err != nil {
return err
}
typeInfo.TypeName = name.StrVal
typeInfo.Annotations = annotations
p.pkg.Types = append(p.pkg.Types, typeInfo)
return nil
}
}
func (p *Parser) parseFunc(annotations ast.Annotations) (*ast.Func, error) {
name, err := p.needToken(TIdentifier)
if err != nil {
return nil, err
}
_, err = p.needToken('(')
if err != nil {
return nil, err
}
// Argument list.
var arguments []*ast.Variable
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
if t.Type != ')' {
p.lexer.Unget(t)
for {
t, err = p.needToken(TIdentifier)
if err != nil {
return nil, err
}
arg := &ast.Variable{
Point: t.From,
Name: t.StrVal,
}
t, err = p.lexer.Get()
if err != nil {
return nil, err
}
if t.Type == ',' {
arguments = append(arguments, arg)
continue
}
p.lexer.Unget(t)
// Type.
typeInfo, err := p.parseType()
if err != nil {
return nil, err
}
arg.Type = typeInfo
// All untyped arguments get this type.
for i := len(arguments) - 1; i >= 0; i-- {
if arguments[i].Type != nil {
break
}
arguments[i].Type = typeInfo
}
// Append new argument.
arguments = append(arguments, arg)
t, err = p.lexer.Get()
if err != nil {
return nil, err
}
if t.Type == ')' {
break
}
if t.Type != ',' {
return nil, p.errUnexpected(t, ',')
}
}
}
// Return values.
var returnValues []*ast.Variable
var namedReturnValues bool
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
switch n.Type {
case '(':
var identifiers []*ast.TypeInfo
for {
typeInfo, err := p.parseType()
if err != nil {
return nil, err
}
// Peek next token.
n, err = p.lexer.Get()
if err != nil {
return nil, err
}
p.lexer.Unget(n)
switch n.Type {
case ',':
identifiers = append(identifiers, typeInfo)
case ')':
if namedReturnValues {
return nil, p.errf(typeInfo.Point,
"mixing named and unnamed return values")
}
identifiers = append(identifiers, typeInfo)
for _, id := range identifiers {
returnValues = append(returnValues, &ast.Variable{
Point: id.Point,
Type: id,
})
}
default:
// typeInfo is named return variable and the next
// component is its type.
if !typeInfo.IsIdentifier() {
return nil, p.errf(n.From,
"unexpected %s, expecting comma or )", n)
}
identifiers = append(identifiers, typeInfo)
typeInfo, err = p.parseType()
if err != nil {
return nil, err
}
// Add current list of identifiers to return
// values. All elements in identifiers must be
// identifiers.
for _, id := range identifiers {
if !id.IsIdentifier() {
return nil, p.errf(id.Point,
"mixing named and unnamed return values")
}
returnValues = append(returnValues, &ast.Variable{
Point: id.Point,
Name: id.String(),
Type: typeInfo,
})
}
namedReturnValues = true
identifiers = nil
}
n, err = p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type == ')' {
break
}
if n.Type != ',' {
return nil, p.errUnexpected(n, ',')
}
}
_, err = p.needToken('{')
if err != nil {
return nil, err
}
case '{':
default:
p.lexer.Unget(n)
typeInfo, err := p.parseType()
if err != nil {
return nil, err
}
returnValues = append(returnValues, &ast.Variable{
Point: n.From,
Type: typeInfo,
})
_, err = p.needToken('{')
if err != nil {
return nil, err
}
}
body, end, err := p.parseBlock()
if err != nil {
return nil, err
}
return ast.NewFunc(name.From, name.StrVal, arguments, returnValues,
namedReturnValues, body, end, annotations), nil
}
func (p *Parser) parseBlock() (ast.List, utils.Point, error) {
var result ast.List
var end utils.Point
for {
t, err := p.lexer.Get()
if err != nil {
return nil, utils.Point{}, err
}
if t.Type == '}' {
end = t.From
break
}
p.lexer.Unget(t)
ast, err := p.parseStatement(false)
if err != nil {
return nil, utils.Point{}, err
}
result = append(result, ast)
}
return result, end, nil
}
func (p *Parser) parseStatement(needLBrace bool) (ast.AST, error) {
tStmt, err := p.lexer.Get()
if err != nil {
return nil, err
}
switch tStmt.Type {
case TSymVar:
var names []string
for {
tName, err := p.needToken(TIdentifier)
if err != nil {
return nil, err
}
names = append(names, tName.StrVal)
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
if t.Type != ',' {
p.lexer.Unget(t)
break
}
}
typeInfo, err := p.parseType()
if err != nil {
return nil, err
}
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
var expr ast.AST
if t.Type == '=' {
// Initializer.
expr, err = p.parseExpr(needLBrace)
if err != nil {
return nil, err
}
} else {
p.lexer.Unget(t)
}
return &ast.VariableDef{
Point: tStmt.From,
Names: names,
Type: typeInfo,
Init: expr,
}, nil
case TSymIf:
expr, err := p.parseExpr(true)
if err != nil {
return nil, err
}
_, err = p.needToken('{')
if err != nil {
return nil, err
}
var b1, b2 ast.AST
b1, _, err = p.parseBlock()
if err != nil {
return nil, err
}
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
if t.Type == TSymElse {
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
switch n.Type {
case '{':
b2, _, err = p.parseBlock()
if err != nil {
return nil, err
}
case TSymIf:
p.lexer.Unget(n)
b2, err = p.parseStatement(needLBrace)
if err != nil {
}
default:
return nil, p.errf(n.From, "unexpected %s, expected if or {", n)
}
} else {
p.lexer.Unget(t)
}
return &ast.If{
Expr: expr,
True: b1,
False: b2,
}, nil
case TSymReturn:
var exprs []ast.AST
if p.sameLine(tStmt.To) {
expr, err := p.parseExpr(needLBrace)
if err != nil {
return nil, err
}
exprs = append(exprs, expr)
for {
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
if t.Type != ',' {
p.lexer.Unget(t)
break
}
expr, err = p.parseExpr(needLBrace)
if err != nil {
return nil, err
}
exprs = append(exprs, expr)
}
}
return &ast.Return{
Point: tStmt.From,
Exprs: exprs,
}, nil
// ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
// Condition = Expression .
case TSymFor:
var init ast.AST
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
p.lexer.Unget(n)
if n.Type != ';' {
// init | Condition | RangeClause
init, err = p.parseStatement(true)
if err != nil {
return nil, err
}
forRange, ok := init.(*ast.ForRange)
if ok {
// for RangeClause
_, err := p.needToken('{')
if err != nil {
return nil, err
}
body, _, err := p.parseBlock()
if err != nil {
return nil, err
}
forRange.Body = body
return forRange, nil
}
// init | Condition
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type == '{' {
// for Condition
list, ok := init.(ast.List)
if !ok || len(list) != 1 {
return nil, p.errf(init.Location(),
"unexpected condition: %v", init)
}
body, _, err := p.parseBlock()
if err != nil {
return nil, err
}
return &ast.For{
Point: tStmt.From,
Cond: list[0],
Body: body,
}, nil
}
p.lexer.Unget(n)
}
_, err = p.needToken(';')
if err != nil {
return nil, err
}
cond, err := p.parseExpr(false)
if err != nil {
return nil, err
}
_, err = p.needToken(';')
if err != nil {
return nil, err
}
inc, err := p.parseStatement(true)
if err != nil {
return nil, err
}
_, err = p.needToken('{')
if err != nil {
return nil, err
}
body, _, err := p.parseBlock()
if err != nil {
return nil, err
}
return &ast.For{
Point: tStmt.From,
Init: init,
Cond: cond,
Inc: inc,
Body: body,
}, nil
default:
p.lexer.Unget(tStmt)
lvalues, err := p.parseExprList(needLBrace)
if err != nil {
return nil, err
}
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
switch t.Type {
case '=', TDefAssign:
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type == TSymRange {
expr, err := p.parseExpr(true)
if err != nil {
return nil, err
}
return &ast.ForRange{
Point: t.From,
ExprList: lvalues,
Def: t.Type == TDefAssign,
Expr: expr,
}, nil
}
p.lexer.Unget(n)
values, err := p.parseExprList(needLBrace)
if err != nil {
return nil, err
}
return &ast.Assign{
Point: t.From,
LValues: lvalues,
Exprs: values,
Define: t.Type == TDefAssign,
}, nil
case TPlusEq, TMinusEq, TMultEq, TDivEq, TOrEq, TXorEq, TAndEq,
TLshiftEq, TRshiftEq:
if len(lvalues) != 1 {
return nil, p.errf(tStmt.From, "expected 1 expression")
}
var op ast.BinaryType
switch t.Type {
case TPlusEq:
op = ast.BinaryAdd
case TMinusEq:
op = ast.BinarySub
case TMultEq:
op = ast.BinaryMul
case TDivEq:
op = ast.BinaryDiv
case TOrEq:
op = ast.BinaryBor
case TXorEq:
op = ast.BinaryBxor
case TAndEq:
op = ast.BinaryBand
case TLshiftEq:
op = ast.BinaryLshift
case TRshiftEq:
op = ast.BinaryRshift
default:
panic(t.Type)
}
value, err := p.parseExpr(needLBrace)
if err != nil {
return nil, err
}
return &ast.Assign{
Point: t.From,
LValues: lvalues,
Exprs: []ast.AST{
&ast.Binary{
Point: t.From,
Left: lvalues[0],
Op: op,
Right: value,
},
},
}, nil
case TPlusPlus, TMinusMinus:
if len(lvalues) != 1 {
return nil, p.errf(tStmt.From, "expected 1 expression")
}
var op ast.BinaryType
if t.Type == TPlusPlus {
op = ast.BinaryAdd
} else {
op = ast.BinarySub
}
return &ast.Assign{
Point: t.From,
LValues: lvalues,
Exprs: []ast.AST{
&ast.Binary{
Point: t.From,
Left: lvalues[0],
Op: op,
Right: &ast.BasicLit{
Point: t.From,
Value: int64(1),
},
},
},
}, nil
default:
p.lexer.Unget(t)
return ast.List(lvalues), nil
}
}
}
func (p *Parser) parseExprList(needLBrace bool) ([]ast.AST, error) {
var list []ast.AST
for {
expr, err := p.parseExpr(needLBrace)
if err != nil {
return nil, err
}
list = append(list, expr)
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
if t.Type != ',' {
p.lexer.Unget(t)
break
}
}
return list, nil
}
func (p *Parser) parseExpr(needLBrace bool) (ast.AST, error) {
// Precedence Operator
// -----------------------------
// 5 * / % << >> & &^
// 4 + - | ^
// 3 == != < <= > >=
// 2 &&
// 1 ||
return p.parseExprLogicalOr(needLBrace)
}
func (p *Parser) parseExprLogicalOr(needLBrace bool) (ast.AST, error) {
left, err := p.parseExprLogicalAnd(needLBrace)
if err != nil {
return nil, err
}
for {
t, err := p.lexer.Get()
if err != nil {
if err != io.EOF {
return nil, err
}
return left, nil
}
if t.Type != TOr {
p.lexer.Unget(t)
return left, nil
}
right, err := p.parseExprLogicalAnd(needLBrace)
if err != nil {
return nil, err
}
left = &ast.Binary{
Point: t.From,
Left: left,
Op: t.Type.BinaryType(),
Right: right,
}
}
}
func (p *Parser) parseExprLogicalAnd(needLBrace bool) (ast.AST, error) {
left, err := p.parseExprComparative(needLBrace)
if err != nil {
return nil, err
}
for {
t, err := p.lexer.Get()
if err != nil {
if err != io.EOF {
return nil, err
}
return left, nil
}
if t.Type != TAnd {
p.lexer.Unget(t)
return left, nil
}
right, err := p.parseExprComparative(needLBrace)
if err != nil {
return nil, err
}
left = &ast.Binary{
Point: t.From,
Left: left,
Op: t.Type.BinaryType(),
Right: right,
}
}
}
func (p *Parser) parseExprComparative(needLBrace bool) (ast.AST, error) {
left, err := p.parseExprAdditive(needLBrace)
if err != nil {
return nil, err
}
for {
t, err := p.lexer.Get()
if err != nil {
if err != io.EOF {
return nil, err
}
return left, nil
}
switch t.Type {
case TEq, TNeq, TLt, TLe, TGt, TGe:
right, err := p.parseExprAdditive(needLBrace)
if err != nil {
return nil, err
}
left = &ast.Binary{
Point: t.From,
Left: left,
Op: t.Type.BinaryType(),
Right: right,
}
default:
p.lexer.Unget(t)
return left, nil
}
}
}
func (p *Parser) parseExprAdditive(needLBrace bool) (ast.AST, error) {
left, err := p.parseExprMultiplicative(needLBrace)
if err != nil {
return nil, err
}
for {
t, err := p.lexer.Get()
if err != nil {
if err != io.EOF {
return nil, err
}
return left, nil
}
switch t.Type {
case '+', '-', '|', '^':
right, err := p.parseExprMultiplicative(needLBrace)
if err != nil {
return nil, err
}
left = &ast.Binary{
Point: t.From,
Left: left,
Op: t.Type.BinaryType(),
Right: right,
}
default:
p.lexer.Unget(t)
return left, nil
}
}
}
func (p *Parser) parseExprMultiplicative(needLBrace bool) (ast.AST, error) {
left, err := p.parseExprUnary(needLBrace)
if err != nil {
return nil, err
}
for {
t, err := p.lexer.Get()
if err != nil {
if err != io.EOF {
return nil, err
}
return left, nil
}
switch t.Type {
case '*', '/', '%', TLshift, TRshift, '&', TBitClear:
right, err := p.parseExprUnary(needLBrace)
if err != nil {
return nil, err
}
left = &ast.Binary{
Point: t.From,
Left: left,
Op: t.Type.BinaryType(),
Right: right,
}
default:
p.lexer.Unget(t)
return left, nil
}
}
}
// UnaryExpr = PrimaryExpr | unary_op UnaryExpr .
func (p *Parser) parseExprUnary(needLBrace bool) (ast.AST, error) {
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
switch t.Type {
case '+', '-', '!', '^', '*', '&', TSend:
expr, err := p.parseExprUnary(needLBrace)
if err != nil {
return nil, err
}
return &ast.Unary{
Point: t.From,
Type: t.Type.UnaryType(),
Expr: expr,
}, nil
default:
p.lexer.Unget(t)
return p.parseExprPrimary(needLBrace)
}
}
// PrimaryExpr =
// Operand |
// Conversion |
// MethodExpr |
// PrimaryExpr Selector |
// PrimaryExpr Index |
// PrimaryExpr Slice |
// PrimaryExpr TypeAssertion |
// PrimaryExpr Arguments .
//
// Selector = "." identifier .
// Index = "[" Expression "]" .
// Slice = "[" [ Expression ] ":" [ Expression ] "]" |
// "[" [ Expression ] ":" Expression ":" Expression "]" .
// TypeAssertion = "." "(" Type ")" .
// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
func (p *Parser) parseExprPrimary(needLBrace bool) (ast.AST, error) {
primary, err := p.parseOperand(needLBrace)
if err != nil {
return nil, err
}
primary:
for {
t, err := p.lexer.Get()
if err != nil {
if err == io.EOF {
return primary, nil
}
return nil, err
}
switch t.Type {
case '.':
// Selector.
return nil, p.errf(t.From, "selector not implemented yet")
case '[':
var expr1, expr2 ast.AST
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type != ':' {
p.lexer.Unget(n)
expr1, err = p.parseExpr(needLBrace)
if err != nil {
return nil, err
}
n, err = p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type == ']' {
primary = &ast.Index{
Point: primary.Location(),
Expr: primary,
Index: expr1,
}
continue primary
}
if n.Type != ':' {
p.lexer.Unget(n)
return nil, p.errUnexpected(n, ':')
}
}
n, err = p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type != ']' {
p.lexer.Unget(n)
expr2, err = p.parseExpr(needLBrace)
if err != nil {
return nil, err
}
_, err = p.needToken(']')
if err != nil {
return nil, err
}
}
primary = &ast.Slice{
Point: primary.Location(),
Expr: primary,
From: expr1,
To: expr2,
}
case '(':
vr, ok := primary.(*ast.VariableRef)
if !ok {
return nil, p.errf(primary.Location(),
"non-function %s used as function", primary)
}
var arguments []ast.AST
var isMake bool
var ti *ast.TypeInfo
if vr.String() == "make" {
isMake = true
}
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type != ')' {
p.lexer.Unget(n)
for {
if isMake && ti == nil {
ti, err = p.parseType()
if err != nil {
return nil, err
}
} else {
expr, err := p.parseExpr(needLBrace)
if err != nil {
return nil, err
}
arguments = append(arguments, expr)
}
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type == ')' {
break
} else if n.Type != ',' {
return nil, p.errf(n.From, "unexpected token %s", n)
}
}
}
if isMake {
if ti == nil {
return nil, p.errf(primary.Location(),
"missing arguments to make")
}
primary = &ast.Make{
Point: primary.Location(),
Type: ti,
Exprs: arguments,
}
} else if vr.String() == "copy" {
if len(arguments) != 2 {
return nil, p.errf(primary.Location(),
"invalid arguments for copy (expected 2, found %v)",
len(arguments))
}
primary = &ast.Copy{
Point: primary.Location(),
Dst: arguments[0],
Src: arguments[1],
}
} else {
primary = &ast.Call{
Point: primary.Location(),
Ref: vr,
Exprs: arguments,
}
}
default:
p.lexer.Unget(t)
return primary, nil
}
}
}
// Operand = Literal | OperandName | "(" Expression ")" .
// Literal = BasicLit | CompositeLit | FunctionLit .
// BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
// OperandName = identifier | QualifiedIdent .
//
// QualifiedIdent = PackageName "." identifier .
//
// CompositeLit = LiteralType LiteralValue .
// LiteralType = StructType | ArrayType | "[" "..." "]" ElementType |
// SliceType | MapType | TypeName .
// LiteralValue = "{" [ ElementList [ "," ] ] "}" .
// ElementList = KeyedElement { "," KeyedElement } .
// KeyedElement = [ Key ":" ] Element .
// Key = FieldName | Expression | LiteralValue .
// FieldName = identifier .
// Element = Expression | LiteralValue .
func (p *Parser) parseOperand(needLBrace bool) (ast.AST, error) {
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
switch t.Type {
case TConstant: // Literal
return &ast.BasicLit{
Point: t.From,
Value: t.ConstVal,
}, nil
case TSymNil:
return &ast.BasicLit{
Point: t.From,
}, nil
case TIdentifier: // OperandName
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
var operandName *ast.VariableRef
if n.Type == '.' {
id, err := p.needToken(TIdentifier)
if err != nil {
return nil, err
}
// QualifiedIdent.
operandName = &ast.VariableRef{
Point: t.From,
Name: ast.Identifier{
Defined: p.pkg.Name,
Package: t.StrVal,
Name: id.StrVal,
},
}
} else {
// Identifier in current package.
p.lexer.Unget(n)
operandName = &ast.VariableRef{
Point: t.From,
Name: ast.Identifier{
Defined: p.pkg.Name,
Name: t.StrVal,
},
}
}
if needLBrace {
return operandName, nil
}
n, err = p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type != '{' {
p.lexer.Unget(n)
return operandName, nil
}
return p.parseCompositeLit(&ast.TypeInfo{
Point: operandName.Point,
Type: ast.TypeName,
Name: operandName.Name,
})
case '[': // ArrayType LiteralValue
p.lexer.Unget(t)
typeInfo, err := p.parseType()
if err != nil {
return nil, err
}
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
switch n.Type {
case '{':
return p.parseCompositeLit(typeInfo)
case '(':
return p.parseArrayCast(n.From, typeInfo)
default:
return nil, p.errf(n.From, "unexpected token '%s'", n.Type)
}
case '(': // '(' Expression ')'
expr, err := p.parseExpr(false)
if err != nil {
return nil, err
}
_, err = p.needToken(')')
if err != nil {
return nil, err
}
return expr, nil
default:
return nil, p.errf(t.From,
"unexpected token '%s' while parsing expression", t)
}
}
func (p *Parser) parseArrayCast(loc utils.Point, typeInfo *ast.TypeInfo) (
ast.AST, error) {
expr, err := p.parseExpr(false)
if err != nil {
return nil, err
}
_, err = p.needToken(')')
if err != nil {
return nil, err
}
return &ast.ArrayCast{
Point: loc,
TypeInfo: typeInfo,
Expr: expr,
}, nil
}
func (p *Parser) toByteArray(expr ast.AST) ([]byte, error) {
switch val := expr.(type) {
case *ast.BasicLit:
switch v := val.Value.(type) {
case string:
return []byte(v), nil
}
}
return nil, p.errf(expr.Location(), "cannot convert %s to []byte", expr)
}
func (p *Parser) toRuneArray(expr ast.AST) ([]rune, error) {
switch val := expr.(type) {
case *ast.BasicLit:
switch v := val.Value.(type) {
case string:
return []rune(v), nil
}
}
return nil, p.errf(expr.Location(), "cannot convert %s to []rune", expr)
}
func (p *Parser) parseCompositeLit(typeInfo *ast.TypeInfo) (ast.AST, error) {
n, err := p.lexer.Get()
p.lexer.Unget(n)
value, err := p.parseCompositeLitValue(typeInfo)
if err != nil {
return nil, err
}
return &ast.CompositeLit{
Point: n.From,
Type: typeInfo,
Value: value,
}, nil
}
func (p *Parser) parseCompositeLitValue(typeInfo *ast.TypeInfo) (
[]ast.KeyedElement, error) {
var value []ast.KeyedElement
for {
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type == '}' {
break
} else if n.Type == '{' {
if typeInfo.Type != ast.TypeArray {
return nil, p.errf(n.From,
"invalid initializer for type %s", typeInfo)
}
v, err := p.parseCompositeLitValue(typeInfo.ElementType)
if err != nil {
return nil, err
}
value = append(value, ast.KeyedElement{
Element: &ast.CompositeLit{
Point: n.From,
Type: typeInfo.ElementType,
Value: v,
},
})
} else {
p.lexer.Unget(n)
key, err := p.parseExpr(false)
if err != nil {
return nil, err
}
n, err = p.lexer.Get()
if err != nil {
return nil, err
}
var element ast.AST
if n.Type == ':' {
element, err = p.parseExpr(false)
if err != nil {
return nil, err
}
} else {
p.lexer.Unget(n)
element = key
key = nil
}
value = append(value, ast.KeyedElement{
Key: key,
Element: element,
})
}
n, err = p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type == '}' {
p.lexer.Unget(n)
} else if n.Type != ',' {
return nil, p.errf(n.From, "unexpected token %s", n)
}
}
return value, nil
}
// Type = TypeName | TypeLit | "(" Type ")" .
// TypeName = identifier | QualifiedIdent .
// TypeLit = ArrayType | StructType | PointerType | SliceType .
func (p *Parser) parseType() (*ast.TypeInfo, error) {
t, err := p.lexer.Get()
if err != nil {
return nil, err
}
switch t.Type {
case TIdentifier:
loc := t.From
var name string
n, err := p.lexer.Get()
if err != nil {
if err != io.EOF {
return nil, err
}
} else {
if n.Type == '.' {
n, err = p.lexer.Get()
if err != nil {
return nil, err
}
if n.Type == TIdentifier {
name = n.StrVal
loc = n.From
} else {
p.lexer.Unget(n)
}
} else {
p.lexer.Unget(n)
}
}
var pkg string
if len(name) > 0 {
pkg = t.StrVal
} else {
name = t.StrVal
}
return &ast.TypeInfo{
Point: loc,
Type: ast.TypeName,
Name: ast.Identifier{
Defined: p.pkg.Name,
Package: pkg,
Name: name,
},
}, nil
case '[':
loc := t.From
n, err := p.lexer.Get()
if err != nil {
return nil, err
}
var length ast.AST
if n.Type != ']' {
p.lexer.Unget(n)
length, err = p.parseExpr(false)
if err != nil {
return nil, err
}
_, err := p.needToken(']')
if err != nil {
return nil, err
}
}
elType, err := p.parseType()
if err != nil {
return nil, err
}
if length != nil {
return &ast.TypeInfo{
Point: loc,
Type: ast.TypeArray,
ElementType: elType,
ArrayLength: length,
}, nil
}
return &ast.TypeInfo{
Point: loc,
Type: ast.TypeSlice,
ElementType: elType,
}, nil
case '*':
elType, err := p.parseType()
if err != nil {
return nil, err
}
return &ast.TypeInfo{
Point: t.From,
Type: ast.TypePointer,
ElementType: elType,
}, nil
default:
return nil, p.errf(t.From,
"unexpected token '%s' while parsing type", t)
}
}