A => .gitignore +1 -0
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")?;
+};