~smlavine/eaton

cc3a57826ef3cd6c0097878f752796d3a1bf5dba — Sebastian LaVine 6 months ago 2410337
TOREVIEW rewrite to use more XML-y style for macros

I think I'm going to throw all this out to use a non-regular lexer
though.
6 files changed, 15 insertions(+), 42 deletions(-)

M README.md
M macro/+test.ha
M macro/concat.ha
M macro/getenv.ha
M macro/img.ha
M macro/upper.ha
M README.md => README.md +1 -22
@@ 12,7 12,7 @@ still being worked on; in particular, nested macros.
$ make
hare build
$ cat input1.txt
Hi there, ``upper!(everyone)''!
Hi there, ``<upper! "everyone"/>''!
$ ./eaton < input1.txt
Hi there, EVERYONE!
```


@@ 38,27 38,6 @@ All of the macros provided with eaton are named like `name!(arguments)`.
But this is just a convention; any macros you write yourself do not have
to follow it.

### Regular expressions

Take care when writing the regular expressions used to match your
macros. Writing a regular expression that always matches (such as
`(.*)`) will crash the program. The Hare regex engine is greedy, so if
matching for text within parens, don't match closing parens in your
capture. Consider a simple, correct regular expression, as defined in
`macro/upper.ha`:

```hare
def UPPER_RE = `upper!\(([^)]*)\)`;
```

The contents of the capture group is what will be provided to the macro
for formatting. Instead of capturing `.*`, we capture `[^)]*`,
preventing the expression from matching beyond what is intended.

For more information on regular expressions, consider reading [the
documentation for the Hare regex module](https://docs.harelang.org/regex)
or [regex(7)](https://linux.die.net/man/7/regex).

### Modifying state

There is no method of modifying state (i.e. defining macros or setting

M macro/+test.ha => macro/+test.ha +4 -4
@@ 19,7 19,7 @@ use strings::{toutf8};
};

@test fn one_simple_macro() void = {
	const input = "upper!(text)";
	const input = `<upper! "text"/>`;
	let out = memio::dynamic();
	defer io::close(&out): void;



@@ 31,7 31,7 @@ use strings::{toutf8};
};

@test fn regular_text_and_macro() void = {
	const input = "Hi there, upper!(everyone)!";
	const input = `Hi there, <upper! "everyone" />!";
	let out = memio::dynamic();
	defer io::close(&out): void;



@@ 43,7 43,7 @@ use strings::{toutf8};
};

@test fn multiple_macros() void = {
	const input = "My name is upper!(eaton) and I live at concat!(123 Apple Street).";
	const input = `My name is <upper! "eaton"/> and I live at <concat! "123 Apple Street"/>.`;
	let out = memio::dynamic();
	defer io::close(&out): void;



@@ 55,7 55,7 @@ use strings::{toutf8};
};

@test fn nested_macros() void = {
	const input = "upper!(concat!(a b))";
	const input = `<upper! "<concat! "a b"/>" />"`;
	let out = memio::dynamic();
	defer io::close(&out): void;


M macro/concat.ha => macro/concat.ha +3 -3
@@ 10,16 10,17 @@ use memio;
use regex::{compile};
use strings;

def CONCAT_RE = `concat!\(([^)]*)\)`;
def CONCAT_RE = "<[ \t]*concat![ \t]+\"(.*)\"[ \t]*/[ \t]*>";

@init fn append_macro() void = {
	append(macros, macro {
		info = (`concat!(...)`, "Removes all whitespace"),
		info = (`<concat! "..." />`, "Removes all whitespace"),
		regex = compile(CONCAT_RE)!,
		formatter = &concat,
	});
};

// XXX slice[n..m] instead of reallocating a buffer and deleting
export fn concat(h: io::handle, args: []str) (size | io::error) = {
	assert(len(args) == 1);



@@ 51,7 52,6 @@ export fn concat(h: io::handle, args: []str) (size | io::error) = {
	let out = memio::dynamic();
	defer io::close(&out): void;


	concat(&out, ["\f\n\r\t\v "])!;
	let outstr = memio::string(&out)!;


M macro/getenv.ha => macro/getenv.ha +2 -8
@@ 11,17 11,11 @@ use os;
use regex::{compile};
use strings;

// TODO: provided default value; doesn't work
//def GETENV_RE = `getenv!\([[:space:]]*([^),]*)(,[[:space:]]*"[^"]*")?[[:space:]]*\)`;

def GETENV_RE = `getenv!\([[:space:]]*([^)[:space:]]*)[[:space:]]*\)`;
def GETENV_RE = "<[ \t]*getenv![ \t]+\"(.*)\"[ \t]*/[ \t]*>";

@init fn append_macro() void = {
	append(macros, macro {
		info = (
			`getenv!(...)`,
			`Replaces macro with environment variable value, or "" if unset`,
		),
		info = (`<getenv! "..." />`, `Replaces macro with env. var.`),
		regex = compile(GETENV_RE)!,
		formatter = &getenv,
	});

M macro/img.ha => macro/img.ha +3 -3
@@ 10,13 10,13 @@ use io;
use memio;
use regex::{compile};

def IMG_RE = `img!\([[:space:]]*"(.*)"[[:space:]]*,[[:space:]]*"(.*)"[[:space:]]*\)`;
def IMG_RE = "<[ \t]*img![ \t]+\"(.*)\"[ \t]*,[ \t]*\"(.*)\"[ \t]*/[ \t]*>";

@init fn append_macro() void = {
	append(macros, macro {
		info = (
			`img!("src", "alt")`,
			`<a href="$src"><img src="$src" alt="$alt"></a>`
			`<img! "src", "alt"/>`,
			`<a href="$src"><img src="$src" alt="$alt"></a>`,
		),
		regex = compile(IMG_RE)!,
		formatter = &img,

M macro/upper.ha => macro/upper.ha +2 -2
@@ 12,12 12,12 @@ use memio;
use regex::{compile};
use strings::{toutf8};

def UPPER_RE = `upper!\(([^)]*)\)`;
def UPPER_RE = "<[ \t]*upper![ \t]+\"(.*)\"[ \t]*/[ \t]*>";

@init fn append_macro() void = {
	append(macros, macro {
		info = (
			`upper!(...)`,
			`<upper! "..." />`,
			"Converts all lowercase characters to uppercase",
		),
		regex = compile(UPPER_RE)!,