package parser import ( "fmt" "strconv" "usebox.net/micro-lang/ast" "usebox.net/micro-lang/errors" "usebox.net/micro-lang/tokens" ) // XXX: duplicated const runtime = "" type Env struct { env map[string]ast.Type parent *Env } func NewEnv(parent *Env) *Env { return &Env{make(map[string]ast.Type), parent} } func (e *Env) Get(key string, local bool) (ast.Type, bool) { if v, ok := e.env[key]; ok { return v, true } if !local && e.parent != nil { return e.parent.Get(key, local) } return ast.TypeNone, false } func (e *Env) Set(key string, typ ast.Type) *Env { n := e if _, ok := e.Get(key, false); ok { n = NewEnv(e) } n.env[key] = typ return n } type Parser struct { tokens []tokens.Token pos int env *Env structs map[string]*Env currFuncRet *ast.Type currLoop bool } func NewParser(builtIns map[string]ast.Type) Parser { global := NewEnv(nil) for k, v := range builtIns { // not shadowing any value global.Set(k, v) } return Parser{env: global, structs: make(map[string]*Env)} } func (p *Parser) Parse(tokens []tokens.Token) ([]ast.Expr, error) { var errs []error var exprs []ast.Expr p.tokens = tokens p.pos = 0 for !p.end() { expr, err := p.statement() if err != nil { errs = append(errs, err) p.recover() continue } exprs = append(exprs, expr) } if len(errs) > 0 { return nil, errors.ErrorList{Errors: errs} } return exprs, nil } func (p *Parser) recover() { p.next() for !p.end() { if p.prev().Id == tokens.Semicolon { return } if p.match([]tokens.TokenId{tokens.Def, tokens.Var, tokens.Const, tokens.If, tokens.Return, tokens.For}) { p.back() return } p.next() } } func (p *Parser) next() tokens.Token { if !p.end() { p.pos++ return p.prev() } return p.curr() } func (p *Parser) back() { if p.pos != 0 { p.pos-- } } func (p *Parser) prev() tokens.Token { return p.tokens[p.pos-1] } func (p *Parser) curr() tokens.Token { return p.tokens[p.pos] } func (p *Parser) end() bool { return p.curr().Id == tokens.Eof } func (p *Parser) check(id tokens.TokenId) bool { if p.end() { return false } return p.curr().Id == id } func (p *Parser) match(ids []tokens.TokenId) bool { for _, t := range ids { if p.check(t) { p.next() return true } } return false } func (p *Parser) statement() (ast.Expr, error) { if p.match([]tokens.TokenId{tokens.Var, tokens.Const}) { return p.declaration() } if p.check(tokens.Break) { p.next() return p.breakStmt() } if p.check(tokens.Continue) { p.next() return p.continueStmt() } if p.check(tokens.Return) { p.next() return p.returnStmt() } if p.check(tokens.For) { p.next() return p.forStmt() } if p.check(tokens.If) { return p.ifElse() } if p.check(tokens.LBrace) { return p.block() } if p.check(tokens.Def) { p.next() return p.definition() } expr, err := p.expr() if err != nil { return nil, err } if p.next().Id != tokens.Semicolon { return nil, errors.NewError(errors.ExpectedSemicolon, p.prev().Loc, "';' expected after expression") } return expr, nil } func (p *Parser) expr() (ast.Expr, error) { return p.assign() } func (p *Parser) typ() (*ast.Type, error) { if p.check(tokens.LBracket) { tok := p.next() size, err := p.primary() if err != nil { return nil, err } if p.next().Id != tokens.RBracket { return nil, errors.NewError(errors.ExpectedRBracket, p.prev().Loc, "']' expected in array definition") } typ, err := p.typ() if err != nil { return nil, err } if typ.Value.Id == tokens.TArray { return nil, errors.NewError(errors.InvalidType, p.prev().Loc, "invalid array type", typ.String()) } if size.Resolve().Compare(&ast.TypeNumber) { if v, ok := size.(ast.Literal); ok { tok.Id = tokens.TArray intval := int(v.Numval) return &ast.Type{Value: tok, Ret: typ, Len: &intval}, nil } } return nil, errors.NewError(errors.InvalidValue, p.prev().Loc, "invalid value for array size") } if !p.match([]tokens.TokenId{tokens.TNumber, tokens.TBool, tokens.TString, tokens.TFunc, tokens.Ident}) { return nil, errors.NewError(errors.ExpectedType, p.curr().Loc, "expected type") } typ := p.prev() if typ.Id == tokens.Ident { typ, ok := p.env.Get(typ.Value, false) if !ok || typ.Value.Id != tokens.TStruct { return nil, errors.NewError(errors.ExpectedType, p.curr().Loc, "expected type") } if typ.Inst { return nil, errors.NewError(errors.ExpectedType, p.curr().Loc, "expected type found instance of", typ.String()) } // this is an instance of the struct typ.Inst = true return &typ, nil } if typ.Id != tokens.TFunc { return &ast.Type{Value: typ}, nil } if p.next().Id != tokens.LParen { return nil, errors.NewError(errors.ExpectedLParen, p.prev().Loc, "'(' expected in function type") } params := make([]ast.Type, 0, 16) for !p.check(tokens.RParen) { typ, err := p.typ() if err != nil { return nil, err } params = append(params, *typ) if !p.check(tokens.Comma) { break } p.next() } p.next() var ret *ast.Type if !p.check(tokens.Semicolon) && !p.check(tokens.Assign) && !p.check(tokens.RParen) && !p.check(tokens.LBrace) { if typ, err := p.typ(); err != nil { return nil, err } else { ret = typ } } return &ast.Type{Value: typ, Params: params, Ret: ret}, nil } func (p *Parser) definition() (ast.Expr, error) { name := p.next() if name.Id != tokens.Ident { return nil, errors.NewError(errors.ExpectedIdent, name.Loc, "expected identifier") } if p.check(tokens.LBrace) { return p.strctDecl(name) } return p.funcDecl(name) } func (p *Parser) strctDecl(name tokens.Token) (ast.Expr, error) { if v, ok := p.env.Get(name.Value, true); ok { loc := v.Value.Loc.String() if v.Value.Loc.File == "" { loc = runtime } return nil, errors.NewError(errors.AlreadyDeclared, name.Loc, "struct", name.String(), "already declared in", loc) } p.env = p.env.Set(name.Value, *ast.NewStructType(name)) pEnv := p.env sEnv := NewEnv(pEnv) p.env = sEnv expr, err := p.strctBlock() if err != nil { return nil, err } if len(sEnv.env) == 0 { return nil, errors.NewError(errors.InvalidValue, name.Loc, "empty struct") } p.structs[name.Value] = sEnv p.env = pEnv return ast.Struct{Name: name, Body: expr.(ast.Block)}, nil } func (p *Parser) funcDecl(name tokens.Token) (ast.Expr, error) { if p.next().Id != tokens.LParen { return nil, errors.NewError(errors.ExpectedLParen, p.prev().Loc, "'(' expected in function declaration") } params := make([]ast.Var, 0, 16) paramTypes := make([]ast.Type, 0, 16) seen := map[string]tokens.Location{} for !p.check(tokens.RParen) { pname := p.next() if pname.Id != tokens.Ident { return nil, errors.NewError(errors.ExpectedIdent, pname.Loc, "expected parameter name") } typ, err := p.typ() if err != nil { return nil, err } if prev, ok := seen[pname.Value]; ok { return nil, errors.NewError(errors.AlreadyDeclared, pname.Loc, "parameter", pname.String(), "already declared in", prev.String()) } seen[pname.Value] = pname.Loc params = append(params, ast.Var{Name: pname, Type: *typ}) paramTypes = append(paramTypes, *typ) if !p.check(tokens.Comma) { break } p.next() } p.next() var ret *ast.Type if !p.check(tokens.LBrace) { if typ, err := p.typ(); err != nil { return nil, err } else { ret = typ } } if v, ok := p.env.Get(name.Value, true); ok { loc := v.Value.Loc.String() if v.Value.Loc.File == "" { loc = runtime } return nil, errors.NewError(errors.AlreadyDeclared, name.Loc, "function", name.String(), "already declared in", loc) } p.env = p.env.Set(name.Value, *ast.NewFuncType(name, paramTypes, ret)) oldFuncRet := p.currFuncRet if ret != nil { p.currFuncRet = ret } else { p.currFuncRet = &ast.TypeNone } defer func() { p.currFuncRet = oldFuncRet }() for _, v := range params { p.env = p.env.Set(v.Name.Value, v.Type) } expr, err := p.block() if err != nil { return nil, err } // return checks? return ast.Func{Name: name, Params: params, Ret: ret, Body: expr.(ast.Block)}, nil } func (p *Parser) exprList() (ast.Expr, error) { if p.check(tokens.LBracket) { p.next() vals := make([]ast.Expr, 0, 16) if !p.check(tokens.RBracket) { var prev ast.Expr for { val, err := p.expr() if err != nil { return nil, err } if prev != nil && !prev.Resolve().Compare(val.Resolve()) { return nil, errors.NewError(errors.TypeMismatch, p.prev().Loc, "type mismatch in expression list: found", val.Resolve().String(), "expected", prev.Resolve().String()) } prev = val vals = append(vals, val) if !p.check(tokens.Comma) { break } p.next() } if !p.check(tokens.RBracket) { return nil, errors.NewError(errors.ExpectedRBracket, p.prev().Loc, "']' expected to end expression list") } } p.next() if len(vals) == 0 { return nil, errors.NewError(errors.InvalidValue, p.prev().Loc, "empty expression list") } return ast.ExprList{Exprs: vals}, nil } return p.expr() } func (p *Parser) decl(con bool) (ast.Expr, error) { name := p.next() if name.Id != tokens.Ident { return nil, errors.NewError(errors.ExpectedIdent, name.Loc, "expected identifier") } typ, err := p.typ() if err != nil { return nil, err } if typ.Value.Id == tokens.TStruct { if _, ok := p.structs[typ.Value.Value]; !ok { return nil, errors.NewError(errors.RecursiveStruct, p.curr().Loc, "recursive definition of", typ.String()) } } var initv ast.Expr if p.check(tokens.Assign) { p.next() var err error initv, err = p.exprList() if err != nil { return nil, err } } if p.next().Id != tokens.Semicolon { return nil, errors.NewError(errors.ExpectedSemicolon, p.prev().Loc, "';' expected after expression") } if v, ok := p.env.Get(name.Value, true); ok { loc := v.Value.Loc.String() if v.Value.Loc.File == "" { loc = runtime } return nil, errors.NewError(errors.AlreadyDeclared, name.Loc, name.String(), "already declared in", loc) } if initv != nil { if v, ok := initv.(ast.ExprList); ok { if typ.Value.Id != tokens.TArray { return nil, errors.NewError(errors.InvalidValue, name.Loc, "expression list to initialize a type", typ.String()) } if *typ.Len != len(v.Exprs) { return nil, errors.NewError(errors.InvalidValue, name.Loc, "invalid number of elements in expression list to initialize a type", typ.String()) } if !typ.Ret.Compare(initv.Resolve()) { return nil, errors.NewError(errors.TypeMismatch, name.Loc, "type mismatch expected list of", typ.Ret.String(), "found", initv.Resolve().String()) } } else if !typ.Compare(initv.Resolve()) { return nil, errors.NewError(errors.TypeMismatch, name.Loc, "type mismatch expected", typ.String(), "found", initv.Resolve().String()) } if initv.Resolve().Value.Id == tokens.TArray || initv.Resolve().Value.Id == tokens.TStruct { if !con && initv.Resolve().ResolveConst() { return nil, errors.NewError(errors.TypeMismatch, name.Loc, "variable cannot be used as referece to constant") } typ.Ref = true } } if typ.Value.Id == tokens.TStruct { // it is an instance p.structs[name.Value] = p.structs[typ.Value.Value] typ.Inst = true } if con { if initv == nil { return nil, errors.NewError(errors.ExpectedAssign, name.Loc, "expected assignation in constant declaration") } if typ.Value.Id == tokens.TArray { // allow changing reference but not the values of the array typ.Ret.Const = true } else { typ.Const = true } } p.env = p.env.Set(name.Value, *typ) return ast.Var{Name: name, Type: *typ, Initv: initv}, nil } func (p *Parser) declaration() (ast.Expr, error) { if p.prev().Id == tokens.Const { return p.decl(true) } return p.decl(false) } func (p *Parser) continueStmt() (ast.Expr, error) { if p.next().Id != tokens.Semicolon { return nil, errors.NewError(errors.ExpectedSemicolon, p.prev().Loc, "';' expected after expression") } if !p.currLoop { return nil, errors.NewError(errors.InvalidOperation, p.curr().Loc, "continue without loop") } return ast.Continue{Loc: p.curr().Loc}, nil } func (p *Parser) breakStmt() (ast.Expr, error) { if p.next().Id != tokens.Semicolon { return nil, errors.NewError(errors.ExpectedSemicolon, p.prev().Loc, "';' expected after expression") } if !p.currLoop { return nil, errors.NewError(errors.InvalidOperation, p.curr().Loc, "break without loop") } return ast.Break{Loc: p.curr().Loc}, nil } func (p *Parser) returnStmt() (ast.Expr, error) { if p.currFuncRet == nil { return nil, errors.NewError(errors.InvalidOperation, p.prev().Loc, "return without function call") } var isError bool if p.check(tokens.TagE) { p.next() isError = true } if p.check(tokens.Semicolon) { p.next() if p.currFuncRet.Value.Id != tokens.None { return nil, errors.NewError(errors.TypeMismatch, p.prev().Loc, "type mismatch in return value, expected", p.currFuncRet.String()) } return ast.Return{Loc: p.curr().Loc, Error: isError}, nil } expr, err := p.expr() if err != nil { return nil, err } if p.next().Id != tokens.Semicolon { return nil, errors.NewError(errors.ExpectedSemicolon, p.prev().Loc, "';' expected after expression") } exprRet := expr.Resolve() if exprRet == nil { exprRet = &ast.TypeNone } else { if exprRet.Value.Id == tokens.TArray || exprRet.Value.Id == tokens.TStruct { if v, ok := expr.(ast.Variable); ok { if val, ok := p.env.Get(v.Name.Value, true); ok && !val.Ref { return nil, errors.NewError(errors.InvalidOperation, p.prev().Loc, "returning a local value of type", exprRet.String()) } } } } if !p.currFuncRet.Compare(exprRet) { return nil, errors.NewError(errors.TypeMismatch, p.prev().Loc, "type mismatch in return value, expected", p.currFuncRet.String(), "found", exprRet.String()) } return ast.Return{Value: expr, Loc: p.prev().Loc, Error: isError}, nil } func (p *Parser) forBody() (ast.Expr, error) { oldCurrLoop := p.currLoop p.currLoop = true defer func() { p.currLoop = oldCurrLoop }() return p.block() } func (p *Parser) forStmt() (ast.Expr, error) { var expr ast.Expr var err error if !p.check(tokens.LBrace) { if p.check(tokens.Ident) { ident := p.next() if p.check(tokens.In) { p.next() expr, err = p.expr() if err != nil { return nil, err } if expr.Resolve().Value.Id != tokens.TArray { return nil, errors.NewError(errors.TypeMismatch, p.next().Loc, "expected array in for-in statement found", expr.Resolve().String()) } pEnv := p.env p.env = NewEnv(pEnv) defer func() { p.env = pEnv }() p.env = p.env.Set(ident.Value, *expr.Resolve().Ret) stmts, err := p.forBody() if err != nil { return nil, err } return ast.ForIn{Name: ident, Expr: expr, Stmts: stmts}, nil } p.back() } expr, err = p.expr() if err != nil { return nil, err } } stmts, err := p.forBody() if err != nil { return nil, err } return ast.For{Cond: expr, Stmts: stmts}, nil } func (p *Parser) ifElse() (ast.Expr, error) { ifTok := p.next() expr, err := p.expr() if err != nil { return nil, err } ifBlock, err := p.block() if err != nil { return nil, err } var elseBlock ast.Expr if p.check(tokens.Else) { p.next() elseBlock, err = p.block() if err != nil { return nil, err } } else { elseBlock = ast.Literal{Value: tokens.Token{Id: tokens.None, Loc: ifTok.Loc}} } return ast.IfElse{Cond: expr, True: ifBlock, False: elseBlock}, nil } func (p *Parser) strctBlock() (ast.Expr, error) { definitions := make([]ast.Expr, 0, 256) if p.next().Id != tokens.LBrace { return nil, errors.NewError(errors.ExpectedLBrace, p.prev().Loc, "'{' expected to open block") } for !p.check(tokens.RBrace) && !p.end() { if p.check(tokens.Def) { p.next() decl, err := p.definition() if err != nil { return nil, err } definitions = append(definitions, decl) continue } if p.match([]tokens.TokenId{tokens.Var, tokens.Const}) { decl, err := p.declaration() if err != nil { return nil, err } definitions = append(definitions, decl) continue } return nil, errors.NewError(errors.ExpectedDefinition, p.next().Loc, "expected definition in a struct block") } next := p.next() if next.Id != tokens.RBrace { // MUST be EOF return nil, errors.NewError(errors.ExpectedRBrace, next.Loc, "'}' expected to close block, end of file found") } return ast.Block{Stmts: definitions}, nil } func (p *Parser) block() (ast.Expr, error) { pEnv := p.env p.env = NewEnv(pEnv) defer func() { p.env = pEnv }() statements := make([]ast.Expr, 0, 256) if p.next().Id != tokens.LBrace { return nil, errors.NewError(errors.ExpectedLBrace, p.prev().Loc, "'{' expected to open block") } for !p.check(tokens.RBrace) && !p.end() { decl, err := p.statement() if err != nil { return nil, err } statements = append(statements, decl) } next := p.next() if next.Id != tokens.RBrace { // MUST be EOF return nil, errors.NewError(errors.ExpectedRBrace, next.Loc, "'}' expected to close block, end of file found") } return ast.Block{Stmts: statements}, nil } func (p *Parser) assign() (ast.Expr, error) { expr, err := p.or() if err != nil { return nil, err } if p.check(tokens.Assign) { tok := p.next() value, err := p.expr() if err != nil { return nil, err } left := expr env := p.env if obj, ok := expr.(ast.GetExpr); ok { oType := obj.Object.Resolve() sEnv, ok := p.structs[oType.Value.Value] if !ok { // shouldn't happen return nil, errors.NewError(errors.Unexpected, oType.Value.Loc, "undeclared struct", oType.String()) } env = sEnv expr = obj.Expr } v, ok := expr.(ast.Variable) if !ok { return nil, errors.NewError(errors.InvalidTarget, tok.Loc, "invalid assignment target") } typ, ok := env.Get(v.Name.Value, false) if !ok { return nil, errors.NewError(errors.UndefinedIdent, v.Name.Loc, "undeclared variable", v.String()) } if typ.Value.Id == tokens.TArray && v.Index != nil { typ = *typ.Ret } if !typ.Compare(value.Resolve()) { return nil, errors.NewError(errors.InvalidValue, tok.Loc, "invalid value in assignation, expected", typ.String(), "found", value.Resolve().String()) } if typ.Value.Id == tokens.TArray || typ.Value.Id == tokens.TStruct { if !typ.Ref { return nil, errors.NewError(errors.TypeMismatch, v.Name.Loc, "variable cannot be converted to a reference", v.String()) } if !typ.ResolveConst() && value.Resolve().ResolveConst() { return nil, errors.NewError(errors.TypeMismatch, v.Name.Loc, "variable cannot be used as referece to constant") } } if typ.Const { return nil, errors.NewError(errors.AssignConst, v.Name.Loc, "value cannot be assigned to constant") } return ast.Assign{Left: left, Right: value, Loc: tok.Loc}, nil } return expr, nil } func (p *Parser) or() (ast.Expr, error) { expr, err := p.and() if err != nil { return nil, err } if p.check(tokens.Or) { op := p.next() right, err := p.and() if err != nil { return nil, err } expr = ast.Binary{Left: expr, Op: op, Right: right, Type: &ast.TypeBool} } return expr, nil } func (p *Parser) and() (ast.Expr, error) { expr, err := p.equality() if err != nil { return nil, err } if p.check(tokens.And) { op := p.next() right, err := p.equality() if err != nil { return nil, err } expr = ast.Binary{Left: expr, Op: op, Right: right, Type: &ast.TypeBool} } return expr, nil } func (p *Parser) equality() (ast.Expr, error) { expr, err := p.comparison() if err != nil { return nil, err } for p.match([]tokens.TokenId{tokens.Eq, tokens.Ne}) { op := p.prev() right, err := p.comparison() if err != nil { return nil, err } if !expr.Resolve().Compare(right.Resolve()) { return nil, errors.NewError(errors.TypeMismatch, op.Loc, "type mistmach expected", expr.Resolve().String(), "found", right.Resolve().String()) } expr = ast.Binary{Left: expr, Op: op, Right: right, Type: &ast.TypeBool} } return expr, nil } func (p *Parser) comparison() (ast.Expr, error) { expr, err := p.bitterm() if err != nil { return nil, err } for p.match([]tokens.TokenId{tokens.Gt, tokens.Ge, tokens.Lt, tokens.Le}) { op := p.prev() right, err := p.bitterm() if err != nil { return nil, err } if !expr.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", expr.Resolve().String()) } if !right.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", right.Resolve().String()) } expr = ast.Binary{Left: expr, Op: op, Right: right, Type: &ast.TypeBool} } return expr, nil } func (p *Parser) bitterm() (ast.Expr, error) { expr, err := p.term() if err != nil { return nil, err } for p.match([]tokens.TokenId{tokens.BitShl, tokens.BitShr, tokens.BitOr, tokens.BitXor}) { op := p.prev() right, err := p.term() if err != nil { return nil, err } if !expr.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", expr.Resolve().String()) } if !right.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", right.Resolve().String()) } expr = ast.Binary{Left: expr, Op: op, Right: right} } return expr, nil } func (p *Parser) term() (ast.Expr, error) { expr, err := p.bitfactor() if err != nil { return nil, err } for p.match([]tokens.TokenId{tokens.Sub, tokens.Add}) { op := p.prev() right, err := p.bitfactor() if err != nil { return nil, err } if !expr.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", expr.Resolve().String()) } if !right.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", right.Resolve().String()) } expr = ast.Binary{Left: expr, Op: op, Right: right} } return expr, nil } func (p *Parser) bitfactor() (ast.Expr, error) { expr, err := p.factor() if err != nil { return nil, err } if p.check(tokens.BitAnd) { op := p.next() right, err := p.factor() if err != nil { return nil, err } if !expr.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", expr.Resolve().String()) } if !right.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", right.Resolve().String()) } expr = ast.Binary{Left: expr, Op: op, Right: right} } return expr, nil } func (p *Parser) factor() (ast.Expr, error) { expr, err := p.unary() if err != nil { return nil, err } for p.match([]tokens.TokenId{tokens.Div, tokens.Mul, tokens.Mod}) { op := p.prev() right, err := p.unary() if err != nil { return nil, err } if !expr.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", expr.Resolve().String()) } if !right.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", right.Resolve().String()) } expr = ast.Binary{Left: expr, Op: op, Right: right} } return expr, nil } func (p *Parser) unary() (ast.Expr, error) { if p.match([]tokens.TokenId{tokens.TestE, tokens.Not, tokens.Neg, tokens.Sub}) { op := p.prev() right, err := p.unary() if err != nil { return nil, err } if (op.Id == tokens.Sub || op.Id == tokens.Neg) && right.Resolve().Value.Id != tokens.TNumber { return nil, errors.NewError(errors.InvalidOperation, op.Loc, "invalid operation for type", right.Resolve().String()) } return ast.Unary{Op: op, Right: right}, nil } return p.call() } func (p *Parser) call() (ast.Expr, error) { expr, err := p.primary() if err != nil { return nil, err } for { if p.check(tokens.LBracket) { loc := p.next().Loc if expr.Resolve().Value.Id != tokens.TArray { return nil, errors.NewError(errors.NotIndexable, loc, "value is not indexable") } index, err := p.expr() if err != nil { return nil, err } if !index.Resolve().Compare(&ast.TypeNumber) { return nil, errors.NewError(errors.InvalidValue, p.prev().Loc, "invalid index value") } if p.next().Id != tokens.RBracket { return nil, errors.NewError(errors.ExpectedRBracket, p.prev().Loc, "']' expected to close index") } switch v := expr.(type) { case ast.Variable: v.Index = index expr = v case ast.GetExpr: val := v.Expr.(ast.Variable) val.Index = index v.Expr = val expr = v default: // shouldn't happen return nil, errors.NewError(errors.Unexpected, loc, "value is not indexable") } continue } if p.check(tokens.Dot) { loc := p.next().Loc prop := p.next() if prop.Id != tokens.Ident { return nil, errors.NewError(errors.ExpectedIdent, prop.Loc, "expected identifier") } sType := expr.Resolve() if sType.Value.Id != tokens.TStruct { return nil, errors.NewError(errors.NotStruct, loc, "property get on a non struct value") } sEnv, ok := p.structs[sType.Value.Value] if !ok { // shouldn't happen return nil, errors.NewError(errors.Unexpected, sType.Value.Loc, "undeclared struct", sType.String()) } pType, ok := sEnv.Get(prop.Value, true) if !ok { return nil, errors.NewError(errors.UndefinedIdent, loc, "property", prop.String(), "not defined in", sType.String()) } expr = ast.GetExpr{Object: expr, Expr: ast.Variable{Name: prop, Type: pType}} continue } if p.check(tokens.LParen) { p.next() args := make([]ast.Expr, 0, 16) if !p.check(tokens.RParen) { for { arg, err := p.expr() if err != nil { return nil, err } args = append(args, arg) if !p.check(tokens.Comma) { break } p.next() } if !p.check(tokens.RParen) { return nil, errors.NewError(errors.ExpectedRParen, p.prev().Loc, "')' expected to end function call") } } typ := expr.Resolve() if typ.Value.Id != tokens.TFunc { return nil, errors.NewError(errors.NotCallable, p.next().Loc, "value is not callable") } // mostly for println that doesn't have type checks // and will consume any number of arguments if typ.Params != nil { if len(typ.Params) != len(args) { return nil, errors.NewError(errors.CallError, p.next().Loc, fmt.Sprintf("invalid number of arguments in function call: %d expected %d found", len(typ.Params), len(args))) } for n, arg := range args { if !typ.Params[n].Compare(arg.Resolve()) { return nil, errors.NewError(errors.TypeMismatch, p.next().Loc, fmt.Sprintf("type mismatch in function call argument %d: expected %s found %s", n+1, typ.Params[n].String(), arg.Resolve().String())) } } } expr = ast.Call{Callee: expr, Loc: p.next().Loc, Args: args} continue } break } return expr, nil } func (p *Parser) primary() (ast.Expr, error) { if p.match([]tokens.TokenId{tokens.True, tokens.False}) { return ast.Literal{Value: p.prev(), Type: ast.TypeBool}, nil } else if p.check(tokens.String) { return ast.Literal{Value: p.next(), Type: ast.TypeString}, nil } else if p.check(tokens.Ident) { value := p.next() typc, ok := p.env.Get(value.Value, false) if !ok { return nil, errors.NewError(errors.UndefinedIdent, value.Loc, "undeclared variable", value.String()) } if typc.Value.Id == tokens.TStruct && !typc.Inst { return nil, errors.NewError(errors.InvalidValue, value.Loc, value.String(), "is not an instance") } return ast.Variable{Name: value, Type: typc}, nil } else if p.check(tokens.Char) { value := p.next() value.Id = tokens.Number nval := byte(value.Value[0]) return ast.Literal{Value: value, Numval: ast.Number(nval), Type: ast.TypeNumber}, nil } else if p.check(tokens.Number) { value := p.next() nval, err := strconv.ParseInt(value.Value, 0, 64) if err != nil { return nil, errors.Error{ Code: errors.FailedNumeric, Message: fmt.Sprintf("[%s] failed to parse numeric value", value.Loc), Err: err, } } return ast.Literal{Value: value, Numval: ast.Number(nval), Type: ast.TypeNumber}, nil } else if p.check(tokens.LParen) { p.next() expr, err := p.expr() if err != nil { return nil, err } next := p.next() if next.Id != tokens.RParen { found := fmt.Sprintf("'%v'", next.Value) if next.Id == tokens.Eof { found = "end of file" } return nil, errors.NewError(errors.ExpectedRParen, next.Loc, "')' expected after expression,", found, "found") } return ast.Group{Expr: expr}, nil } return nil, errors.NewError(errors.ExpectedExpr, p.curr().Loc, "expression expected") }