Skip to content

Commit

Permalink
WIP on array input
Browse files Browse the repository at this point in the history
  • Loading branch information
msullivan committed Sep 8, 2022
1 parent 64c0922 commit 228cec1
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 34 deletions.
2 changes: 1 addition & 1 deletion src/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ pub fn main(data: Sender<Input>, control: Receiver<Control>)
}
Err(e) => Err(e)?,
};
match var_type.parse(&text) {
match var_type.parse(&text, true) {
Ok(value) => break (text, value),
Err(e) => {
println!("Bad value: {}", e);
Expand Down
144 changes: 128 additions & 16 deletions src/prompt/variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn no_pos_err<E: fmt::Display>(err: E) -> Error {
}

pub trait VariableInput: fmt::Debug + Send + Sync + 'static {
fn parse(&self, input: &str) -> Result<Value, Error>;
fn parse(&self, input: &str, top: bool) -> Result<Value, Error>;
fn type_name(&self) -> &str;
}

Expand All @@ -38,8 +38,19 @@ pub struct Str;

impl VariableInput for Str {
fn type_name(&self) -> &str { "str" }
fn parse(&self, input: &str) -> Result<Value, Error> {
Ok(Value::Str(input.into()))
fn parse(&self, input: &str, top: bool) -> Result<Value, Error> {
if top {
Ok(Value::Str(input.into()))
} else {
// This is hacky, but use the json parser
// TODO: ... use a real parser for edgedb strings
match serde_json::from_str::<String>(input) {
Err(e) if e.classify() == serde_json::error::Category::Eof
=> Err(Error::Incomplete),
Err(e) => Err(no_pos_err(e)),
Ok(d) => Ok(Value::Str(d)),
}
}
}
}

Expand All @@ -48,7 +59,7 @@ pub struct Uuid;

impl VariableInput for Uuid {
fn type_name(&self) -> &str { "uuid" }
fn parse(&self, input: &str) -> Result<Value, Error> {
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
Ok(Value::Uuid(input.parse().map_err(no_pos_err)?))
}
}
Expand All @@ -58,7 +69,7 @@ pub struct Int16;

impl VariableInput for Int16 {
fn type_name(&self) -> &str { "int16" }
fn parse(&self, input: &str) -> Result<Value, Error> {
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
Ok(Value::Int16(input.parse().map_err(no_pos_err)?))
}
}
Expand All @@ -68,7 +79,7 @@ pub struct Int32;

impl VariableInput for Int32 {
fn type_name(&self) -> &str { "int32" }
fn parse(&self, input: &str) -> Result<Value, Error> {
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
Ok(Value::Int32(input.parse().map_err(no_pos_err)?))
}
}
Expand All @@ -78,7 +89,7 @@ pub struct Int64;

impl VariableInput for Int64 {
fn type_name(&self) -> &str { "int64" }
fn parse(&self, input: &str) -> Result<Value, Error> {
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
Ok(Value::Int64(input.parse().map_err(no_pos_err)?))
}
}
Expand All @@ -88,7 +99,7 @@ pub struct Float32;

impl VariableInput for Float32 {
fn type_name(&self) -> &str { "float32" }
fn parse(&self, input: &str) -> Result<Value, Error> {
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
Ok(Value::Float32(input.parse().map_err(no_pos_err)?))
}
}
Expand All @@ -98,7 +109,7 @@ pub struct Float64;

impl VariableInput for Float64 {
fn type_name(&self) -> &str { "float64" }
fn parse(&self, input: &str) -> Result<Value, Error> {
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
Ok(Value::Float32(input.parse().map_err(no_pos_err)?))
}
}
Expand All @@ -108,7 +119,7 @@ pub struct Bool;

impl VariableInput for Bool {
fn type_name(&self) -> &str { "bool" }
fn parse(&self, input: &str) -> Result<Value, Error> {
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
Ok(Value::Bool(input.parse().map_err(no_pos_err)?))
}
}
Expand All @@ -118,7 +129,7 @@ pub struct BigInt;

impl VariableInput for BigInt {
fn type_name(&self) -> &str { "bigint" }
fn parse(&self, input: &str) -> Result<Value, Error> {
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
let dec: BigDecimal = input.parse().map_err(no_pos_err)?;
let int = dec.to_bigint()
.context("number is not an integer")
Expand All @@ -133,7 +144,7 @@ pub struct Decimal;

impl VariableInput for Decimal {
fn type_name(&self) -> &str { "decimal" }
fn parse(&self, input: &str) -> Result<Value, Error> {
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
let dec: BigDecimal = input.parse().map_err(no_pos_err)?;
let dec = dec.try_into().map_err(no_pos_err)?;
Ok(Value::Decimal(dec))
Expand All @@ -145,7 +156,7 @@ pub struct Json;

impl VariableInput for Json {
fn type_name(&self) -> &str { "json" }
fn parse(&self, input: &str) -> Result<Value, Error> {
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
match serde_json::from_str::<serde_json::Value>(input) {
Err(e) if e.classify() == serde_json::error::Category::Eof
=> Err(Error::Incomplete),
Expand All @@ -155,6 +166,107 @@ impl VariableInput for Json {
}
}

fn partial_parse(var: &dyn VariableInput, input: &str)
-> Result<(Value, usize), Error>
{
let mut end = input.len();
loop {
if end == 0 {
var.parse(input, false)?;
panic!("parse should have failed!");
}
if !input.is_char_boundary(end) {
end -= 1;
continue;
}
match var.parse(&input[.. end], false) {
Ok(val) => { return Ok((val, end)); }
Err(_) => { end -= 1; }
}
}
}

fn skip_whitespace(input: &[u8], mut idx: usize) -> usize {
loop {
if idx == input.len() { break };
let c = char::from_u32(input[idx].into()).unwrap();
if !c.is_ascii_whitespace() { break };
idx += 1;
}
return idx;
}

#[derive(Debug)]
pub struct Array {
pub elem: Arc<dyn VariableInput>,
pub name: String,
}

impl Array {
pub fn new(elem: Arc<dyn VariableInput>) -> Array {
Array {
name: format!("array<{}>", elem.type_name()),
elem: elem,
}
}
}

impl VariableInput for Array {
fn type_name(&self) -> &str { self.name.as_str() }
fn parse(&self, input: &str, _top: bool) -> Result<Value, Error> {
let binput = input.as_bytes();

if binput.get(0) != Some(&b'[') {
return Err(Error::Mistake {
offset: None, description: "array must start with [".to_string()
})
}
let mut idx = 1;
idx = skip_whitespace(binput, idx);
let mut vs = Vec::new();
loop {
match partial_parse(&*self.elem, &input[idx ..]) {
Ok((v, end)) => {
let orig = idx;
vs.push(v);
idx += end;
idx = skip_whitespace(binput, idx);

if binput.get(idx) == Some(&b']') { break };
if binput.get(idx) != Some(&b',') {
if idx == input.len() {
return Err(Error::Incomplete);
} else {
self.elem.parse(&input[orig ..], false)?;
panic!("succeeded??");
}
}
idx += 1;
idx = skip_whitespace(binput, idx);
}
Err(e) => {
if idx != input.len() &&
binput.get(idx) != Some(&b']') {
return Err(e);
}

break;
}
}
}
if binput.get(idx) != Some(&b']') {
return Err(Error::Incomplete);
}
if idx + 1 != input.len() {
return Err(Error::Mistake {
offset: Some(idx + 1),
description: "trailing garbage".to_string(),
})
}
Ok(Value::Array(vs))
}
}

pub struct VarHelper {
var_type: Arc<dyn VariableInput>,
}
Expand Down Expand Up @@ -183,7 +295,7 @@ impl Hinter for VarHelper {
if line == "" { // be friendly from the start
return None;
}
match self.var_type.parse(line) {
match self.var_type.parse(line, true) {
Ok(_) => None,
Err(Error::Incomplete) => None,
Err(e) => Some(ErrorHint(format!(" -- {}", e))),
Expand All @@ -193,7 +305,7 @@ impl Hinter for VarHelper {

impl Highlighter for VarHelper {
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
match self.var_type.parse(line) {
match self.var_type.parse(line, true) {
Ok(_) => line.into(),
Err(_) => line.light_red().to_string().into(),
}
Expand All @@ -214,7 +326,7 @@ impl Validator for VarHelper {
fn validate(&self, ctx: &mut ValidationContext)
-> Result<ValidationResult, ReadlineError>
{
match self.var_type.parse(ctx.input()) {
match self.var_type.parse(ctx.input(), true) {
Ok(_) => Ok(ValidationResult::Valid(None)),
Err(Error::Incomplete) => Ok(ValidationResult::Incomplete),
Err(e) => Ok(ValidationResult::Invalid(
Expand Down
46 changes: 29 additions & 17 deletions src/variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,16 @@ pub async fn input_variables(desc: &InputTypedesc, state: &mut repl::PromptRpc)
}
}

async fn input_item(name: &str, mut item: &Descriptor, all: &InputTypedesc,
state: &mut repl::PromptRpc, optional: bool)
-> Result<Option<Value>, anyhow::Error>
fn make_variable_input(item: &Descriptor, all: &InputTypedesc)
-> Result<Arc<dyn VariableInput>, anyhow::Error>
{
match item {
let citem = match item {
Descriptor::Scalar(s) => {
item = all.get(s.base_type_pos)?;
all.get(s.base_type_pos)?
}
_ => {},
}
match item {
_ => { item },
};
match citem {
Descriptor::BaseScalar(s) => {
let var_type: Arc<dyn VariableInput> = match s.id {
codec::STD_STR => Arc::new(variable::Str),
Expand All @@ -94,21 +93,34 @@ async fn input_item(name: &str, mut item: &Descriptor, all: &InputTypedesc,
"Unimplemented input type {}", s.id))
};

let val = match
state.variable_input(name, var_type, optional, "").await?
{
| prompt::Input::Value(val) => Some(val),
| prompt::Input::Text(_) => unreachable!(),
| prompt::Input::Interrupt => Err(Canceled)?,
| prompt::Input::Eof => None,
};
Ok(val)
Ok(var_type)
}
Descriptor::Array(s) => {
// XXX: dimensions
let elem = all.get(s.type_pos)?;
Ok(Arc::new(variable::Array::new(make_variable_input(elem, all)?)))
}
_ => Err(anyhow::anyhow!(
"Unimplemented input type descriptor: {:?}", item)),
}
}

async fn input_item(name: &str, item: &Descriptor, all: &InputTypedesc,
state: &mut repl::PromptRpc, optional: bool)
-> Result<Option<Value>, anyhow::Error>
{
let var_type: Arc<dyn VariableInput> = make_variable_input(item, all)?;
let val = match
state.variable_input(name, var_type, optional, "").await?
{
| prompt::Input::Value(val) => Some(val),
| prompt::Input::Text(_) => unreachable!(),
| prompt::Input::Interrupt => Err(Canceled)?,
| prompt::Input::Eof => None,
};
Ok(val)
}

impl Error for Canceled {
}

Expand Down

0 comments on commit 228cec1

Please sign in to comment.