Skip to content

Instantly share code, notes, and snippets.

@tiffany352
Last active December 22, 2015 01:48
Show Gist options
  • Save tiffany352/6398426 to your computer and use it in GitHub Desktop.
Save tiffany352/6398426 to your computer and use it in GitHub Desktop.
#[ link(name = "frscript",
vers = "0.0",
uuid = "62527026-e47c-44ce-be34-7137e8194772") ];
#[ desc = "FRP Scripting Language" ];
#[ license = "Zlib/libpng" ];
#[ author = "tiffany" ];
#[ crate_type="lib" ];
pub mod parse;
pub mod grammar;
extern mod frscript;
use frscript::parse::*;
use frscript::grammar::*;
use std::io::*;
fn main() {
let mut ctx = grammar();
//let rule = Rule("digit");
loop {
print("= ");
let line = stdin().read_line();
match line {
~"quit" => return,
~"exit" => return,
_ => {
let res = parse(&ctx, ctx.grammar.get(& &"digit"), line.clone(), 0);
println(fmt!("%?", res));
}
}
}
}
use parse::*;
use std::float;
pub enum FRToken {
Unparsed(~str),
Label(~str),
String(~str),
Number(float),
SExpr
}
fn make_token<'a>(s: ~str) -> FRToken {
Unparsed(s)
}
fn make_number<'a>(s: ~str) -> Option<FRToken> {
match float::from_str(s) {
Some(x) => Some(Number(x)),
None => None
}
}
fn grammar() -> ParseContext<FRToken> {
let mut ctx = ParseContext::new(make_token);
ctx.rule("space", Set(" \t\n".iter().collect()));
ctx.rule("ws", More(~Rule("space")));
ctx.rule("sws", MoreThan(1, ~Rule("space")));
ctx.rule("digit", Range('0','9'));
ctx.rule("digits", MoreThan(1, ~Rule("digit")));
ctx.rule("alpha", Or(~[Range('a','z'), Range('A','Z')]));
ctx.rule("number", Build(~Seq(~[LessThan(1, ~Literal("-")), Rule("digits"), LessThan(1, ~Seq(~[Literal("."), Rule("digits")])), LessThan(1, ~Seq(~[Set("eE".iter().collect()), LessThan(1, ~Literal("-")), Rule("digits")]))]), make_number));
ctx
}
use std::str::*;
use std::vec::*;
use std::hashmap::*;
pub enum Pattern<'self, T> {
// reference into a hash table containing the pattern
Rule(&'self str),
// matches and consumes
Literal(&'self str),
Range(char, char),
Chars(uint),
Set(~[char]),
More(~Pattern<'self, T>),
MoreThan(uint, ~Pattern<'self, T>),
Exactly(uint, ~Pattern<'self, T>),
LessThan(uint, ~Pattern<'self, T>),
Seq(~[Pattern<'self, T>]),
Or(~[Pattern<'self, T>]),
Diff(~Pattern<'self, T>, ~Pattern<'self, T>),
//Match(~Pattern<'self, T>, ~fn(&Pattern<'self, T>, &Token<'self, T>) -> uint),
// matches but doesn't consume
And(~Pattern<'self, T>),
Always,
// controls visibility
Merge(~Pattern<'self, T>),
Select(uint, ~Pattern<'self, T>),
Var(~Pattern<'self, T>),
Ref(~Pattern<'self, T>),
//Fold(~Pattern<'self, T>, ~fn(&'self Pattern<'self, T>, Token<'self, T>) -> Option<Token<'self, T>>),
Build(~Pattern<'self, T>, ~fn(~str) -> Option<T>)
}
pub struct Token<'self, T> {
children: ~[Token<'self, T>],
value: T,
pat: &'self Pattern<'self, T>,
start: uint,
end: uint
}
pub struct ParseContext<'self, T> {
grammar: HashMap<&'self str, Pattern<'self, T>>,
variables: HashMap<~str, Token<'self, T>>,
make_token: ~fn(~str) -> T
}
impl<'self, T> ParseContext<'self, T> {
pub fn new(make_token: ~fn(~str) -> T) -> ParseContext<'self, T> {
ParseContext {grammar: HashMap::new(), make_token: make_token, variables: HashMap::new()}
}
pub fn rule(&mut self, name: &'self str, rule: Pattern<'self, T>) {
self.grammar.insert(name, rule);
}
}
pub fn parse<'a, 'b, T>(ctx: &'a ParseContext<'a, T>, pat: &'a Pattern<'a, T>, text: &'b str, position: uint) -> Option<Token<'b, T>> {
let tok = |children, start, end| {
Some(Token {children: children, value: (ctx.make_token)(text.slice(start, end).to_owned()), pat: pat, start: start+position, end: end+position})
};
match *pat {
Rule(name) => parse(ctx, ctx.grammar.get(&name), text, position),
Literal(s) => {
if text.len() >= s.len() && text.slice_to(s.len()) == s {
tok(~[], 0, s.len())
} else {
None
}
}
Range(x, y) => {
if text.char_len() < 1 {
return None
}
let CharRange{ch, next} = text.char_range_at(0);
if ch <= y && ch >= x {
tok(~[], 0, next)
} else {
None
}
}
Chars(n) => {
if text.len() >= n {
tok(~[], 0, n)
} else {
None
}
}
Set(ref arr) => {
if text.char_len() < 1 {
return None
}
let CharRange{ch, next} = text.char_range_at(0);
for elem in arr.iter() {
if *elem == ch {
return tok(~[], 0, next)
}
}
None
}
More(ref p) => {
let mut acc = 0;
let mut res = ~[];
loop {
match parse(ctx, *p, text.slice_from(acc), acc + position) {
Some(x) => {
acc = x.end;
res.push(x);
}
None => break
}
}
tok(res, 0, acc)
}
MoreThan(n, ref p) => {
let mut acc = 0;
let mut res = ~[];
for _ in range(0, n) {
match parse(ctx, *p, text.slice_from(acc), acc + position) {
Some(x) => {
acc = x.end;
res.push(x);
}
None => return None
}
}
loop {
match parse(ctx, *p, text.slice_from(acc), acc + position) {
Some(x) => {
acc = x.end;
res.push(x);
}
None => break
}
}
tok(res, 0, acc)
}
Exactly(n, ref p) => {
let mut acc = 0;
let mut res = ~[];
for _ in range(0, n) {
match parse(ctx, *p, text.slice_from(acc), acc + position) {
Some(x) => {
acc = x.end;
res.push(x);
}
None => return None
}
}
tok(res, 0, acc)
}
LessThan(n, ref p) => {
let mut acc = 0;
let mut res = ~[];
for _ in range(0, n) {
match parse(ctx, *p, text.slice_from(acc), acc + position) {
Some(x) => {
acc = x.end;
res.push(x);
}
None => break
}
}
tok(res, 0, acc)
}
Seq(ref arr) => {
let mut acc = 0;
let mut res = ~[];
for elem in arr.iter() {
match parse(ctx, elem, text.slice_from(acc), position + acc) {
Some(x) => {
acc += x.end - acc;
res.push(x);
}
None => return None
}
}
tok(res, 0, acc)
}
Or(ref arr) => {
for elem in arr.iter() {
match parse(ctx, elem, text, position) {
Some(x) => return Some(x),
None => {}
}
}
None
}
Diff(ref a, ref b) => match parse(ctx, *b, text, position) {
Some(_) => None,
None => parse(ctx, *a, text, position)
},
/*Match(ref p, ref f) => match parse(ctx, *p, text, position) {
Some(x) => {
let res = (*f)(*p, &x);
let xstart = x.start;
tok(~[x], xstart-position, res-position)
}
None => None
},*/
And(ref p) => match parse(ctx, *p, text, position) {
Some(x) => tok(~[x], 0, 0),
None => None
},
Always => tok(~[], 0, 0),
Merge(ref p) => match parse(ctx, *p, text, position) {
Some(x) => tok(~[], x.start-position, x.end-position),
None => None
},
Select(n, ref p) => match parse(ctx, *p, text, position) {
Some(x) => match x.children.len() {
l if l <= n => None,
_ => {
let t = x.children[n];
let start = t.start;
let end = t.end;
tok(~[t], start-position, end-position)
}
},
None => None
},
// var and ref would require adding another parameter to parse()
/*Fold(ref p, ref f) => match parse(ctx, *p, text, position) {
Some(x) => (*f)(*p, x),
None => None
},*/
Build(ref p, ref f) => match parse(ctx, *p, text, position) {
Some(x) => match (*f)(text.slice(x.start-position, x.end-position).to_owned()) {
Some(v) => {
let s = x.start;
let e = x.end;
Some(Token {children: ~[x], value: v, pat: pat, start: s, end: e})
}
None => None
},
None => None
},
_ => fail!("NYI")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment