Esempio n. 1
0
func binaryOpConst(b *builder, op string, A, B *ref) *ref {
	va, _ := types.NumConst(A.Type())
	vb, _ := types.NumConst(B.Type())

	br := func(b bool) *ref {
		if b {
			return refTrue
		}
		return refFalse
	}
	switch op {
	case "==":
		return br(va == vb)
	case "!=":
		return br(va != vb)
	case ">":
		return br(va > vb)
	case "<":
		return br(va < vb)
	case ">=":
		return br(va >= vb)
	case "<=":
		return br(va <= vb)
	}
	panic("bug")
}
Esempio n. 2
0
func opAssign(b *builder, dest, src tast.Expr, op *lex8.Token) tast.Stmt {
	destRef := dest.R()
	srcRef := src.R()
	if !destRef.IsSingle() || !srcRef.IsSingle() {
		b.Errorf(op.Pos, "%s %s %s", destRef, op.Lit, srcRef)
		return nil
	} else if !destRef.Addressable {
		b.Errorf(op.Pos, "assign to non-addressable")
		return nil
	}

	opLit := parseAssignOp(op.Lit)
	destType := destRef.Type()
	srcType := srcRef.Type()

	if opLit == ">>" || opLit == "<<" {
		if v, ok := types.NumConst(srcType); ok {
			src = constCast(b, op.Pos, v, src, types.Uint)
			if src == nil {
				return nil
			}
			srcRef = src.R()
			srcType = types.Uint
		}

		if !canShift(b, destType, srcType, op.Pos, opLit) {
			return nil
		}
		return &tast.AssignStmt{dest, op, src}
	}

	if v, ok := types.NumConst(srcType); ok {
		src = constCast(b, op.Pos, v, src, destType)
		if src == nil {
			return nil
		}
		srcRef = src.R()
		srcType = destType
	}

	if ok, t := types.SameBasic(destType, srcType); ok {
		switch t {
		case types.Int, types.Int8, types.Uint, types.Uint8:
			return &tast.AssignStmt{dest, op, src}
		}
	}

	b.Errorf(op.Pos, "invalid %s %s %s", destType, opLit, srcType)
	return nil
}
Esempio n. 3
0
File: define.go Progetto: Xslxy/e8vm
// allocPrepare checks if the provided types are all allocable, and insert
// implicit type casts if needed. Only literay expression list needs alloc
// prepare.
func allocPrepare(
	b *builder, toks []*lex8.Token, lst *tast.ExprList,
) *tast.ExprList {
	ret := tast.NewExprList()
	for i, tok := range toks {
		e := lst.Exprs[i]
		t := e.Type()
		if types.IsNil(t) {
			b.Errorf(tok.Pos, "cannot infer type from nil for %q", tok.Lit)
			return nil
		}
		if v, ok := types.NumConst(t); ok {
			e = constCastInt(b, tok.Pos, v, e)
			if e == nil {
				return nil
			}
		}
		if !types.IsAllocable(t) {
			b.Errorf(tok.Pos, "cannot allocate for %s", t)
			return nil
		}
		ret.Append(e)
	}
	return ret
}
Esempio n. 4
0
func unaryOpConst(b *builder, opTok *lex8.Token, B tast.Expr) tast.Expr {
	op := opTok.Lit
	bref := B.R()
	if !bref.IsSingle() {
		b.Errorf(opTok.Pos, "invalid operation: %q on %s", op, bref)
		return nil
	}

	v, ok := types.NumConst(bref.T)
	if !ok {
		// TODO: support type const
		b.Errorf(opTok.Pos, "typed const operation not implemented")
		return nil
	}

	switch op {
	case "+":
		return B // just shortcut this
	case "-":
		return &tast.Const{tast.NewRef(types.NewNumber(-v))}
	}

	b.Errorf(opTok.Pos, "invalid operation: %q on %s", op, B)
	return nil
}
Esempio n. 5
0
func buildCast(b *builder, expr *ast.CallExpr, t types.T) tast.Expr {
	pos := expr.Lparen.Pos

	args := buildExprList(b, expr.Args)
	if args == nil {
		return nil
	}

	ref := args.R()
	if !ref.IsSingle() {
		b.Errorf(pos, "cannot convert %s to %s", ref, t)
		return nil
	}

	srcType := ref.T
	if c, ok := srcType.(*types.Const); ok {
		if v, ok := types.NumConst(srcType); ok && types.IsInteger(t) {
			return constCast(b, pos, v, args, t)
		}
		srcType = c.Type // using the underlying type
	}

	if types.IsInteger(t) && types.IsInteger(srcType) {
		return tast.NewCast(args, t)
	}
	if regSizeCastable(t, srcType) {
		return tast.NewCast(args, t)
	}

	b.Errorf(pos, "cannot convert from %s to %s", srcType, t)
	return nil
}
Esempio n. 6
0
func buildConst(b *builder, c *tast.Const) *ref {
	if _, ok := types.NumConst(c.T); ok {
		// untyped consts are just types.
		return newRef(c.T, nil)
	}

	if t, ok := c.T.(types.Basic); ok {
		v := c.ConstValue.(int64)
		switch t {
		case types.Int, types.Uint:
			return newRef(c.T, ir.Num(uint32(v)))
		case types.Int8, types.Uint8, types.Bool:
			return newRef(c.T, ir.Byt(uint8(v)))
		default:
			panic("other basic types not supported yet")
		}
	}

	if c.T == types.String {
		s := c.ConstValue.(string)
		ret := b.newTemp(c.T)
		b.b.Arith(ret.IR(), nil, "makeStr", b.p.NewString(s))
		return ret
	}

	panic("other const types not supported")
}
Esempio n. 7
0
func checkArrayIndex(b *builder, index tast.Expr, pos *lex8.Pos) tast.Expr {
	t := index.R().T
	if v, ok := types.NumConst(t); ok {
		if v < 0 {
			b.Errorf(pos, "array index is negative: %d", v)
			return nil
		}
		return constCastInt(b, pos, v, index)
	}
	if !types.IsInteger(t) {
		b.Errorf(pos, "index must be an integer")
		return nil
	}
	return index
}
Esempio n. 8
0
func assign(b *builder, dest, src tast.Expr, op *lex8.Token) tast.Stmt {
	destRef := dest.R()
	srcRef := src.R()

	ndest := destRef.Len()
	nsrc := srcRef.Len()
	if ndest != nsrc {
		b.Errorf(op.Pos, "cannot assign %s to %s", nsrc, ndest)
		return nil
	}

	for i := 0; i < ndest; i++ {
		r := destRef.At(i)
		if !r.Addressable {
			b.Errorf(op.Pos, "assigning to non-addressable")
			return nil
		}

		destType := r.Type()
		srcType := srcRef.At(i).Type()
		if !types.CanAssign(destType, srcType) {
			b.Errorf(op.Pos, "cannot assign %s to %s", srcType, destType)
			return nil
		}
	}

	// insert casting if needed
	if srcList, ok := tast.MakeExprList(src); ok {
		newList := tast.NewExprList()
		for i, e := range srcList.Exprs {
			t := e.Type()
			if types.IsNil(t) {
				e = tast.NewCast(e, destRef.At(i).Type())
			} else if v, ok := types.NumConst(t); ok {
				e = constCast(b, nil, v, e, destRef.At(i).Type())
				if e == nil {
					panic("bug")
				}
			}
			newList.Append(e)
		}
		src = newList
	}

	return &tast.AssignStmt{dest, op, src}
}
Esempio n. 9
0
func varDeclPrepare(
	b *builder, toks []*lex8.Token, lst *tast.ExprList, t types.T,
) *tast.ExprList {
	ret := tast.NewExprList()
	for i, tok := range toks {
		e := lst.Exprs[i]
		etype := e.Type()
		if types.IsNil(etype) {
			e = tast.NewCast(e, t)
		} else if v, ok := types.NumConst(etype); ok {
			e = constCast(b, tok.Pos, v, e, t)
			if e == nil {
				return nil
			}
		}
		ret.Append(e)
	}
	return ret
}
Esempio n. 10
0
func buildArrayType(b *builder, expr *ast.ArrayTypeExpr) types.T {
	t := buildType(b, expr.Type)
	if t == nil {
		return nil
	}

	if expr.Len == nil {
		// slice
		return &types.Slice{t}
	}

	// array
	n := b.buildConstExpr(expr.Len)
	if n == nil {
		return nil
	}

	ntype := n.R().T
	c, ok := ntype.(*types.Const)
	if !ok {
		// might be true, false, or other builtin consts
		b.Errorf(ast.ExprPos(expr), "array index is not a constant")
		return nil
	}

	if v, ok := types.NumConst(ntype); ok {
		if v < 0 {
			b.Errorf(ast.ExprPos(expr),
				"array index is negative: %d", c.Value,
			)
			return nil
		} else if !types.InRange(v, types.Int) {
			b.Errorf(ast.ExprPos(expr), "index out of range of int32")
			return nil
		}
		return &types.Array{T: t, N: int32(v)}
	}

	// TODO: support typed const
	b.Errorf(ast.ExprPos(expr), "typed const not implemented yet")
	return nil
}
Esempio n. 11
0
func buildConst(b *builder, c *tast.Const) *ref {
	if _, ok := types.NumConst(c.T); ok {
		// untyped consts are just types.
		return newRef(c.T, nil)
	}

	if t, ok := c.T.(types.Basic); ok {
		v := c.ConstValue.(int64)
		return newRef(c.T, constNumIr(v, t))
	}

	if c.T == types.String {
		s := c.ConstValue.(string)
		ret := b.newTemp(c.T)
		b.b.Arith(ret.IR(), nil, "makeStr", b.p.NewString(s))
		return ret
	}

	if t, ok := c.T.(*types.Slice); ok {
		if bt, ok := t.T.(types.Basic); ok {
			switch bt {
			case types.Int, types.Uint, types.Int8, types.Uint8, types.Bool:
				bs := c.ConstValue.([]byte)
				ret := b.newTemp(t)
				ref := b.p.NewHeapDat(bs, bt.Size(), bt.RegSizeAlign())
				b.b.Arith(ret.IR(), nil, "makeDat", ref)
				return ret
			default:
				panic("other const slices not supported")
			}
		} else {
			panic("not basic type")
		}
	}

	panic("other const types not supported")
}
Esempio n. 12
0
func buildCast(b *builder, from *ref, t types.T) *ref {
	srcType := from.Type()
	ret := b.newTemp(t)

	if types.IsNil(srcType) {
		size := t.Size()
		if size == arch8.RegSize {
			return newRef(t, ir.Num(0))
		}
		if _, ok := t.(*types.Slice); !ok {
			panic("bug")
		}
		ret := b.newTemp(t)
		b.b.Zero(ret.IR())
		return ret
	}

	if c, ok := srcType.(*types.Const); ok {
		if v, ok := types.NumConst(srcType); ok && types.IsInteger(t) {
			return newRef(t, constNumIr(v, t))
		}
		// TODO: we do not support typed const right?
		// so why need this line?
		srcType = c.Type // using the underlying type
	}

	if types.IsInteger(t) && types.IsInteger(srcType) {
		b.b.Arith(ret.IR(), nil, "cast", from.IR())
		return ret
	}
	if regSizeCastable(t, srcType) {
		b.b.Arith(ret.IR(), nil, "", from.IR())
		return ret
	}
	panic("bug")
}
Esempio n. 13
0
func buildReturnStmt(b *builder, stmt *ast.ReturnStmt) tast.Stmt {
	pos := stmt.Kw.Pos
	if stmt.Exprs == nil {
		if b.retType == nil || b.retNamed {
			return &tast.ReturnStmt{}
		}
		b.Errorf(pos, "expects return %s", fmt8.Join(b.retType, ","))
		return nil
	}

	if b.retType == nil {
		b.Errorf(pos, "function expects no return value")
		return nil
	}

	src := b.buildExpr(stmt.Exprs)
	if src == nil {
		return nil
	}

	srcRef := src.R()
	nret := len(b.retType)
	nsrc := srcRef.Len()
	if nret != nsrc {
		b.Errorf(pos, "expect (%s), returning (%s)",
			fmt8.Join(b.retType, ","), srcRef,
		)
		return nil
	}

	for i := 0; i < nret; i++ {
		t := b.retType[i]
		srcType := srcRef.At(i).Type()
		if !types.CanAssign(t, srcType) {
			b.Errorf(pos, "expect (%s), returning (%s)",
				fmt8.Join(b.retType, ","), srcRef,
			)
			return nil
		}
	}

	// insert implicit type casts
	if srcList, ok := tast.MakeExprList(src); ok {
		newList := tast.NewExprList()
		for i, e := range srcList.Exprs {
			t := e.Type()
			if types.IsNil(t) {
				e = tast.NewCast(e, b.retType[i])
			} else if v, ok := types.NumConst(t); ok {
				e = constCast(b, nil, v, e, b.retType[i])
				if e == nil {
					panic("bug")
				}
			}
			newList.Append(e)
		}
		src = newList
	}

	return &tast.ReturnStmt{src}
}
Esempio n. 14
0
func binaryOpConst(b *builder, opTok *lex8.Token, A, B tast.Expr) tast.Expr {
	op := opTok.Lit
	aref := A.R()
	bref := B.R()
	if aref.List != nil || bref.List != nil {
		b.Errorf(opTok.Pos, "invalid %s %q %s", aref.T, op, bref.T)
		return nil
	}

	va, oka := types.NumConst(aref.T)
	vb, okb := types.NumConst(bref.T)
	if !(oka && okb) {
		b.Errorf(opTok.Pos, "non-numeric consts ops not implemented")
		return nil
	}

	r := func(v int64) tast.Expr {
		return &tast.Const{tast.NewRef(types.NewNumber(v))}
	}

	switch op {
	case "+":
		return r(va + vb)
	case "-":
		return r(va - vb)
	case "*":
		return r(va * vb)
	case "&":
		return r(va & vb)
	case "|":
		return r(va | vb)
	case "^":
		return r(va ^ vb)
	case "%":
		if vb == 0 {
			b.Errorf(opTok.Pos, "modular by zero")
			return nil
		}
		return r(va % vb)
	case "/":
		if vb == 0 {
			b.Errorf(opTok.Pos, "divide by zero")
			return nil
		}
		return r(va / vb)
	case "==", "!=", ">", "<", ">=", "<=":
		return &tast.OpExpr{A, opTok, B, tast.NewRef(types.Bool)}
	case "<<":
		if vb < 0 {
			b.Errorf(opTok.Pos, "shift with negative value", vb)
			return nil
		}
		return r(va << uint64(vb))
	case ">>":
		if vb < 0 {
			b.Errorf(opTok.Pos, "shift with negative value", vb)
			return nil
		}
		return r(va >> uint64(vb))
	}

	b.Errorf(opTok.Pos, "%q on consts", op)
	return nil
}
Esempio n. 15
0
func buildCallExpr(b *builder, expr *ast.CallExpr) tast.Expr {
	f := b.buildExpr(expr.Func)
	if f == nil {
		return nil
	}

	pos := ast.ExprPos(expr.Func)
	fref := f.R()
	if !fref.IsSingle() {
		b.Errorf(pos, "%s is not callable", fref)
		return nil
	}
	if t, ok := fref.T.(*types.Type); ok {
		return buildCast(b, expr, t.T)
	}

	builtin, ok := fref.T.(*types.BuiltInFunc)
	if ok {
		switch builtin.Name {
		case "len":
			return buildCallLen(b, expr, f)
		case "make":
			return buildCallMake(b, expr, f)
		}
		b.Errorf(pos, "builtin %s() not implemented", builtin.Name)
		return nil
	}

	funcType, ok := fref.T.(*types.Func)
	if !ok {
		b.Errorf(pos, "function call on non-callable: %s", fref)
		return nil
	}

	args := buildExprList(b, expr.Args)
	if args == nil {
		return nil
	}

	argsRef := args.R()
	nargs := argsRef.Len()
	if nargs != len(funcType.Args) {
		b.Errorf(ast.ExprPos(expr), "argument expects (%s), got (%s)",
			fmt8.Join(funcType.Args, ","), args,
		)
		return nil
	}

	// type check on each argument
	for i := 0; i < nargs; i++ {
		argType := argsRef.At(i).Type()
		expect := funcType.Args[i].T
		if !types.CanAssign(expect, argType) {
			pos := ast.ExprPos(expr.Args.Exprs[i])
			b.Errorf(pos, "argument %d expects %s, got %s",
				i+1, expect, argType,
			)
			return nil
		}
	}

	// insert casting when it is a literal expression list.
	callArgs, ok := tast.MakeExprList(args)
	if ok {
		castedArgs := tast.NewExprList()
		for i := 0; i < nargs; i++ {
			argExpr := callArgs.Exprs[i]
			argType := argExpr.Type()
			expect := funcType.Args[i].T

			// insert auto casts for consts
			if types.IsNil(argType) {
				castedArgs.Append(tast.NewCast(argExpr, expect))
			} else if _, ok := types.NumConst(argType); ok {
				castedArgs.Append(tast.NewCast(argExpr, expect))
			} else {
				castedArgs.Append(argExpr)
			}
		}
		args = castedArgs
	}

	retRef := tast.Void
	for _, t := range funcType.RetTypes {
		retRef = tast.AppendRef(retRef, tast.NewRef(t))
	}

	return &tast.CallExpr{f, args, retRef}
}
Esempio n. 16
0
File: op.go Progetto: 272489711/e8vm
func buildBinaryOpExpr(b *builder, expr *ast.OpExpr) tast.Expr {
	opTok := expr.Op
	op := opTok.Lit
	opPos := opTok.Pos

	A := b.buildExpr(expr.A)
	if A == nil {
		return nil
	}
	aref := A.R()
	if !aref.IsSingle() {
		b.Errorf(opPos, "%q on %s", op, aref)
		return nil
	}
	atyp := aref.T

	B := b.buildExpr(expr.B)
	if B == nil {
		return nil
	}
	bref := B.R()
	if !bref.IsSingle() {
		b.Errorf(opPos, "%q on %s", op, bref)
		return nil
	}
	btyp := bref.T

	if types.IsConst(atyp) && types.IsConst(btyp) {
		return binaryOpConst(b, opTok, A, B)
	}

	if op == ">>" || op == "<<" {
		if v, ok := types.NumConst(btyp); ok {
			B = constCast(b, opPos, v, B, types.Uint)
			if B == nil {
				return nil
			}
			btyp = types.Uint
		}

		if v, ok := types.NumConst(atyp); ok {
			A = constCast(b, opPos, v, A, types.Int)
			if A == nil {
				return nil
			}
			atyp = types.Int
		}

		if !canShift(b, atyp, btyp, opPos, op) {
			return nil
		}

		r := tast.NewRef(atyp)
		return &tast.OpExpr{A, opTok, B, r}
	}

	if v, ok := types.NumConst(atyp); ok {
		A = constCast(b, opPos, v, A, btyp)
		if A == nil {
			return nil
		}
		atyp = btyp
	} else if c, ok := atyp.(*types.Const); ok {
		atyp = c.Type
	}

	if v, ok := types.NumConst(btyp); ok {
		B = constCast(b, opPos, v, B, atyp)
		if B == nil {
			return nil
		}
		btyp = atyp
	} else if c, ok := btyp.(*types.Const); ok {
		btyp = c.Type
	}

	if ok, t := types.SameBasic(atyp, btyp); ok {
		switch t {
		case types.Int, types.Int8, types.Uint, types.Uint8:
			return binaryOpInt(b, opTok, A, B, t)
		case types.Bool:
			return binaryOpBool(b, opTok, A, B)
		case types.Float32:
			b.Errorf(opPos, "floating point operations not implemented")
			return nil
		}
	}

	if types.IsNil(atyp) && types.IsNil(btyp) {
		return binaryOpNil(b, opTok, A, B)
	} else if types.BothPointer(atyp, btyp) {
		return binaryOpPtr(b, opTok, A, B)
	} else if types.BothFuncPointer(atyp, btyp) {
		return binaryOpPtr(b, opTok, A, B)
	} else if types.BothSlice(atyp, btyp) {
		return binaryOpSlice(b, opTok, A, B)
	}

	b.Errorf(opPos, "invalid operation of %s %s %s", atyp, op, btyp)
	if types.IsInteger(atyp) && types.IsInteger(btyp) {
		switch op {
		case "+", "-", "*", "&", "|", "^", "%", "/",
			"==", "!=", ">", "<", ">=", "<=":
			b.Errorf(
				opPos,
				"operation %s needs the same type on both sides",
				op,
			)
		}
	}
	return nil
}
Esempio n. 17
0
func buildCallMake(b *builder, expr *ast.CallExpr, f tast.Expr) tast.Expr {
	args := buildExprList(b, expr.Args)
	if args == nil {
		return nil
	}

	n := args.R().Len()
	if n == 0 {
		b.Errorf(expr.Lparen.Pos, "make() takes at least one argument")
		return nil
	}

	argsList, ok := tast.MakeExprList(args)
	if !ok {
		b.Errorf(expr.Lparen.Pos, "make() only takes a literal list")
		return nil
	}

	arg0 := argsList.Exprs[0]
	t, ok := arg0.R().T.(*types.Type)
	if !ok {
		b.Errorf(expr.Lparen.Pos, "make() takes a type as the 1st argument")
		return nil
	}
	switch st := t.T.(type) {
	case *types.Slice:
		if n != 3 {
			b.Errorf(expr.Lparen.Pos, "make() slice takes 3 arguments")
			return nil
		}

		size := argsList.Exprs[1]
		pos := ast.ExprPos(expr.Args.Exprs[1])
		size = checkArrayIndex(b, size, pos)
		if size == nil {
			return nil
		}

		start := argsList.Exprs[2]
		startType := start.R().T
		startPos := ast.ExprPos(expr.Args.Exprs[2])
		if v, ok := types.NumConst(startType); ok {
			start = constCastUint(b, startPos, v, start)
			if start == nil {
				return nil
			}
		} else if !types.IsBasic(startType, types.Uint) {
			pt := types.PointerOf(startType)
			if pt == nil || !types.SameType(pt, st.T) {
				b.Errorf(startPos,
					"make() takes an uint or a typed pointer as the 3rd arg",
				)
				return nil
			}
		}

		callArgs := tast.NewExprList()
		callArgs.Append(arg0)
		callArgs.Append(size)
		callArgs.Append(start)

		r := tast.NewRef(st)
		return &tast.CallExpr{f, callArgs, r}
	}

	b.Errorf(expr.Lparen.Pos, "cannot make() type %s", t.T)
	return nil
}
Esempio n. 18
0
func buildArrayLit(b *builder, lit *ast.ArrayLiteral) tast.Expr {
	hold := b.lhsSwap(false)
	defer b.lhsRestore(hold)

	if lit.Type.Len != nil {
		b.Errorf(
			ast.ExprPos(lit),
			"array literal with length not supported yet",
		)
		return nil
	}

	buf := new(bytes.Buffer)
	t := buildType(b, lit.Type.Type)
	if t == nil {
		return nil
	}

	if !types.IsInteger(t) {
		pos := ast.ExprPos(lit.Type.Type)
		b.Errorf(pos, "array literal must be integer type")
		return nil
	}
	bt := t.(types.Basic)

	if lit.Exprs != nil {
		for _, expr := range lit.Exprs.Exprs {
			n := b.buildConstExpr(expr)
			if n == nil {
				return nil
			}
			ntype := n.R().T
			if _, ok := ntype.(*types.Const); !ok {
				b.Errorf(ast.ExprPos(expr), "array literal not a constant")
				return nil
			}

			if v, ok := types.NumConst(ntype); ok {
				if !types.InRange(v, t) {
					b.Errorf(
						ast.ExprPos(expr),
						"constant out of range of %s",
						t,
					)
					return nil
				}

				switch bt {
				case types.Int, types.Uint:
					var bs [4]byte
					binary.LittleEndian.PutUint32(bs[:], uint32(v))
					buf.Write(bs[:])
				case types.Int8, types.Uint8:
					buf.Write([]byte{byte(v)})
				default:
					panic("not integer")
				}
			}
		}
	}

	ref := tast.NewConstRef(&types.Slice{bt}, buf.Bytes())
	return &tast.Const{ref}
}