Skip to content

Commit

Permalink
remove \mathfmt{...} macro (#8)
Browse files Browse the repository at this point in the history
* Now _all_ comment text is processed as a formula.
* Uses go ast tooling, therefore limits this tool to Go source code
* Adds golang.org/x/tools dependency

Fixes #7
  • Loading branch information
mmcloughlin authored Feb 5, 2020
1 parent 6786248 commit b9c7196
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 180 deletions.
104 changes: 55 additions & 49 deletions format.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,65 @@ package main
import (
"bytes"
"errors"
"go/ast"
"go/format"
"go/parser"
"go/token"
"strings"
"unicode"
"unicode/utf8"
)

// macroname is the name of the macro that applies math formatting.
const macroname = "\\mathfmt"

// Format processes the source code in b.
func Format(b []byte) ([]byte, error) {
var buf bytes.Buffer
s := string(b)
for len(s) > 0 {
// Look for the next macro.
i := strings.Index(s, macroname)

// Exit if not found.
if i < 0 {
buf.WriteString(s)
break
}

// Write out up to the macro.
buf.WriteString(s[:i])
s = s[i:]

// Process the macro.
rest, err := macro(&buf, s[len(macroname):])
if err != nil {
return nil, err
}
s = rest
}

return buf.Bytes(), nil
}
"golang.org/x/tools/go/ast/astutil"
)

// macro processes a macro starting at s. Note s begins at the character directly after the macro name.
func macro(w *bytes.Buffer, s string) (string, error) {
if len(s) == 0 {
return "", errors.New("empty macro")
// Format processes the source code.
func Format(src []byte) ([]byte, error) {
// Parse.
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
if err != nil {
return nil, err
}

arg, rest, err := parsebraces(s)
// Apply transform.
transformed := CommentTransform(f, func(text string) string {
newtext, errf := formula(text)
if errf != nil {
err = errf
return text
}
return newtext
})
if err != nil {
return "", err
return nil, err
}

n := len(arg)
if err := formula(w, arg[1:n-1]); err != nil {
return "", err
// Format.
buf := bytes.NewBuffer(nil)
if err := format.Node(buf, fset, transformed); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

return rest, nil
// CommentTransform applies transform to the text of every comment under the root AST.
func CommentTransform(root ast.Node, transform func(string) string) ast.Node {
return astutil.Apply(root, func(c *astutil.Cursor) bool {
switch n := c.Node().(type) {
case *ast.Comment:
c.Replace(&ast.Comment{
Slash: n.Slash,
Text: transform(n.Text),
})
case *ast.File:
for _, g := range n.Comments {
for _, comment := range g.List {
comment.Text = transform(comment.Text)
}
}
}
return true
}, nil)
}

// Fixed data structures required for formula processing.
Expand Down Expand Up @@ -86,15 +91,16 @@ func init() {
}

// formula processes a formula in s, writing the result to w.
func formula(w *bytes.Buffer, s string) error {
func formula(s string) (string, error) {
if len(s) == 0 {
return nil
return "", nil
}

// Replace symbols.
s = replacer.Replace(s)

// Replace super/subscripts.
buf := bytes.NewBuffer(nil)
last := None
for len(s) > 0 {
r, size := utf8.DecodeRuneInString(s)
Expand All @@ -107,7 +113,7 @@ func formula(w *bytes.Buffer, s string) error {
case '_':
repl = sub
default:
w.WriteRune(r)
buf.WriteRune(r)
last = r
s = s[size:]
continue
Expand All @@ -116,19 +122,19 @@ func formula(w *bytes.Buffer, s string) error {
// Perform replacement.
if unicode.IsPrint(last) && !unicode.IsSpace(last) {
var err error
s, err = supsub(w, s, repl)
s, err = supsub(buf, s, repl)
if err != nil {
return err
return "", err
}
} else {
w.WriteRune(r)
buf.WriteRune(r)
s = s[size:]
}

last = None
}

return nil
return buf.String(), nil
}

// supsub processes a super/subscript starting at s, writing the result to w.
Expand Down
5 changes: 1 addition & 4 deletions format_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"bytes"
"testing"
)

Expand Down Expand Up @@ -58,12 +57,10 @@ func TestFormula(t *testing.T) {
for _, c := range cases {
c := c // scopelint
t.Run(c.Name, func(t *testing.T) {
buf := bytes.NewBuffer(nil)
err := formula(buf, c.Input)
got, err := formula(c.Input)
if err != nil {
t.Fatal(err)
}
got := buf.String()
if got != c.Expect {
t.Logf("input = %q", c.Input)
t.Logf("got = %q", got)
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/mmcloughlin/mathfmt

go 1.11

require golang.org/x/tools v0.0.0-20200204230316-67a4523381ef
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200204230316-67a4523381ef h1:mdhEDFpO1Tfj7PXIflIuP1tbXt4rJgHIvbzdh62SARw=
golang.org/x/tools v0.0.0-20200204230316-67a4523381ef/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
16 changes: 8 additions & 8 deletions testdata/p256.golden
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ func p256ReduceDegree(out *[p256Limbs]uint32, tmp [17]uint64) {
// by adding multiplies of p without affecting the value.
//
// So we eliminate limbs from right to left. Since the bottom 29 bits of p
// are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] == 0.
// are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] 0.
// We can do that for 8 further limbs and then right shift to eliminate the
// extra factor of R.
for i := 0; ; i += 2 {
Expand Down Expand Up @@ -655,16 +655,16 @@ func p256Assign(out, in *[p256Limbs]uint32) {
*out = *in
}

// p256Invert calculates |out| = |in|^{-1}
// p256Invert calculates |out| = |in|⁻¹
//
// Based on Fermat's Little Theorem:
// a^p = a (mod p)
// a^{p-1} = 1 (mod p)
// a^{p-2} = a^{-1} (mod p)
// aᵖ = a (mod p)
// aᵖ⁻¹ = 1 (mod p)
// aᵖ⁻² = a⁻¹ (mod p)
func p256Invert(out, in *[p256Limbs]uint32) {
var ftmp, ftmp2 [p256Limbs]uint32

// each e_I will hold |in|^{2^I - 1}
// each e_I will hold |in|^{2ᴵ - 1}
var e2, e4, e8, e16, e32, e64 [p256Limbs]uint32

p256Square(&ftmp, in) // 2¹
Expand Down Expand Up @@ -939,7 +939,7 @@ func p256CopyConditional(out, in *[p256Limbs]uint32, mask uint32) {
}
}

// p256SelectAffinePoint sets {out_x,out_y} to the index'th entry of table.
// p256SelectAffinePoint sets {xOut,yOut} to the index'th entry of table.
// On entry: index < 16, table[0] must be zero.
func p256SelectAffinePoint(xOut, yOut *[p256Limbs]uint32, table []uint32, index uint32) {
for i := range xOut {
Expand All @@ -966,7 +966,7 @@ func p256SelectAffinePoint(xOut, yOut *[p256Limbs]uint32, table []uint32, index
}
}

// p256SelectJacobianPoint sets {out_x,out_y,out_z} to the index'th entry of
// p256SelectJacobianPoint sets {xOut,yOut,zOut} to the index'th entry of
// table.
// On entry: index < 16, table[0] must be zero.
func p256SelectJacobianPoint(xOut, yOut, zOut *[p256Limbs]uint32, table *[16][3][p256Limbs]uint32, index uint32) {
Expand Down
Loading

0 comments on commit b9c7196

Please sign in to comment.