| // Copyright 2017 The Wuffs Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package cgen |
| |
| import ( |
| "fmt" |
| "math/big" |
| "strings" |
| |
| a "github.com/google/wuffs/lang/ast" |
| t "github.com/google/wuffs/lang/token" |
| ) |
| |
| func (g *gen) writeExpr(b *buffer, n *a.Expr, sideEffectsOnly bool, depth uint32) error { |
| if depth > a.MaxExprDepth { |
| return fmt.Errorf("expression recursion depth too large") |
| } |
| depth++ |
| |
| if cv := n.ConstValue(); cv != nil { |
| if typ := n.MType(); typ.IsNumTypeOrIdeal() { |
| b.writes(cv.String()) |
| if cv.Cmp(maxInt64) > 0 { |
| b.writeb('u') |
| } |
| } else if typ.IsNullptr() { |
| b.writes("NULL") |
| } else if typ.IsStatus() { |
| b.writes("wuffs_base__make_status(NULL)") |
| } else if !typ.IsBool() { |
| return fmt.Errorf("cannot generate C expression for %v constant of type %q", n.Str(g.tm), n.MType().Str(g.tm)) |
| } else if cv.Cmp(zero) == 0 { |
| b.writes("false") |
| } else if cv.Cmp(one) == 0 { |
| b.writes("true") |
| } else { |
| return fmt.Errorf("%v has type bool but constant value %v is neither 0 or 1", n.Str(g.tm), cv) |
| } |
| return nil |
| } |
| |
| switch op := n.Operator(); { |
| case op.IsXUnaryOp(): |
| return g.writeExprUnaryOp(b, n, depth) |
| case op.IsXBinaryOp(): |
| return g.writeExprBinaryOp(b, n, depth) |
| case op.IsXAssociativeOp(): |
| return g.writeExprAssociativeOp(b, n, depth) |
| } |
| return g.writeExprOther(b, n, sideEffectsOnly, depth) |
| } |
| |
| func (g *gen) writeExprOther(b *buffer, n *a.Expr, sideEffectsOnly bool, depth uint32) error { |
| switch n.Operator() { |
| case 0: |
| if ident := n.Ident(); ident == t.IDThis { |
| b.writes("self") |
| |
| } else if ident == t.IDCoroutineResumed { |
| if g.currFunk.astFunc.Effect().Coroutine() { |
| // TODO: don't hard-code [0], and allow recursive coroutines. |
| b.printf("(self->private_impl.%s%s[0] != 0)", |
| pPrefix, g.currFunk.astFunc.FuncName().Str(g.tm)) |
| } else { |
| b.writes("false") |
| } |
| |
| } else if ident.IsDQStrLiteral(g.tm) { |
| if z := g.statusMap[t.QID{0, n.Ident()}]; z.cName != "" { |
| b.writes("wuffs_base__make_status(") |
| b.writes(z.cName) |
| b.writes(")") |
| return nil |
| } |
| return fmt.Errorf("unrecognized status %s", n.Str(g.tm)) |
| |
| } else if c, ok := g.scalarConstsMap[t.QID{0, n.Ident()}]; ok { |
| b.writes(c.Value().ConstValue().String()) |
| |
| } else { |
| if n.GlobalIdent() { |
| b.writes(g.PKGPREFIX) |
| } else { |
| b.writes(vPrefix) |
| } |
| b.writes(ident.Str(g.tm)) |
| } |
| return nil |
| |
| case t.IDOpenParen: |
| // n is a function call. |
| if err := g.writeBuiltinCall(b, n, sideEffectsOnly, depth); err != errNoSuchBuiltin { |
| return err |
| } |
| |
| if n.LHS().AsExpr().Ident() == t.IDReset { |
| method := n.LHS().AsExpr() |
| recv := method.LHS().AsExpr() |
| recvTyp, addr := recv.MType(), "&" |
| if p := recvTyp.Decorator(); p == t.IDNptr || p == t.IDPtr { |
| recvTyp, addr = recvTyp.Inner(), "" |
| } |
| if recvTyp.Decorator() != 0 { |
| return fmt.Errorf("cannot generate reset method call %q for receiver type %q", |
| n.Str(g.tm), recv.MType().Str(g.tm)) |
| } |
| qid := recvTyp.QID() |
| |
| b.printf("wuffs_base__ignore_status("+ |
| "%s%s__initialize(%s", g.packagePrefix(qid), qid[1].Str(g.tm), addr) |
| if err := g.writeExpr(b, recv, false, depth); err != nil { |
| return err |
| } |
| b.printf(", sizeof (%s%s), WUFFS_VERSION, 0))", g.packagePrefix(qid), qid[1].Str(g.tm)) |
| |
| return nil |
| } |
| |
| return g.writeExprUserDefinedCall(b, n, depth) |
| |
| case t.IDOpenBracket: |
| // n is an index. |
| if err := g.writeExpr(b, n.LHS().AsExpr(), false, depth); err != nil { |
| return err |
| } |
| if lTyp := n.LHS().AsExpr().MType(); lTyp.IsSliceType() { |
| // TODO: don't assume that the slice is a slice of base.u8. |
| b.writes(".ptr") |
| } |
| b.writeb('[') |
| if err := g.writeExpr(b, n.RHS().AsExpr(), false, depth); err != nil { |
| return err |
| } |
| b.writeb(']') |
| return nil |
| |
| case t.IDDotDot: |
| // n is a slice. |
| lhs := n.LHS().AsExpr() |
| mhs := n.MHS().AsExpr() |
| rhs := n.RHS().AsExpr() |
| |
| mcv := (*big.Int)(nil) |
| rcv := (*big.Int)(nil) |
| lhsIsArray := lhs.MType().IsArrayType() |
| if lhsIsArray { |
| if (mhs != nil) && (mhs.ConstValue() != nil) { |
| mcv = mhs.ConstValue() |
| mhs = nil |
| } |
| if (rhs != nil) && (rhs.ConstValue() != nil) { |
| rcv = rhs.ConstValue() |
| rhs = nil |
| } |
| } |
| |
| switch { |
| case mhs != nil && rhs == nil: |
| b.writes("wuffs_base__slice_u8__subslice_i(") |
| case mhs == nil && rhs != nil: |
| b.writes("wuffs_base__slice_u8__subslice_j(") |
| case mhs != nil && rhs != nil: |
| b.writes("wuffs_base__slice_u8__subslice_ij(") |
| } |
| |
| comma := ", " |
| if (mhs != nil) && (mhs.Operator() != 0) && |
| (rhs != nil) && (rhs.Operator() != 0) { |
| comma = ",\n" |
| } |
| |
| if lhsIsArray { |
| // TODO: don't assume that the slice is a slice of base.u8. |
| b.writes("wuffs_base__make_slice_u8(") |
| if mcv != nil { |
| b.writeb('(') |
| } |
| } |
| if err := g.writeExpr(b, lhs, false, depth); err != nil { |
| return err |
| } |
| if lhsIsArray { |
| if mcv != nil { |
| b.writes(") + ") |
| b.writes(mcv.String()) |
| } |
| b.writes(comma) |
| |
| length := lhs.MType().ArrayLength().ConstValue() |
| if rcv != nil { |
| length = rcv |
| } |
| if mcv != nil { |
| length = big.NewInt(0).Sub(length, mcv) |
| } |
| b.writes(length.String()) |
| b.writeb(')') |
| } |
| |
| if mhs != nil { |
| b.writes(comma) |
| if err := g.writeExpr(b, mhs, false, depth); err != nil { |
| return err |
| } |
| } |
| if rhs != nil { |
| b.writes(comma) |
| if err := g.writeExpr(b, rhs, false, depth); err != nil { |
| return err |
| } |
| } |
| if mhs != nil || rhs != nil { |
| b.writeb(')') |
| } |
| return nil |
| |
| case t.IDDot: |
| lhs := n.LHS().AsExpr() |
| if lhs.Ident() == t.IDArgs { |
| b.writes(aPrefix) |
| b.writes(n.Ident().Str(g.tm)) |
| return nil |
| } else if (lhs.Operator() == 0) && n.Ident().IsDQStrLiteral(g.tm) { |
| if z := g.statusMap[t.QID{lhs.Ident(), n.Ident()}]; z.cName != "" { |
| b.writes("wuffs_base__make_status(") |
| b.writes(z.cName) |
| b.writes(")") |
| return nil |
| } |
| return fmt.Errorf("unrecognized status %s", n.Str(g.tm)) |
| } |
| |
| if err := g.writeExpr(b, lhs, false, depth); err != nil { |
| return err |
| } |
| if p := lhs.MType().Decorator(); p == t.IDNptr || p == t.IDPtr { |
| b.writes("->") |
| } else { |
| b.writes(".") |
| } |
| |
| b.writes(g.privateImplData(lhs.MType(), n.Ident())) |
| b.writeb('.') |
| b.writes(fPrefix) |
| b.writes(n.Ident().Str(g.tm)) |
| return nil |
| } |
| return fmt.Errorf("unrecognized token (0x%X) for writeExprOther", n.Operator()) |
| } |
| |
| func (g *gen) privateImplData(typ *a.TypeExpr, fieldName t.ID) string { |
| if p := typ.Decorator(); p == t.IDNptr || p == t.IDPtr { |
| typ = typ.Inner() |
| } |
| if s := g.structMap[typ.QID()]; s != nil { |
| qid := s.QID() |
| if _, ok := g.privateDataFields[t.QQID{qid[0], qid[1], fieldName}]; ok { |
| return "private_data" |
| } |
| } |
| return "private_impl" |
| } |
| |
| func (g *gen) writeExprUnaryOp(b *buffer, n *a.Expr, depth uint32) error { |
| op := n.Operator() |
| opName := cOpName(op) |
| if opName == "" { |
| return fmt.Errorf("unrecognized operator %q", op.AmbiguousForm().Str(g.tm)) |
| } |
| |
| b.writes(opName) |
| return g.writeExpr(b, n.RHS().AsExpr(), false, depth) |
| } |
| |
| func (g *gen) writeExprBinaryOp(b *buffer, n *a.Expr, depth uint32) error { |
| opName, lhsCast, tildeMod := "", false, false |
| |
| op := n.Operator() |
| switch op { |
| case t.IDXBinaryTildeSatPlus, t.IDXBinaryTildeSatMinus: |
| uBits := uintBits(n.MType().QID()) |
| if uBits == 0 { |
| return fmt.Errorf("unsupported tilde-operator type %q", n.MType().Str(g.tm)) |
| } |
| uOp := "add" |
| if op != t.IDXBinaryTildeSatPlus { |
| uOp = "sub" |
| } |
| b.printf("wuffs_base__u%d__sat_%s", uBits, uOp) |
| opName = ", " |
| |
| case t.IDXBinaryAs: |
| return g.writeExprAs(b, n.LHS().AsExpr(), n.RHS().AsTypeExpr(), depth) |
| |
| case t.IDXBinaryTildeModPlus, t.IDXBinaryTildeModMinus, t.IDXBinaryTildeModStar: |
| tildeMod = true |
| |
| case t.IDXBinaryTildeModShiftL: |
| tildeMod = true |
| fallthrough |
| |
| case t.IDXBinaryShiftL, t.IDXBinaryShiftR: |
| if lhs := n.LHS().AsExpr(); lhs.ConstValue() != nil { |
| lhsCast = true |
| } |
| } |
| |
| if opName == "" { |
| opName = cOpName(op) |
| if opName == "" { |
| return fmt.Errorf("unrecognized operator %q", op.AmbiguousForm().Str(g.tm)) |
| } |
| } |
| |
| b.writeb('(') |
| if tildeMod { |
| b.writeb('(') |
| if err := g.writeCTypeName(b, n.MType(), "", ""); err != nil { |
| return err |
| } |
| b.writes(")(") |
| } |
| |
| if lhsCast { |
| b.writes("((") |
| if err := g.writeCTypeName(b, n.LHS().AsExpr().MType(), "", ""); err != nil { |
| return err |
| } |
| b.writes(")(") |
| } |
| if err := g.writeExprRepr(b, n.LHS().AsExpr(), depth); err != nil { |
| return err |
| } |
| if lhsCast { |
| b.writes("))") |
| } |
| |
| b.writes(opName) |
| |
| if err := g.writeExprRepr(b, n.RHS().AsExpr(), depth); err != nil { |
| return err |
| } |
| |
| if tildeMod { |
| b.writeb(')') |
| } |
| b.writeb(')') |
| return nil |
| } |
| |
| func (g *gen) writeExprRepr(b *buffer, n *a.Expr, depth uint32) error { |
| isStatus := n.MType().IsStatus() |
| if isStatus { |
| if op := n.Operator(); ((op == 0) || (op == a.ExprOperatorSelector)) && n.Ident().IsDQStrLiteral(g.tm) { |
| qid := t.QID{0, n.Ident()} |
| if op == t.IDDot { |
| qid[0] = n.LHS().AsExpr().Ident() |
| } |
| if z := g.statusMap[qid]; z.cName != "" { |
| b.writes(z.cName) |
| return nil |
| } |
| } |
| } |
| if err := g.writeExpr(b, n, false, depth); err != nil { |
| return err |
| } |
| if isStatus { |
| b.writes(".repr") |
| } |
| return nil |
| } |
| |
| func (g *gen) writeExprAs(b *buffer, lhs *a.Expr, rhs *a.TypeExpr, depth uint32) error { |
| b.writes("((") |
| // TODO: watch for passing an array type to writeCTypeName? In C, an array |
| // type can decay into a pointer. |
| if err := g.writeCTypeName(b, rhs, "", ""); err != nil { |
| return err |
| } |
| b.writes(")(") |
| if err := g.writeExpr(b, lhs, false, depth); err != nil { |
| return err |
| } |
| b.writes("))") |
| return nil |
| } |
| |
| func (g *gen) writeExprAssociativeOp(b *buffer, n *a.Expr, depth uint32) error { |
| op := n.Operator() |
| opName := cOpName(op) |
| if opName == "" { |
| return fmt.Errorf("unrecognized operator %q", op.AmbiguousForm().Str(g.tm)) |
| } |
| if len(n.Args()) > 3 { |
| opName = strings.TrimRight(opName, " ") + "\n" |
| } |
| |
| b.writeb('(') |
| for i, o := range n.Args() { |
| if i != 0 { |
| b.writes(opName) |
| } |
| if err := g.writeExpr(b, o.AsExpr(), false, depth); err != nil { |
| return err |
| } |
| } |
| b.writeb(')') |
| return nil |
| } |
| |
| func (g *gen) writeExprUserDefinedCall(b *buffer, n *a.Expr, depth uint32) error { |
| method := n.LHS().AsExpr() |
| recv := method.LHS().AsExpr() |
| recvTyp, addr := recv.MType(), "&" |
| if p := recvTyp.Decorator(); p == t.IDNptr || p == t.IDPtr { |
| recvTyp, addr = recvTyp.Inner(), "" |
| } |
| if recvTyp.Decorator() != 0 { |
| return fmt.Errorf("cannot generate user-defined method call %q for receiver type %q", |
| n.Str(g.tm), recv.MType().Str(g.tm)) |
| } |
| qid := recvTyp.QID() |
| b.printf("%s%s__%s(", g.packagePrefix(qid), qid[1].Str(g.tm), method.Ident().Str(g.tm)) |
| if !recvTyp.IsEtcUtilityType() { |
| b.writes(addr) |
| if err := g.writeExpr(b, recv, false, depth); err != nil { |
| return err |
| } |
| if len(n.Args()) > 0 { |
| b.writes(", ") |
| } |
| } |
| return g.writeArgs(b, n.Args(), depth) |
| } |
| |
| func (g *gen) writeCTypeName(b *buffer, n *a.TypeExpr, varNamePrefix string, varName string) error { |
| // It may help to refer to http://unixwiz.net/techtips/reading-cdecl.html |
| |
| // TODO: fix this, allow slices of all types, not just of base.u8's. Also |
| // allow arrays of slices, slices of pointers, etc. |
| if n.IsSliceType() { |
| o := n.Inner() |
| if o.Decorator() == 0 && o.QID() == (t.QID{t.IDBase, t.IDU8}) && !o.IsRefined() { |
| b.writes("wuffs_base__slice_u8") |
| if varNamePrefix != "" { |
| b.writeb(' ') |
| b.writes(varNamePrefix) |
| b.writes(varName) |
| } |
| return nil |
| } |
| return fmt.Errorf("cannot convert Wuffs type %q to C", n.Str(g.tm)) |
| } |
| if n.IsTableType() { |
| o := n.Inner() |
| if o.Decorator() == 0 && o.QID() == (t.QID{t.IDBase, t.IDU8}) && !o.IsRefined() { |
| b.writes("wuffs_base__table_u8") |
| if varNamePrefix != "" { |
| b.writeb(' ') |
| b.writes(varNamePrefix) |
| b.writes(varName) |
| } |
| return nil |
| } |
| return fmt.Errorf("cannot convert Wuffs type %q to C", n.Str(g.tm)) |
| } |
| |
| // maxNumPointers is an arbitrary implementation restriction. |
| const maxNumPointers = 16 |
| |
| x := n |
| for ; x != nil && x.IsArrayType(); x = x.Inner() { |
| } |
| |
| numPointers, innermost := 0, x |
| for ; innermost != nil && innermost.Inner() != nil; innermost = innermost.Inner() { |
| if p := innermost.Decorator(); p == t.IDNptr || p == t.IDPtr { |
| if numPointers == maxNumPointers { |
| return fmt.Errorf("cannot convert Wuffs type %q to C: too many ptr's", n.Str(g.tm)) |
| } |
| numPointers++ |
| continue |
| } |
| // TODO: fix this. |
| return fmt.Errorf("cannot convert Wuffs type %q to C", n.Str(g.tm)) |
| } |
| |
| fallback := true |
| if qid := innermost.QID(); qid[0] == t.IDBase { |
| if key := qid[1]; key < t.ID(len(cTypeNames)) { |
| if s := cTypeNames[key]; s != "" { |
| b.writes(s) |
| fallback = false |
| } |
| } |
| } |
| if fallback { |
| qid := innermost.QID() |
| b.printf("%s%s", g.packagePrefix(qid), qid[1].Str(g.tm)) |
| } |
| |
| for i := 0; i < numPointers; i++ { |
| b.writeb('*') |
| } |
| |
| if varNamePrefix != "" { |
| b.writeb(' ') |
| b.writes(varNamePrefix) |
| b.writes(varName) |
| } |
| |
| x = n |
| for ; x != nil && x.IsArrayType(); x = x.Inner() { |
| b.writeb('[') |
| b.writes(x.ArrayLength().ConstValue().String()) |
| b.writeb(']') |
| } |
| |
| return nil |
| } |
| |
| func (g *gen) packagePrefix(qid t.QID) string { |
| if qid[0] != 0 { |
| otherPkg := g.tm.ByID(qid[0]) |
| // TODO: map the "deflate" in "deflate.decoder" to the "deflate" in |
| // `use "std/deflate"`, and use the latter "deflate". |
| // |
| // This is pretty academic at the moment, since they're the same |
| // "deflate", but in the future, we might be able to rename used |
| // packages, e.g. `use "foo/bar" as "baz"`, so "baz.qux" would map |
| // to generating "wuffs_bar__qux". |
| // |
| // TODO: sanitize or validate otherPkg, e.g. that it's ASCII only? |
| // |
| // See gen.writeInitializerImpl for a similar use of otherPkg. |
| return "wuffs_" + otherPkg + "__" |
| } |
| return g.pkgPrefix |
| } |
| |
| func isBaseRangeType(qid t.QID) bool { |
| if qid[0] == t.IDBase { |
| switch qid[1] { |
| case t.IDRangeIEU32, t.IDRangeIIU32, t.IDRangeIEU64, t.IDRangeIIU64: |
| return true |
| } |
| } |
| return false |
| } |
| |
| var cTypeNames = [...]string{ |
| t.IDI8: "int8_t", |
| t.IDI16: "int16_t", |
| t.IDI32: "int32_t", |
| t.IDI64: "int64_t", |
| t.IDU8: "uint8_t", |
| t.IDU16: "uint16_t", |
| t.IDU32: "uint32_t", |
| t.IDU64: "uint64_t", |
| t.IDBool: "bool", |
| |
| t.IDIOReader: "wuffs_base__io_buffer*", |
| t.IDIOWriter: "wuffs_base__io_buffer*", |
| t.IDTokenReader: "wuffs_base__token_buffer*", |
| t.IDTokenWriter: "wuffs_base__token_buffer*", |
| |
| t.IDARMCRC32U32: "uint32_t", |
| t.IDARMNeonU8x8: "uint8x8_t", |
| t.IDARMNeonU16x4: "uint16x4_t", |
| t.IDARMNeonU32x2: "uint32x2_t", |
| t.IDARMNeonU64x1: "uint64x1_t", |
| t.IDARMNeonU8x16: "uint8x16_t", |
| t.IDARMNeonU16x8: "uint16x8_t", |
| t.IDARMNeonU32x4: "uint32x4_t", |
| t.IDARMNeonU64x2: "uint64x2_t", |
| t.IDX86M128I: "__m128i", |
| t.IDX86M256I: "__m256i", |
| } |
| |
| const noSuchCOperator = " no_such_C_operator " |
| |
| func cOpName(x t.ID) string { |
| if x < t.ID(len(cOpNames)) { |
| if s := cOpNames[x]; s != "" { |
| return s |
| } |
| } |
| return noSuchCOperator |
| } |
| |
| var cOpNames = [...]string{ |
| t.IDPlusEq: " += ", |
| t.IDMinusEq: " -= ", |
| t.IDStarEq: " *= ", |
| t.IDSlashEq: " /= ", |
| t.IDShiftLEq: " <<= ", |
| t.IDShiftREq: " >>= ", |
| t.IDAmpEq: " &= ", |
| t.IDPipeEq: " |= ", |
| t.IDHatEq: " ^= ", |
| t.IDPercentEq: " %= ", |
| t.IDTildeModPlusEq: " += ", |
| t.IDTildeModMinusEq: " -= ", |
| t.IDTildeModStarEq: " *= ", |
| t.IDTildeModShiftLEq: " <<= ", |
| t.IDTildeSatPlusEq: noSuchCOperator, |
| t.IDTildeSatMinusEq: noSuchCOperator, |
| |
| t.IDEq: " = ", |
| t.IDEqQuestion: " = ", |
| |
| t.IDXBinaryPlus: " + ", |
| t.IDXBinaryMinus: " - ", |
| t.IDXBinaryStar: " * ", |
| t.IDXBinarySlash: " / ", |
| t.IDXBinaryShiftL: " << ", |
| t.IDXBinaryShiftR: " >> ", |
| t.IDXBinaryAmp: " & ", |
| t.IDXBinaryPipe: " | ", |
| t.IDXBinaryHat: " ^ ", |
| t.IDXBinaryPercent: " % ", |
| t.IDXBinaryTildeModPlus: " + ", |
| t.IDXBinaryTildeModMinus: " - ", |
| t.IDXBinaryTildeModStar: " * ", |
| t.IDXBinaryTildeModShiftL: " << ", |
| t.IDXBinaryTildeSatPlus: noSuchCOperator, |
| t.IDXBinaryTildeSatMinus: noSuchCOperator, |
| t.IDXBinaryNotEq: " != ", |
| t.IDXBinaryLessThan: " < ", |
| t.IDXBinaryLessEq: " <= ", |
| t.IDXBinaryEqEq: " == ", |
| t.IDXBinaryGreaterEq: " >= ", |
| t.IDXBinaryGreaterThan: " > ", |
| t.IDXBinaryAnd: " && ", |
| t.IDXBinaryOr: " || ", |
| t.IDXBinaryAs: noSuchCOperator, |
| |
| t.IDXAssociativePlus: " + ", |
| t.IDXAssociativeStar: " * ", |
| t.IDXAssociativeAmp: " & ", |
| t.IDXAssociativePipe: " | ", |
| t.IDXAssociativeHat: " ^ ", |
| t.IDXAssociativeAnd: " && ", |
| t.IDXAssociativeOr: " || ", |
| |
| t.IDXUnaryPlus: " + ", |
| t.IDXUnaryMinus: " - ", |
| t.IDXUnaryNot: " ! ", |
| } |