Commit 7e7279a2 authored by Jeff Smits's avatar Jeff Smits

WIP writing SSL primitives

parent a692bebd
......@@ -5,6 +5,7 @@ dependencies = [
"aterm 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"ref_eq 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt-derive 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -50,7 +51,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
version = "2.23.2"
version = "2.23.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -174,7 +175,7 @@ name = "structopt"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clap 2.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.23.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
......@@ -183,12 +184,12 @@ version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.11.10"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -291,7 +292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum aterm 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4be6658304081c33ce6f15bfb1a6c1cf0589ab87bc95624a9f5ad5a2bda618c6"
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
"checksum clap 2.23.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf1114886d7cde2d6448517161d7db8d681a9a1c09f7d210f0b0864e48195f6"
"checksum clap 2.23.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f57e9b63057a545ad2ecd773ea61e49422ed1b1d63d74d5da5ecaee55b3396cd"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
"checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135"
......@@ -309,7 +310,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
"checksum structopt 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2d52740003d84335ccfc6f9223a9cede3943e5f1e75b15b21a9cd40d02f1d6df"
"checksum structopt-derive 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4f5f4da823bbf745599cd6e6c344817e2580cf2ea46d012aa62da714a20725"
"checksum syn 0.11.10 (registry+https://github.com/rust-lang/crates.io-index)" = "171b739972d9a1bfb169e8077238b51f9ebeaae4ff6e08072f7ba386a8802da2"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
"checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a"
......
......@@ -15,6 +15,7 @@ phf_codegen = "0.7.21"
[dependencies]
aterm = "0.11.0"
phf = "0.7.21"
rand = "0.3.15"
ref_eq = "1.0.0"
structopt = "0.0.3"
structopt-derive = "0.0.3"
......
......@@ -4,60 +4,148 @@ use std::env;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
use std::ascii::AsciiExt;
fn main() {
let path = Path::new(&env::var("OUT_DIR").unwrap()).join("codegen.rs");
let mut file = BufWriter::new(File::create(&path).unwrap());
write!(&mut file,
"pub static LIBS: phf::Map<&'static str, \
(&'static str, Option<&'static Primitives<'a, B, F, A>>)> =\n")
.unwrap();
phf_codegen::Map::new()
.entry("java-front", r#"("java_front/libjava-front.ctree", None)"#)
.entry("libstratego-aterm",
r#"("libstratego-aterm/libstratego-aterm.ctree", None)"#)
.entry("libstratego-gpp",
r#"("libstratego-gpp/libstratego-gpp.ctree", Some(&SSL))"#)
.entry("libstratego-lib",
r#"("libstratego-lib/libstratego-lib-posix-xsi.ctree", Some(&SSL))"#)
.entry("libstratego-rtg",
r#"("libstratego-rtg/libstratego-rtg.ctree", None)"#)
.entry("libstratego-sdf",
r#"("libstratego-sdf/libstratego-sdf.ctree", None)"#)
.entry("libstratego-sglr",
r#"("libstratego-sglr/libstratego-sglr.ctree", Some(&JSGLR))"#)
.entry("libstratego-tool-doc",
r#"("libstratego-tool-doc/libstratego-tool-doc.ctree", None)"#)
.entry("libstratego-xtc",
r#"("libstratego-xtc/libstratego-xtc.ctree", Some(&SSL))"#)
.entry("libstrc", r#"("libstrc/libstrc.ctree", Some(&STRC))"#)
.build(&mut file)
.unwrap();
write!(&mut file, ";\n").unwrap();
write!(&mut file,
"static SSL: phf::Map<&'static str, Primitive<'a, B, F, A>> =\n")
let libs =
[("java-front", r#"("java_front/libjava-front.ctree", None)"#),
("libstratego-aterm", r#"("libstratego-aterm/libstratego-aterm.ctree", None)"#),
("libstratego-gpp", r#"("libstratego-gpp/libstratego-gpp.ctree", Some(&SSL))"#),
("libstratego-lib", r#"("libstratego-lib/libstratego-lib-posix-xsi.ctree", Some(&SSL))"#),
("libstratego-rtg", r#"("libstratego-rtg/libstratego-rtg.ctree", None)"#),
("libstratego-sdf", r#"("libstratego-sdf/libstratego-sdf.ctree", None)"#),
("libstratego-sglr", r#"("libstratego-sglr/libstratego-sglr.ctree", Some(&JSGLR))"#),
("libstratego-tool-doc", r#"("libstratego-tool-doc/libstratego-tool-doc.ctree", None)"#),
("libstratego-xtc", r#"("libstratego-xtc/libstratego-xtc.ctree", Some(&SSL))"#),
("libstrc", r#"("libstrc/libstrc.ctree", Some(&STRC))"#)];
let ssl = ["is_int", "is_real", "addi", "addr", "iori", "xori", "andi", "shli", "shri"];
let jsglr = [];
let strc = [];
writeln!(&mut file,
"pub type Primitives = phf::Map<&'static str, (Libs, usize)>;")
.unwrap();
phf_codegen::Map::new()
.entry("SSL_is_int", "&ssl::is_int")
.build(&mut file)
.unwrap();
write!(&mut file, ";\n").unwrap();
write!(&mut file,
"static JSGLR: phf::Map<&'static str, Primitive<'a, B, F, A>> =\n")
writeln!(&mut file,
"pub static LIBS: phf::Map<&'static str, \
(&'static str, Option<&'static Primitives>)> =")
.unwrap();
phf_codegen::Map::<&'static str>::new()
.build(&mut file)
.unwrap();
write!(&mut file, ";\n").unwrap();
let mut builder = phf_codegen::Map::new();
for &(name, pair) in &libs {
builder.entry(name, pair);
}
builder.build(&mut file).unwrap();
writeln!(&mut file, ";").unwrap();
write!(&mut file,
"static STRC: phf::Map<&'static str, Primitive<'a, B, F, A>> =\n")
write_primitive_libs(&mut file,
&[("ssl", &ssl), ("jsglr", &jsglr), ("strc", &strc)])
.unwrap();
phf_codegen::Map::<&'static str>::new()
.build(&mut file)
.unwrap();
write!(&mut file, ";\n").unwrap();
}
fn write_primitive_libs(file: &mut BufWriter<File>,
libs: &[(&'static str, &[&'static str])])
-> ::std::io::Result<()> {
writeln!(file,
"#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Libs {{")?;
for &(name, _) in libs {
write!(file, "{},", name.to_ascii_uppercase())?;
}
writeln!(file, "}}")?;
writeln!(file,
"\
pub fn eval_prim_ref<'a, B, F, A>(prim_ref: (Libs, usize),
context: &MutContext<'a, B, F, A>,
sargs: Vec<SDefT>,
targs: Vec<A>,
current: A)
-> Result<A>
where F: 'a + ATermFactory<'a, B, ATermRef = A>,
A: Borrow<<F as ATermFactory<'a, B>>::ATerm> + Clone,
<F as ATermFactory<'a, B>>::ATerm: Eq,
B: 'a
{{
match prim_ref.0 {{\
")?;
for &(name, _) in libs {
write!(file,
"Libs::{} => {}(prim_ref.1, context, sargs, targs, current),",
name.to_ascii_uppercase(),
name)?;
}
writeln!(file,
"\
}}
}}
\
")?;
for &(name, lib) in libs {
write_primitives(file, lib, name)?;
}
Ok(())
}
fn write_primitives(file: &mut BufWriter<File>,
names: &[&'static str],
lib_name: &'static str)
-> ::std::io::Result<()> {
write!(file,
"static {}: Primitives =\n",
lib_name.to_ascii_uppercase())?;
{
let true_names: Vec<String> = names
.iter()
.map(|name| format!("{}_{}", lib_name.to_ascii_uppercase(), name))
.collect();
let mut builder = phf_codegen::Map::<&str>::new();
for (offset, true_name) in true_names.iter().enumerate() {
builder.entry(true_name.as_ref(),
&format!("(Libs::{}, {})", lib_name.to_ascii_uppercase(), offset));
}
builder.build(file).unwrap();
}
write!(file, ";\n")?;
write!(file,
"\
#[inline]
fn {}<'a, B, F, A>(offset: usize,
context: &MutContext<'a, B, F, A>,
sargs: Vec<SDefT>,
targs: Vec<A>,
current: A)
-> Result<A>
where F: 'a + ATermFactory<'a, B, ATermRef = A>,
A: Borrow<<F as ATermFactory<'a, B>>::ATerm> + Clone,
<F as ATermFactory<'a, B>>::ATerm: Eq,
B: 'a
{{
match offset {{
\
",
lib_name)?;
for (offset, &name) in names.iter().enumerate() {
writeln!(file,
" {} => {}::{}(context, sargs, targs, current),",
offset,
lib_name,
name)?;
}
write!(file,
r#" _ => unreachable!("Don't call {} with an self chosen offset!")
}}
}}
"#,
lib_name)
}
......@@ -6,7 +6,7 @@ use std::borrow::Borrow;
use std::marker::PhantomData;
use preprocess::SDefT;
use std::cell::RefCell;
use primitives::Primitives;
use primitives::{Primitives, eval_prim_ref};
/// This context is internally mutable in RefCell fields. This is just for convenience as we don't
/// want to pass around the mutable fields separately. But the internal mutability is not a hidden
......@@ -20,7 +20,7 @@ pub struct MutContext<'a, B, F, A>
pub stack_tracer: RefCell<StackTracer>,
pub scopes: RefCell<Vec<Scope<A>>>,
pub factory: &'a F,
pub primitives: Vec<Primitives<'a, B, F, A>>,
pub primitives: Vec<&'static Primitives>,
b: PhantomData<B>,
}
......@@ -30,16 +30,34 @@ impl<'a, B, F, A> MutContext<'a, B, F, A>
<F as ATermFactory<'a, B>>::ATerm: Eq,
B: 'a
{
pub fn new(factory: &'a F, scopes: Vec<Scope<A>>) -> MutContext<'a, B, F, A> {
pub fn new(factory: &'a F,
scopes: Vec<Scope<A>>,
primitives: Vec<&'static Primitives>)
-> MutContext<'a, B, F, A> {
MutContext {
stack_tracer: RefCell::new(StackTracer::default()),
factory: factory,
primitives: Vec::new(),
primitives: primitives,
scopes: RefCell::new(scopes),
b: PhantomData,
}
}
pub fn call_primitive(&self,
prim_name: &str,
sargs: Vec<SDefT>,
targs: Vec<A>,
current: A)
-> Result<A> {
let prim_ref = self.primitives
.iter()
.flat_map(|prims| prims.get(prim_name))
.cloned() // very cheap, so whatever
.next()
.ok_or_else(|| Error::UndefinedPrimitive(prim_name.to_owned()))?;
eval_prim_ref(prim_ref, self, sargs, targs, current)
}
// Should really return a &SDefT, but I can't figure out lifetimes that borrowck will accept :(
pub fn get_strategy(&self, strat_name: &str) -> Result<SDefT> {
self.scopes
......@@ -161,5 +179,3 @@ impl<ATerm> Scope<ATerm> {
}
}
}
pub type Primitive = ();
use types::{Result, Error};
use aterm::{ATerm, ATermFactory};
use aterm::{ATerm, ATermFactory, Term};
use ctree;
use try_from::TryInto;
use std::borrow::Borrow;
......@@ -18,14 +18,14 @@ macro_rules! eprintln {
}}
}
pub struct Libraries<'a, B, F, A> {
pub struct Libraries<A> {
libs: Vec<A>,
prims: Vec<&'static Primitives<'a, B, F, A>>,
prims: Vec<&'static Primitives>,
}
impl<'a, B, F, A> iter::FromIterator<(A, Option<&'static Primitives<'a, B, F, A>>)> for Libraries<'a, B, F, A> {
impl<A> iter::FromIterator<(A, Option<&'static Primitives>)> for Libraries<A> {
fn from_iter<T>(iter: T) -> Self
where T: IntoIterator<Item = (A, Option<&'static Primitives<'a, B, F, A>>)>
where T: IntoIterator<Item = (A, Option<&'static Primitives>)>
{
let mut libs = Libraries {
libs: Vec::new(),
......@@ -43,7 +43,7 @@ impl<'a, B, F, A> iter::FromIterator<(A, Option<&'static Primitives<'a, B, F, A>
}
}
pub fn interpret_main<'a, B, F, A>(factory: &'a F, program: A, libraries: Libraries<'a, B, F, A>) -> Result<A>
pub fn interpret_main<'a, B, F, A>(factory: &'a F, program: A, libraries: Libraries<A>) -> Result<A>
where F: 'a + ATermFactory<'a, B, ATermRef = A>,
A: Borrow<<F as ATermFactory<'a, B>>::ATerm> + Clone,
<F as ATermFactory<'a, B>>::ATerm: Eq,
......@@ -53,12 +53,12 @@ pub fn interpret_main<'a, B, F, A>(factory: &'a F, program: A, libraries: Librar
program,
libraries,
"main_0_0",
cons_nil_list(factory, iter::once(factory.string("main_0_0".to_owned()))))
to_cons_nil_list(factory, iter::once(factory.string("main_0_0".to_owned()))))
}
pub fn interpret<'a, B, F, A>(factory: &'a F,
program: A,
libraries: Libraries<'a, B, F, A>,
libraries: Libraries<A>,
strategy: &str,
input: A)
-> Result<A>
......@@ -77,7 +77,7 @@ pub fn interpret<'a, B, F, A>(factory: &'a F,
.and_then(preprocess::<'a, B, F, A>)
})
.collect::<Result<_>>()?;
let mut context = MutContext::new(factory, libs);
let context = MutContext::new(factory, libs, libraries.prims);
context
.scopes
.borrow_mut()
......@@ -114,7 +114,6 @@ impl<'a, B, F, A> Eval<'a, B, F, A> for preprocess::Strategy
{
fn eval(&self, context: &MutContext<'a, B, F, A>, current: A) -> Result<A> {
use preprocess::Strategy::*;
use ctree::SVar;
use context;
match *self {
Let(ref defs, ref body) => {
......@@ -197,7 +196,7 @@ fn eval_match<'a, B, F, A>(context: &MutContext<'a, B, F, A>,
.annotations
.into_iter()
.cloned();
let current_annos = &cons_nil_list(context.factory, current_annos);
let current_annos = &to_cons_nil_list(context.factory, current_annos);
eval_match(context, &*annos, current_annos)
}
As(ref v, ref term) => {
......@@ -248,8 +247,8 @@ fn eval_match<'a, B, F, A>(context: &MutContext<'a, B, F, A>,
Term::Application(ref current_cons, ref current_children) => {
let current_cons = &context.factory.string(current_cons.to_owned());
let () = eval_match(context, cons, current_cons)?;
let current_children = &cons_nil_list(context.factory,
current_children.into_iter().cloned());
let current_children = &to_cons_nil_list(context.factory,
current_children.into_iter().cloned());
eval_match(context, children, current_children)
}
_ => Err(Error::StrategyFailed),
......@@ -301,7 +300,7 @@ fn eval_build<'a, B, F, A>(context: &MutContext<'a, B, F, A>,
// Which the STR/J does when interpreting, but the compiled version just does the
// same as the List case: give back the children
Application(_, _) |
List(_) => return Ok(cons_nil_list(context.factory, children.iter().cloned())),
List(_) => return Ok(to_cons_nil_list(context.factory, children.iter().cloned())),
Blob(_) | Placeholder(_) => {
return Err(Error::UnknownBehaviour("Blob or ATerm placeholder in build of \
explode pattern (#) first argument"))
......@@ -544,13 +543,13 @@ fn all_rec<'a, B, F, A>(r: &Box<[A]>,
.map(Vec::into_boxed_slice)
}
fn cons_nil_list<'a, B, F, A, I>(f: &'a F, value: I) -> A
pub fn to_cons_nil_list<'a, B, F, A, I>(f: &'a F, value: I) -> A
where I: IntoIterator<Item = A>,
<I as iter::IntoIterator>::IntoIter: iter::DoubleEndedIterator,
F: 'a + ATermFactory<'a, B, ATermRef = A>,
A: Borrow<<F as ATermFactory<'a, B>>::ATerm> + Clone,
<F as ATermFactory<'a, B>>::ATerm: Eq,
B: 'a + Clone
B: 'a
{
value
.into_iter()
......@@ -560,3 +559,24 @@ fn cons_nil_list<'a, B, F, A, I>(f: &'a F, value: I) -> A
f.application("Cons".to_owned(), iter::once(term).chain(iter::once(list)))
})
}
pub fn from_cons_nil_list<'a, B, F, A>(f: &'a F, list: A) -> Result<Vec<A>>
where F: 'a + ATermFactory<'a, B, ATermRef = A>,
A: Borrow<<F as ATermFactory<'a, B>>::ATerm> + Clone,
<F as ATermFactory<'a, B>>::ATerm: Eq,
B: 'a
{
let mut result = Vec::new();
let mut list_term = &list.borrow().as_inner().term;
while let &Term::Application(ref c, ref r) = list_term {
match (c.as_ref(), r.len()) {
("Nil", 0) => return Ok(result),
("Cons", 2) => {
result.push(r[0].clone());
list_term = &r[1].borrow().as_inner().term;
}
_ => return Err(Error::UnknownBehaviour("TypeError: expected List")),
}
}
Err(Error::UnknownBehaviour("TypeError: expected List"))
}
......@@ -5,6 +5,7 @@ extern crate aterm;
extern crate try_from;
extern crate phf;
extern crate ref_eq;
extern crate rand;
use structopt::StructOpt;
......@@ -94,10 +95,10 @@ fn read_aterm<'a>(f: &ATermFactory<()>,
}
#[cfg_attr(rustfmt, rustfmt_skip)]
fn read_lib<'a, B, F, A>(f: &ATermFactory<()>,
fn read_lib<'a>(f: &ATermFactory<()>,
path: &str)
-> Result<(<ATermFactory<()> as aterm::ATermFactory<'a, ()>>::ATermRef,
Option<&'static Primitives<'a, B, F, A>>)>
Option<&'static Primitives>)>
{
if let Some(&(path, prim)) = primitives::LIBS.get(path) {
Ok((read_aterm(f, path)?, prim))
......
......@@ -2,11 +2,9 @@ use phf;
use context::MutContext;
use preprocess::SDefT;
use types::Result;
use aterm::ATermFactory;
use std::borrow::Borrow;
pub mod ssl;
pub type Primitive<'a, B, F, A> = &'static Fn(&MutContext<'a, B, F, A>, Vec<SDefT>, Vec<A>, A) -> Result<A>;
pub type Primitives<'a, B, F, A> = phf::Map<&'static str, Primitive<'a, B, F, A>>;
include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
This diff is collapsed.
......@@ -15,6 +15,7 @@ pub enum Error {
CTreeParse(&'static str),
UndefinedStrategy(String),
UndefinedVariable(String),
UndefinedPrimitive(String),
StrategyFailed,
UnknownBehaviour(&'static str),
}
......@@ -28,8 +29,9 @@ impl<'t> error::Error for Error {
Error::CTreeParse(_) => "couldn't parse ctree",
Error::UndefinedStrategy(_) => "couldn't find strategy",
Error::UndefinedVariable(_) => "couldn't find variable",
Error::UndefinedPrimitive(_) => "couldn't find primitive",
Error::StrategyFailed => "strategy failed",
Error::UnknownBehaviour(ref s) => "unknown behaviour",
Error::UnknownBehaviour(_) => "unknown behaviour",
}
}
......@@ -41,6 +43,7 @@ impl<'t> error::Error for Error {
Error::CTreeParse(_) => None,
Error::UndefinedStrategy(_) => None,
Error::UndefinedVariable(_) => None,
Error::UndefinedPrimitive(_) => None,
Error::StrategyFailed => None,
Error::UnknownBehaviour(_) => None,
}
......@@ -56,6 +59,7 @@ impl<'t> fmt::Display for Error {
Error::CTreeParse(ref s) => write!(f, "CTreeParseError in {}", s),
Error::UndefinedStrategy(ref s) => write!(f, "UndefinedStrategyError: {}", s),
Error::UndefinedVariable(ref s) => write!(f, "UndefinedVariableError: {}", s),
Error::UndefinedPrimitive(ref s) => write!(f, "UndefinedPrimitiveError: {}", s),
Error::StrategyFailed => write!(f, "Strategy failed"),
Error::UnknownBehaviour(ref s) => write!(f, "Unknown behaviour for {}", s),
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment