diff options
| author | kaa <kaa@disroot.org> | 2025-08-30 22:03:37 -0700 | 
|---|---|---|
| committer | kaa <kaa@disroot.org> | 2025-08-30 22:03:37 -0700 | 
| commit | f38b4ad8b7543b088c5c0c4018285127ad4f4680 (patch) | |
| tree | a2fd9587a98b8605f5416ea69048f13937fca510 /lib/translate | |
| parent | a333aeef611a099827c4e261a3fce7e541eb1f5f (diff) | |
Basic neatroff
Diffstat (limited to 'lib/translate')
| -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  }  | 
