Skip to content

Commit

Permalink
Merge pull request #17 from justinpombrio/demo-ed-concat-lang
Browse files Browse the repository at this point in the history
Draft concatenative language for demo editor
  • Loading branch information
justinpombrio authored Jul 21, 2019
2 parents 157aa99 + d844ca3 commit fc24a6a
Show file tree
Hide file tree
Showing 21 changed files with 1,340 additions and 377 deletions.
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "synless"
version = "0.0.1"
authors = ["justinpombrio <[email protected]>"]
authors = ["justinpombrio <[email protected]>", "e-matteson <[email protected]>"]

[workspace]
members = [
Expand All @@ -10,11 +10,13 @@ members = [
"pretty",
"language",
"frontends",
"editor"]
"editor",
"demo"]
default-members = [
"utility",
"forest",
"pretty",
"language",
"frontends",
"editor"]
"editor",
"demo"]
15 changes: 15 additions & 0 deletions demo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "demo"
version = "0.1.0"
authors = ["e-matteson <[email protected]>"]
edition = "2018"

[dependencies]
editor = { path = "../editor" }
forest = { path = "../forest" }
pretty = { path = "../pretty" }
language = { path = "../language" }
frontends = { path = "../frontends"}
utility = { path = "../utility"}
lazy_static = "*"
termion = "1.5"
36 changes: 36 additions & 0 deletions demo/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use std::io;
use termion::event::Key;

use frontends::terminal;
use language::{ConstructName, LanguageName};

#[derive(Debug)]
pub enum Error {
UnknownKey(Key),
UnknownKeymap(String),
NoKeymap,
UnknownEvent,
KeyboardInterrupt,
UnknownLang(LanguageName),
UnknownConstruct {
construct: ConstructName,
lang: LanguageName,
},
ExpectedWord(String),
EmptyStack,
DocExec(String),
Io(io::Error),
Term(terminal::Error),
}

impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error::Io(e)
}
}

impl From<terminal::Error> for Error {
fn from(e: terminal::Error) -> Error {
Error::Term(e)
}
}
212 changes: 212 additions & 0 deletions demo/src/keymap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
use std::collections::HashMap;
use std::iter;
use termion::event::Key;

use crate::prog::{Prog, Word};
use language::{Language, LanguageName};

pub struct Keymap<'l>(pub HashMap<Key, Prog<'l>>);

impl<'l> Keymap<'l> {
pub fn node(lang: &Language) -> Self {
Keymap(
lang.keymap()
.iter()
.map(|(&ch, construct_name)| {
(
Key::Char(ch),
Prog::named(
construct_name,
&[
// Push the new node onto the stack, and then
// apply the quoted command (eg. InsertAfter)
// that was already on the top of the stack.
Word::LangConstruct(lang.name().into(), construct_name.to_owned()),
Word::NodeByName,
Word::Swap,
Word::Apply,
Word::PopMap,
],
),
)
})
.chain(iter::once((
Key::Esc,
Prog::named(
"Cancel",
&[
Word::Pop, // get rid of the quoted command on the stack
Word::PopMap,
],
),
)))
.collect(),
)
}

pub fn tree() -> Self {
let map = vec![
(Key::Char('d'), Prog::single(Word::Cut)),
(Key::Char('y'), Prog::single(Word::Copy)),
(Key::Char('p'), Prog::single(Word::PasteAfter)),
(Key::Char('P'), Prog::single(Word::PasteBefore)),
(Key::Ctrl('p'), Prog::single(Word::PastePrepend)),
(Key::Alt('p'), Prog::single(Word::PastePostpend)),
(Key::Char('R'), Prog::single(Word::PasteReplace)),
(
Key::Char('a'),
Prog::named("TypeA", &[Word::Char('a'), Word::InsertChar]),
),
(Key::Char('u'), Prog::single(Word::Undo)),
(Key::Ctrl('r'), Prog::single(Word::Redo)),
(Key::Right, Prog::single(Word::Right)),
(Key::Left, Prog::single(Word::Left)),
(Key::Up, Prog::single(Word::Parent)),
(Key::Backspace, Prog::single(Word::Remove)),
(
Key::Down,
Prog::named("Child", &[Word::Usize(0), Word::Child]),
),
(
Key::Char('i'),
Prog::named(
"InsertAfter",
&[
Word::InsertAfter.quote(),
Word::MapName("node".into()),
Word::PushMap,
],
),
),
(
Key::Char('I'),
Prog::named(
"InsertBefore",
&[
Word::InsertBefore.quote(),
Word::MapName("node".into()),
Word::PushMap,
],
),
),
(
Key::Char('o'),
Prog::named(
"InsertPostpend",
&[
Word::InsertPostpend.quote(),
Word::MapName("node".into()),
Word::PushMap,
],
),
),
(
Key::Char('O'),
Prog::named(
"InsertPrepend",
&[
Word::InsertPrepend.quote(),
Word::MapName("node".into()),
Word::PushMap,
],
),
),
(
Key::Char('r'),
Prog::named(
"Replace",
&[
Word::Replace.quote(),
Word::MapName("node".into()),
Word::PushMap,
],
),
),
(
Key::Char(' '),
Prog::named(
"SpeedBoolMode",
&[Word::MapName("speed_bool".into()), Word::PushMap],
),
),
]
.into_iter()
.collect();

Keymap(map)
}

pub fn speed_bool() -> Self {
let lang: LanguageName = "json".into();
let map = vec![
(
Key::Char('t'),
Prog::named(
"True",
&[
Word::LangConstruct(lang.clone(), "true".into()),
Word::NodeByName,
Word::InsertAfter,
],
),
),
(
Key::Char('f'),
Prog::named(
"False",
&[
Word::LangConstruct(lang, "false".into()),
Word::NodeByName,
Word::InsertAfter,
],
),
),
(Key::Esc, Prog::named("Exit", &[Word::PopMap])),
]
.into_iter()
.collect();

Keymap(map)
}

pub fn summary(&self) -> String {
let mut s = String::new();
for (key, prog) in &self.0 {
let prog_name = if let Some(ref name) = prog.name {
name.to_string()
} else if prog.words.len() == 1 {
format!("{:?}", prog.words[0])
} else {
"...".into()
};
s += &format!("{}:{}, ", self.format_key(key), prog_name);
}
s
}

fn format_key(&self, key: &Key) -> String {
match key {
Key::Backspace => "Bksp".to_string(),
Key::Left => "←".to_string(),
Key::Right => "→".to_string(),
Key::Up => "↑".to_string(),
Key::Down => "↓".to_string(),
Key::Home => "Home".to_string(),
Key::End => "End".to_string(),
Key::PageUp => "PgUp".to_string(),
Key::PageDown => "PgDn".to_string(),
Key::Delete => "Del".to_string(),
Key::Insert => "Ins".to_string(),
Key::F(num) => format!("F{}", num),
Key::Char(' ') => "Spc".to_string(),
Key::Char(c) => c.to_string(),
Key::Alt(' ') => "A-Spc".to_string(),
Key::Alt(c) => format!("A-{}", c),
Key::Ctrl(' ') => "C-Spc".to_string(),
Key::Ctrl(c) => format!("C-{}", c),
Key::Null => "Null".to_string(),
Key::Esc => "Esc".to_string(),
_ => "(unknown)".to_string(),
}
}
}
70 changes: 70 additions & 0 deletions demo/src/keymap_lang.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use crate::NotationSet;
use language::{Arity, Construct, Language};
use pretty::{child, literal, no_wrap, repeat, text, Notation, Repeat, Style};

pub fn make_keymap_lang() -> (Language, NotationSet) {
let notations = vec![
("key".into(), key()),
("prog".into(), prog()),
("entry".into(), entry()),
("dict".into(), dict()),
];
let constructs = vec![
Construct::new("key", "Key", Arity::Text, Some('k')),
Construct::new("prog", "Value", Arity::Text, Some('p')),
Construct::new(
"dict",
"Dict",
Arity::Flexible("Entry".to_string()),
Some('d'),
),
Construct::new(
"entry",
"Entry",
Arity::Fixed(vec!["Key".to_string(), "Value".to_string()]),
Some('e'),
),
];
// TODO: some of this boilerplate should get abstracted out
let mut lang = Language::new("keymap");
for construct in constructs {
lang.add(construct);
}
let note_set = NotationSet::new(&lang, notations);
(lang, note_set)
}

fn key() -> Notation {
let style = Style::plain();
text(style)
}

fn prog() -> Notation {
let style = Style::plain();
text(style)
}

/// Try putting the key and value on the same line.
/// If they don't fit, wrap after the colon, and indent the value.
fn entry() -> Notation {
no_wrap(child(0) + punct(": ") + child(1)) | (child(0) + punct(":") ^ indent() + child(1))
}

/// Put all entries on separate lines.
/// If there is more than one entry, put the opening and closing delimiters on separate lines too.
fn dict() -> Notation {
repeat(Repeat {
empty: punct("{}"),
lone: punct("{") + child(0) + punct("}"),
join: child(0) + punct(",") ^ child(1),
surround: punct("{") ^ indent() + child(0) ^ punct("}"),
})
}

fn punct(text: &str) -> Notation {
literal(text, Style::plain())
}

fn indent() -> Notation {
literal(" ", Style::plain())
}
Loading

0 comments on commit fc24a6a

Please sign in to comment.