Skip to content

Instantly share code, notes, and snippets.

@takeshik
Created April 5, 2013 00:50
Show Gist options
  • Save takeshik/5315771 to your computer and use it in GitHub Desktop.
Save takeshik/5315771 to your computer and use it in GitHub Desktop.
// Yacq
private static readonly Grammar _standard = new Grammar();
public static Grammar Standard
{
get
{
return _standard;
}
}
static Grammar()
{
var newline = Combinator.Choice(
Chars.Sequence("\r\n"),
Chars.OneOf('\r', '\n', '\x85', '\u2028', '\u2029')
.Select(EnumerableEx.Return)
).Select(_ => Environment.NewLine);
var punctuation = Chars.OneOf('"', '\'', '(', ')', ',', '.', ':', ';', '[', ']', '`', '{', '}');
Parser<char, YacqExpression> expressionRef = null;
var expression = new Lazy<Parser<Char, YacqExpression>>(
() => stream => expressionRef(stream)
);
_standard.Add("yacq", "expression", g => expression.Value);
// Comments
{
_standard.Add("comment", "eol", g => Prims.Pipe(
';'.Satisfy(),
newline.Not().Right(Chars.Any()).Many(),
newline.Ignore().Or(Chars.Eof()),
(p, r, s) => (YacqExpression) YacqExpression.Ignore()
));
Parser<char, YacqExpression> blockCommentRef = null;
Parser<char, YacqExpression> blockCommentRestRef = null;
var blockComment = new Lazy<Parser<Char, YacqExpression>>(
() => stream => blockCommentRef(stream)
);
var blockCommentRest = new Lazy<Parser<Char, YacqExpression>>(
() => stream => blockCommentRestRef(stream)
);
var blockCommentPrefix = Chars.Sequence("#|");
var blockCommentSuffix = Chars.Sequence("|#");
blockCommentRef = blockCommentPrefix
.Right(blockCommentRest.Value.Many())
.Left(blockCommentSuffix)
.Select(_ => (YacqExpression) YacqExpression.Ignore());
blockCommentRestRef = blockCommentPrefix.Not()
.Right(blockCommentSuffix.Not())
.Right(Chars.Any())
.Select(_ => (YacqExpression) YacqExpression.Ignore())
.Or(blockComment.Value);
_standard.Add("comment", "block", g => blockComment.Value);
_standard.Add("comment", "expression", g => Prims.Pipe(
Chars.Sequence("#;"),
g["yacq", "expression"],
(p, r) => (YacqExpression) YacqExpression.Ignore()
));
_standard.Add("yacq", "comment", g => Combinator.Choice(g["comment"]));
}
var ignore = Combinator.Choice(
_standard.Get["yacq", "comment"].Ignore(),
Chars.Space().Ignore(),
newline.Ignore()
).Many();
// Texts
_standard.Add("term", "text", g => Span(
Chars.OneOf('\'', '\"', '`')
.SelectMany(q => q.Satisfy()
.Not()
.Right('\\'.Satisfy()
.Right(q.Satisfy())
.Or(Chars.Any())
)
.Many()
.Select(cs => cs.StartWith(q).EndWith(q))
)
.Select(cs => (YacqExpression) YacqExpression.Text(new String(cs.ToArray()))),
(start, end, t) => t.SetPosition(start, end)
));
// Numbers
{
var numberPrefix = Chars.OneOf('+', '-');
var numberSuffix = Combinator.Choice(
Chars.Sequence("ul"),
Chars.Sequence("UL"),
Chars.OneOf('D', 'F', 'L', 'M', 'U', 'd', 'f', 'l', 'm', 'u')
.Select(EnumerableEx.Return)
);
var digit = '_'.Satisfy().Many().Right(Chars.Digit());
var hexPrefix = Chars.Sequence("0x");
var hex = '_'.Satisfy().Many().Right(Chars.Hex());
var octPrefix = Chars.Sequence("0o");
var oct = '_'.Satisfy().Many().Right(Chars.Oct());
var binPrefix = Chars.Sequence("0b");
var bin = '_'.Satisfy().Many().Right(Chars.OneOf('0', '1'));
var fraction = Prims.Pipe(
'.'.Satisfy(),
digit.Many(1),
(d, ds) => ds.StartWith(d)
);
var exponent = Prims.Pipe(
Chars.OneOf('E', 'e'),
Chars.OneOf('+', '-').Maybe(),
digit.Many(1),
(e, s, ds) => ds
.If(_ => s.Exists(), _ => _.StartWith(s.Perform()))
.StartWith(e)
);
_standard.Add("term", "number", g => Combinator.Choice(
Span(Prims.Pipe(
binPrefix,
bin.Many(1),
numberSuffix.Maybe(),
(p, n, s) => (YacqExpression) YacqExpression.Number(
new String(p.Concat(n).If(
_ => s.Exists(),
cs => cs.Concat(s.Perform())
).ToArray())
)
), (start, end, t) => t.SetPosition(start, end)),
Span(Prims.Pipe(
octPrefix,
oct.Many(1),
numberSuffix.Maybe(),
(p, n, s) => (YacqExpression) YacqExpression.Number(
new String(p.Concat(n).If(
_ => s.Exists(),
cs => cs.Concat(s.Perform())
).ToArray())
)
), (start, end, t) => t.SetPosition(start, end)),
Span(Prims.Pipe(
hexPrefix,
hex.Many(1),
numberSuffix.Maybe(),
(p, n, s) => (YacqExpression) YacqExpression.Number(
new String(p.Concat(n).If(
_ => s.Exists(),
cs => cs.Concat(s.Perform())
).ToArray())
)
), (start, end, t) => t.SetPosition(start, end)),
Span(
from u in numberPrefix.Maybe()
from w in digit.Many(1).Select(_ => new String(_.ToArray()))
from x in fraction.Maybe().Select(_ => _.Otherwise(() => ""))
from y in exponent.Maybe().Select(_ => _.Otherwise(() => ""))
from z in numberSuffix.Maybe().Select(_ => _.Otherwise(() => ""))
select u.Select(t => (YacqExpression) YacqExpression.Number(String.Concat(t, w, x, y, z)))
.Otherwise(() => (YacqExpression) YacqExpression.Number(String.Concat(w, x, y, z))),
(start, end, t) => t.SetPosition(start, end)
)
));
}
// Lists
_standard.Add("term", "list", g => Span(
g["yacq", "expression"]
.Between(ignore, ignore)
.Many()
.Between('('.Satisfy(), ')'.Satisfy())
.Select(es => (YacqExpression) YacqExpression.List(es)),
(start, end, t) => t.SetPosition(start, end)
));
// Vectors
_standard.Add("term", "vector", g => Span(
g["yacq", "expression"]
.Between(ignore, ignore)
.Many()
.Between('['.Satisfy(), ']'.Satisfy())
.Select(es => (YacqExpression) YacqExpression.Vector(es)),
(start, end, t) => t.SetPosition(start, end)
));
// Lambda Lists
_standard.Add("term", "lambdaList", g => Span(
g["yacq", "expression"]
.Between(ignore, ignore)
.Many()
.Between('{'.Satisfy(), '}'.Satisfy())
.Select(es => (YacqExpression) YacqExpression.LambdaList(es)),
(start, end, t) => t.SetPosition(start, end)
));
// Quotes
_standard.Add("term", "quote", g => Span(
Prims.Pipe(
Chars.Sequence("#'"),
g["yacq", "expression"],
(p, e) => (YacqExpression) YacqExpression.List(YacqExpression.Identifier("quote"), e)
),
(start, end, t) => t.SetPosition(start, end)
));
// Quasiquotes
_standard.Add("term", "quasiquote", g => Span(
Prims.Pipe(
Chars.Sequence("#`"),
g["yacq", "expression"],
(p, e) => (YacqExpression) YacqExpression.List(YacqExpression.Identifier("quasiquote"), e)
),
(start, end, t) => t.SetPosition(start, end)
));
// Unquote-Splicings
_standard.Add("term", "unquoteSplicing", g => Span(
Prims.Pipe(
Chars.Sequence("#,@"),
g["yacq", "expression"],
(p, e) => (YacqExpression) YacqExpression.List(YacqExpression.Identifier("unquote-splicing"), e)
),
(start, end, t) => t.SetPosition(start, end)
));
// Unquotes
_standard.Add("term", "unquote", g => Span(
Prims.Pipe(
Chars.Sequence("#,"),
g["yacq", "expression"],
(p, e) => (YacqExpression) YacqExpression.List(YacqExpression.Identifier("unquote"), e)
),
(start, end, t) => t.SetPosition(start, end)
));
// Identifiers
_standard.Add("term", "identifier", g => Combinator.Choice(
Span('.'.Satisfy()
.Many(1)
.Select(cs => (YacqExpression) YacqExpression.Identifier(new String(cs.ToArray()))),
(start, end, t) => t.SetPosition(start, end)
),
Span(':'.Satisfy()
.Many(1)
.Select(cs => (YacqExpression) YacqExpression.Identifier(new String(cs.ToArray()))),
(start, end, t) => t.SetPosition(start, end)
),
Span(Chars.Digit()
.Not()
.Right(Chars.Space()
.Or(punctuation)
.Not()
.Right(Chars.Any())
.Many(1)
)
.Select(cs => (YacqExpression) YacqExpression.Identifier(new String(cs.ToArray()))),
(start, end, t) => t.SetPosition(start, end)
)
));
// Terms
_standard.Add("yacq", "term", g => Combinator.Choice(g["term"]).Between(ignore, ignore));
// Infix Dots
_standard.Add("infix", "dot", g => Prims.Pipe(
g["yacq", "term"],
'.'.Satisfy()
.Right(g["yacq", "term"])
.Many(),
(h, t) => t.Aggregate(h, (l, r) =>
(YacqExpression) YacqExpression.List(YacqExpression.Identifier("."), l, r)
)
));
// Infix Colons
_standard.Add("infix", "colon", g => Prims.Pipe(
g["infix", "dot"],
':'.Satisfy()
.Right(g["infix", "dot"])
.Many(),
(h, t) => t.Aggregate(h, (l, r) =>
(YacqExpression) YacqExpression.List(YacqExpression.Identifier(":"), l, r)
)
));
expressionRef = _standard.Get["infix"].Last();
_standard.Set.Default = g => g["yacq", "expression"];
}
private static Parser<Char, T> Span<T>(Parser<Char, T> parser,
Action<Position, Position, T> action)
{
if (parser == null)
{
throw new ArgumentNullException("parser");
}
if (action == null)
{
throw new ArgumentNullException("action");
}
Parser<Char, Position> pos = stream => Reply.Success(stream, stream.Position);
return from x in pos
from y in parser
from z in pos
select y.Apply(_ => action(x, z, _));
}
// add...
// original quote has priority 600.
Grammar.Standard.Add("term", 550, "quote2", g => Span(
Prims.Pipe(
Chars.Sequence("#?"),
g["yacq", "expression"],
(p, e) => (YacqExpression) YacqExpression.List(YacqExpression.Identifier("quote"), e)
),
(start, end, t) => t.SetPosition(start, end)
));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment