diff options
Diffstat (limited to 'lib/translate/translate.go')
-rw-r--r-- | lib/translate/translate.go | 306 |
1 files changed, 259 insertions, 47 deletions
diff --git a/lib/translate/translate.go b/lib/translate/translate.go index d02e8b4..613da3a 100644 --- a/lib/translate/translate.go +++ b/lib/translate/translate.go @@ -1,25 +1,116 @@ package translate import ( - "fmt" "io" "pgset/lib/parse" + "errors" + "strings" ) -func a(language string) string { - return "" +type State struct { + // Which type of anchor was last begun: href or id? + lastAnchor string + // Consume input until further notice? + consume bool + // Stay on the same line of output until further notice? + sameLine bool + // Stat on the same line of output at least for this input? + sameLineOnce bool + + // metadata + title string + language string + creator string + subject string +} + +func style(state *State) { + state.consume = true +} +func slashStyle(state *State) { + state.consume = false } -func img(language string) string { - return "" +func href(t *parse.Tag, language string) (string, error) { + switch language { + case "Neatroff": + // .post.url #chapter23\ntext\n\n + return ".post.url " + t.Attributes["href"], nil + default: + return "", errors.New("Unimplemented") + } +} + +func id(t *parse.Tag, language string) (string, error) { + switch language { + case "Neatroff": + // .post.name chapter23 + return ".post.name " + strings.Replace(t.Attributes["id"], "#", "", 1), nil + default: + return "", errors.New("Unimplemented") + } } -func header(language string) string { - return "" +func a(t *parse.Tag, language string, state *State) (string, error) { + // There are two kinds of anchors. + // href: refer to a resource, internal or external + // id: serve as an anchor to be referred to + types := []string{"href", "id"} + // Least bad expression of this idea in this language + m := map[string]func (*parse.Tag, string) (string, error) { + "href": href, + "id": id, + } + for _, i := range types { + if _, ok := t.Attributes[i]; ok { + state.lastAnchor = i + s, err := m[i](t, language) + if err != nil { + return "", err + } + return s, nil + } + } + return "", errors.New("Broken anchor. " + t.String()) +} +func slashA(t *parse.Tag, language string, state *State) (string, error) { + var s string + switch state.lastAnchor { + case "href": + fallthrough + case "id": + fallthrough + default: + s = "\n" + } + state.lastAnchor = "" + return s, nil } -func footer(language string) string { - return "" +func meta(t *parse.Tag, language string, state *State) (string, error) { + // Multiple types of this tag. Only those with "name" attribute are interesting + s := "" + if _, ok := t.Attributes["name"]; ok { + s += "name" + } + return s, nil +} + +func i(state *State) (string, error) { + state.sameLine = true + return `\fI`, nil +} +func slashI(state *State) (string, error) { + state.sameLine = false + state.sameLineOnce = true + return `\fP`, nil +} +func strong(state *State) (string, error) { + state.sameLine = true + return `\fB`, nil +} +func slashStrong(state *State) (string, error) { + return slashI(state) } // Among all languages, these are defined: @@ -32,28 +123,74 @@ func footer(language string) string { // - a: anchors to bookmarks and urls, switch on attribute // - img: images // - p: paragraphs -// - meta: metadata +// - meta, link: metadata // - h{1,2,3,4,5}: headings // - strong, i: emphasis // - table, tbody, tr, td: Table of Contents // - div: chapter separations // - br: line breaks +// - blockquote: block quotations +// - hr: horizontal rule // // All other tags are ignored. -// - span: Unwanted metadata, contents to be consumed +// - span: not meaningful, essentially empty +// - body: not meaningful +// - head: not meaningful +// - Non-standard and atypical extensions var Definitions = map[string]map[string]any{ "ConTeXt": { // Must generate. - "a": a, + "a": a, "/a": ``, - "meta": meta, + // Standardized metadata. + // charset: character set + // + "meta": func(t *parse.Tag) (string, error) { + var s string + for k, v := range t.Attributes { + s = k + v + } + return s, nil + }, "/meta": ``, - "div": div, + "div": func(t *parse.Tag) (string, error) { + var s string + for k, v := range t.Attributes { + s = k + v + } + return s, nil + }, "/div": ``, + "Header": func(t *parse.Tag) (string, error) { + var s string + for k, v := range t.Attributes { + s = k + v + } + return s, nil + }, + "Footer": func(t *parse.Tag) (string, error) { + var s string + for k, v := range t.Attributes { + s = k + v + } + return s, nil + }, + // link tags are empty + "link": func(t *parse.Tag) (string, error) { + var s string + for k, v := range t.Attributes { + s = k + v + } + return s, nil + }, // img tags are empty - "img": img, - "Header": header, - "Footer": footer, + "img": func(t *parse.Tag) (string, error) { + var s string + for k, v := range t.Attributes { + s = k + v + } + return s, nil + }, // Constants. "p": ``, @@ -80,10 +217,14 @@ var Definitions = map[string]map[string]any{ "/h4": ``, "h5": ``, "/h5": ``, + "blockquote": ``, + "/blockquote": ``, // br tags are empty "br": ``, + // hr tags are empty + "hr": ``, "Prefix": ``, - "Postfix", ``, + "Postfix": ``, }, "kerTeX": {}, "XeTeX": {}, @@ -95,6 +236,9 @@ var Definitions = map[string]map[string]any{ .ssh 15 .kn 1 .hlm 2 +.de empty +.. +.blm empty .ds margin 1.25i .po \*[margin] .de header @@ -103,6 +247,9 @@ var Definitions = map[string]map[string]any{ .de footer ' bp .. +.de pg +. ti +1m +.. .wh 0 header .wh -\*[margin] footer .ds measure 6i @@ -173,6 +320,65 @@ var Definitions = map[string]map[string]any{ . /h2 ..`, "Footer": ``, + "Prefix": "", + "Postfix": "", + "NAE": ``, + "a": a, + "/a": slashA, + // Constants. + "p": `.pg`, + "/p": ``, + "i": i, + "/i": slashI, + "strong": strong, + "/strong": slashStrong, + "table": ``, + "/table": ``, + "tr": ``, + "/tr": ``, + "tbody": ``, + "/tbody": ``, + "td": ``, + "/td": ``, + "h1": ``, + "/h1": ``, + "h2": ``, + "/h2": ``, + "h3": ``, + "/h3": ``, + "h4": ``, + "/h4": ``, + "h5": ``, + "/h5": ``, + "blockquote": ``, + "/blockquote": ``, + // br tags are empty + "br": ``, + // hr tags are empty + "hr": ``, + "head": ``, + "/head": ``, + "title": ``, + "/title": ``, + "style": style, + "/style": slashStyle, + "html": ``, + "/html": ``, + "link": ``, + "meta": ``, + "body": ``, + "/body": ``, + "div": ``, + "/div": ``, + "section": ``, + "/section": ``, + "span": ``, + "/span": ``, + "li": ``, + "/li": ``, + "ul": ``, + "/ul": ``, + "img": ``, }, "Utmac": {}, "groff": {}, @@ -402,39 +608,45 @@ It's a versatile family providing users with plenty of features and moods. It sp }, } -// Templates. -// Optically correct scaling. -// Some people need larger body type. - -func style(language, key, value string) (string, error) { - return "", nil -} - -func generate(mark, language string) (string, error) { - if Definitions[language] == nil { - return errors.New(language + " is a new language, define it") - } -} - -func Generate(language string, tag Tag) string { - switch t := Definitions[language][mark].(type) { - case string: - return t - case func(Tag): - t(tag) - } -} - -func Translate(document []any, language string, output io.Writer) { +func Translate(document []any, language string, output io.Writer) error { + output.Write([]byte(Definitions[language]["Header"].(string) + "\n")) + state := new(State) + var err error for _, i := range document { + var out string switch t := i.(type) { case string: - output.Write(generate(language, *Tag{name: "Prefix"})) - output.Write(definitions[language]["Prefix"]) - output.Write(t) - output.Write(generate(language, *Tag{name: "Postfix"})) + out = Definitions[language]["Prefix"].(string) + t + Definitions[language]["Postfix"].(string) case *parse.Tag: - fmt.Println(i.name) + switch u := Definitions[language][t.Name].(type) { + case string: + out = u + case func(*parse.Tag) (string, error): + out, err = u(t) + case func(*parse.Tag, string, *State) (string, error): + out, err = u(t, language, state) + case func(*State) (string, error): + out, err = u(state) + case func(*State): + u(state) + out = "" + case nil: + out = "dne" + t.String() + "\n" + } + } + if err != nil { + return err + } + if out != "" && !state.consume { + if !state.sameLine && !state.sameLineOnce { + out += "\n" + } + if state.sameLineOnce { + state.sameLineOnce = false + } + output.Write([]byte(out)) } } + output.Write([]byte(Definitions[language]["Footer"].(string) + "\n")) + return nil } |