~evan/weblisp

058cb9256122158cfe555baf217b29d42dc5f310 — Evan Johnston 1 year, 1 day ago
init
7 files changed, 176 insertions(+), 0 deletions(-)

A .gitignore
A expr/expr.ha
A lex/lex.ha
A main.ha
A parse/parse.ha
A unparse/expr.ha
A unparse/html.ha
A  => .gitignore +1 -0
@@ 1,1 @@
weblisp

A  => expr/expr.ha +4 -0
@@ 1,4 @@
export type cons = (expr, expr);
export type symbol = str;
export type param = (str, str);
export type expr = (*cons | str | symbol | param | void);

A  => lex/lex.ha +62 -0
@@ 1,62 @@
use io;
use encoding::utf8;
use strio;
use bufio;

export type unexpected = !(rune | io::EOF);
export type error = !(io::error | utf8::invalid | unexpected);
export type symbol = str;
export type lparen = void;
export type rparen = void;
export type token = (lparen | rparen | symbol | str | io::EOF);

fn lexstr(in: io::handle) (token | error) = {
	let escaped = false;
	let buf = strio::dynamic();
	for (true) match (bufio::scanrune(in)?) {
	case io::EOF => return io::EOF: unexpected;
	case let r: rune =>
		if (r == '"' && !escaped) break;
		if (r == '\\') {
			if (!escaped) continue;
			escaped = !escaped;
		};
		strio::appendrune(&buf, r)?;

	};
	return strio::string(&buf);
};

fn lexsym(in: io::handle, start: rune) (token | error) = {
	let buf = strio::dynamic();
	strio::appendrune(&buf, start)?;
	for (true) match (bufio::scanrune(in)?) {
	case io::EOF => break;
	case let r: rune =>
		switch (r) {
		case ' ', '\t', '\n', '(', ')', '"' =>
			bufio::unreadrune(in, r);
			break;
		case =>
			strio::appendrune(&buf, r)?;
		};
	};
	return strio::string(&buf): symbol;
};

export fn lex(in: io::handle) (token | error) = match (bufio::scanrune(in)?) {
case io::EOF => return io::EOF: token;
case let r: rune =>
	switch (r) {
	case ' ', '\t', '\n' =>
		return lex(in);
	case '(' =>
		return lparen;
	case ')' =>
		return rparen;
	case '"' =>
		return lexstr(in);
	case =>
		return lexsym(in, r);
	};
};

A  => main.ha +13 -0
@@ 1,13 @@
use parse;
use expr;
use unparse;
use os;
use fmt;

export fn main() void = {
	let e = parse::parse(os::stdin)!;
	unparse::expr(e, os::stdout)!;
	fmt::println()!;
	unparse::html(e, os::stdout)!;
	fmt::println()!;
};

A  => parse/parse.ha +28 -0
@@ 1,28 @@
use io;
use lex;
use expr;

export type unexpected = !lex::token;
export type error = !(lex::error | unexpected);

fn parselist(in: io::handle) (expr::expr | error) = {
	let t = lex::lex(in)?;
	if (t is lex::rparen) return void;
	let first = _parse(in, t)?;
	let rest = parselist(in)?;
	return alloc((first, rest));
};

fn _parse(in: io::handle, t: lex::token) (expr::expr | error) = match (t) {
case lex::lparen =>
	return parselist(in);
case let s: lex::symbol =>
	if (s == "param") return ("foo","bar"): expr::param;
	return s: expr::symbol;
case let s: str =>
	return s: expr::expr;
case =>
	return t: unexpected;
};

export fn parse(in: io::handle) (expr::expr | error) = _parse(in, lex::lex(in)?);

A  => unparse/expr.ha +34 -0
@@ 1,34 @@
use io;
use fmt;
use expr;

export fn expr(e: expr::expr, o: io::handle) (void | io::error) = match (e) {
case let s: str =>
	//TODO escape the string, ugh
	fmt::fprintf(o, "\"{}\"", s)?;
case let s: expr::symbol =>
	fmt::fprint(o, s)?;
case let p: expr::param =>
	//TODO escape the string, ugh
	fmt::fprintf(o, "(@ {} \"{}\")", p.0, p.1)?;
case let c: *expr::cons =>
	fmt::fprint(o, '(')?;
	for (true) {
		expr(c.0, o)?;
		match (c.1) {
		case void =>
			break;
		case let tail: *expr::cons =>
			c = tail;
			fmt::fprint(o, ' ')?;
			continue;
		case => void;
		};
		fmt::fprint(o, " . ")?;
		expr(c.1, o)?;
		break;
	};
	fmt::fprint(o, ')')?;
case void =>
	fmt::fprint(o, "()")?;
};

A  => unparse/html.ha +34 -0
@@ 1,34 @@
use io;
use fmt;
use expr;

export fn html(e: expr::expr, o: io::handle) (void | io::error) = match (e) {
case let s: str =>
	//TODO escape the string, ugh, but html flavour this time
	fmt::fprint(o, s)?;
case let s: expr::symbol =>
	fmt::fprint(o, s)?;
case let p: expr::param =>
	fmt::fprintf(o, "{}=\"{}\"", p.0, p.1)?;
case let c: *expr::cons =>
	fmt::fprint(o, '<')?;
	html(c.0, o)?;
	let params = true;
	let head = c.1 as *expr::cons;
	for (true) {
		if (params && !(head.0 is expr::param)) {
			fmt::fprint(o, '>')?;
			params = false;
		};
		if (params) fmt::fprint(o, ' ')?;
		html(head.0, o)?;
		let tail = head.1;
		if (tail is void) break;
		head = tail as *expr::cons;
	};
	fmt::fprint(o, "</")?;
	html(c.0, o)?;
	fmt::fprint(o, '>')?;
case void =>
	fmt::fprint(o, "void")?;
};