Skip to content

Commit

Permalink
captures in gmi parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
yegor256 committed Aug 15, 2022
1 parent 296e7f0 commit cafac78
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 87 deletions.
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Check out all text files in UNIX format, with LF as end of line
# Don't change this file. If you have any ideas about it, please
# submit a separate issue about it and we'll discuss.

* text=auto eol=lf
*.java ident
*.xml ident
*.png binary
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ keywords = ["eolang", "compiler", "oop"]
categories = ["command-line-utilities"]

[dependencies]
anyhow = "1.0.60"
regex = "1.5.4"
rstest = "0.12.0"
lazy_static = "1.4.0"
Expand Down
1 change: 1 addition & 0 deletions eo-tests/org/eolang/reo/fibonacci.eo
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
x.minus 1
f
x.minus 2

18 changes: 14 additions & 4 deletions src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,31 @@ impl Data {
Self::from_bytes(Vec::new())
}

// From INT.
/// From BYTES.
pub fn from_bytes(bytes: Vec<u8>) -> Self {
Data { bytes }
}

// From INT.
/// From INT.
pub fn from_int(d: u64) -> Self {
Self::from_bytes(d.to_be_bytes().to_vec())
}

// From FLOAT.
/// From FLOAT.
pub fn from_float(d: f64) -> Self {
Self::from_bytes(d.to_be_bytes().to_vec())
}

// From STRING.
/// From STRING.
pub fn from_string(d: String) -> Self {
Self::from_bytes(d.as_bytes().to_vec())
}

/// It's empty and no data?
pub fn is_empty(&self) -> bool {
self.bytes.len() == 0
}

pub fn as_int(&self) -> u64 {
let a : &[u8; 8] = &self.bytes.clone().try_into().unwrap();
u64::from_be_bytes(*a)
Expand All @@ -61,6 +66,11 @@ impl Data {
String::from_utf8(self.bytes.clone()).unwrap()
}

pub fn as_hex(&self) -> String {
// TODO this is wrong
String::from_utf8(self.bytes.clone()).unwrap()
}

}

#[test]
Expand Down
147 changes: 88 additions & 59 deletions src/gmi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,97 +19,126 @@
// SOFTWARE.

use crate::universe::Universe;
use std::{fs, io};
use std::fs;
use std::collections::HashMap;
use anyhow::{anyhow, Context, Result};
use std::str::FromStr;
use lazy_static::lazy_static;
use regex::Regex;
use crate::data::Data;

// Collection of GMIs, which can be deployed to a `Universe`.
/// Collection of GMIs, which can be deployed to a `Universe`.
pub struct Gmi {
vars: HashMap<String, u32>,
text: String,
}

impl Gmi {
// Read them from a file.
pub fn from_file(file: &str) -> io::Result<Gmi> {
/// Read them from a file.
pub fn from_file(file: &str) -> Result<Gmi> {
return Gmi::from_string(fs::read_to_string(file)?);
}

// Read them from a string.
pub fn from_string(text: String) -> io::Result<Gmi> {
/// Read them from a string.
pub fn from_string(text: String) -> Result<Gmi> {
return Ok(
Gmi {
text
text,
vars: HashMap::new()
}
);
}

pub fn deploy_to(&self, uni: &mut Universe) {
pub fn deploy_to(&mut self, uni: &mut Universe) -> Result<()> {
lazy_static! {
static ref RE: Regex = Regex::new(
"^([A-Z]+)\\((?:(?: *, *)?\"([^\"]+)\")*\\);.*$"
).unwrap();
}
self.text.split("\n").for_each(|t| {
if let Some(cap) = RE.captures(t) {
println!("cmd: {} {}", &cap[1], &cap[2]);
match &cap[1] {
"ADD" => {
let v = Self::parse(&cap[2]);
uni.add(v);
},
"BIND" => {
let e = Self::parse(&cap[2]);
let v1 = Self::parse(&cap[3]);
let v2 = Self::parse(&cap[4]);
let a = &cap[5];
uni.bind(e, v1, v2, a);
},
"REF" => {
let e1 = Self::parse(&cap[2]);
let v1 = Self::parse(&cap[3]);
let l = &cap[4];
let a = &cap[5];
uni.reff(e1, v1, l, a);
},
"COPY" => {
let e1 = Self::parse(&cap[2]);
let v3 = Self::parse(&cap[3]);
let e2 = Self::parse(&cap[4]);
uni.copy(e1, v3, e2);
},
"DATA" => {
let v = Self::parse(&cap[2]);
let d : &str = &cap[3];
let bytes : Vec<u8> = (0..d.len())
.step_by(2)
.map(|i| u8::from_str_radix(&d[i..i + 2], 16).unwrap())
.collect();
uni.data(v, Data::from_bytes(bytes));
},
_ => {
panic!("Unknown GMI: {}", &cap[1])
}
};
} else {
panic!("Can't parse GMI line: \"{}\"", t);
let txt = &self.text.clone();
for (pos, t) in txt.split("\n").enumerate() {
self.deploy_one(t, uni).context(
format!("Failure at the line no.{}: \"{}\"", pos, t)
)?;
}
Ok(())
}

fn deploy_one(&mut self, line: &str, uni: &mut Universe) -> Result<()> {
lazy_static! {
static ref LINE: Regex = Regex::new(
"^([A-Z]+)\\(((?:(?: *, *)?\"(?:[^\"]+)\")*\\)); *(?:#.*)$"
).unwrap();
static ref ARGS: Regex = Regex::new(
"(?: *, *)?\"([^\"]+)\""
).unwrap();
static ref LOC: Regex = Regex::new(
"(^|\\.)\\$"
).unwrap();
}
let cap = LINE.captures(line).context(format!("Can't parse \"{}\"", line))?;
let args : Vec<&str> = ARGS.captures_iter(&cap[2])
.map(|c| c.get(1).unwrap().as_str())
.collect();
match &cap[1] {
"ADD" => {
let v = self.parse(&args[0], uni)?;
uni.add(v);
},
"BIND" => {
let e = self.parse(&args[0], uni)?;
let v1 = self.parse(&args[1], uni)?;
let v2 = self.parse(&args[2], uni)?;
let a = &args[3];
uni.bind(e, v1, v2, a);
},
"REF" => {
let e1 = self.parse(&args[0], uni)?;
let v1 = self.parse(&args[1], uni)?;
let k = LOC.replace_all(&args[2], "$1");
println!("k: {}", &k);
let a = &args[3];
uni.reff(e1, v1, &k, a);
},
"COPY" => {
let e1 = self.parse(&args[0], uni)?;
let v3 = self.parse(&args[1], uni)?;
let e2 = self.parse(&args[2], uni)?;
uni.copy(e1, v3, e2);
},
"DATA" => {
let v = self.parse(&args[0], uni)?;
let d : &str = &args[1];
let bytes : Vec<u8> = (0..d.len())
.step_by(2)
.map(|i| u8::from_str_radix(&d[i..i + 2], 16).unwrap())
.collect();
uni.data(v, Data::from_bytes(bytes));
},
_cmd => {
return Err(anyhow!("Unknown GMI: {}", _cmd))
}
});
}
Ok(())
}

// Parses `v2` or `e5` into 2 and 5.
fn parse(s: &str) -> u32{
/// Parses `v2` or `e5` into 2 and 5.
fn parse(&mut self, s: &str, uni: &mut Universe) -> Result<u32> {
let tail = &s[1..];
u32::from_str(tail).unwrap()
if &s[0..1] == "$" {
Ok(*self.vars.entry(tail.to_string()).or_insert(uni.next_id()))
} else {
Ok(u32::from_str(tail).context(format!("Parsing of \"{}\" failed", s))?)
}
}
}

#[test]
fn deploys_fibonacci() {
fn deploys_fibonacci() -> Result<()> {
let uni : &mut Universe = &mut Universe::empty();
Gmi::from_file("target/eo/gmi/org/eolang/reo/fibonacci.gmi")
.unwrap()
.deploy_to(uni);
uni.add(0);
Gmi::from_file("target/eo/gmi/org/eolang/reo/fibonacci.gmi")?
.deploy_to(uni)?;
assert_eq!(8, uni.dataize(0, "fibonacci.f").unwrap().as_int());
Ok(())
}

1 comment on commit cafac78

@0pdd
Copy link

@0pdd 0pdd commented on cafac78 Aug 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to retrieve PDD puzzles from the code base and submit them to github. If you think that it's a bug on our side, please submit it to yegor256/0pdd:

set -x && set -e && set -o pipefail && cd /tmp/0pdd20220815-14-t6ylgz/Z2l0QGdpdGh1Yi5jb206b2JqZWN0aW9uYXJ5L3Jlby5naXQ && pdd -v -f /tmp/20220815-26433-7og3jq [1]: + set -e + set -o pipefail + cd /tmp/0pdd20220815-14-t6ylgz/Z2l0QGdpdGh1Yi5jb206b2JqZWN0aW9uYXJ5L3Jlby5naXQ + pdd -v -f...

Please, copy and paste this stack trace to GitHub:

UserError
set -x && set -e && set -o pipefail && cd /tmp/0pdd20220815-14-t6ylgz/Z2l0QGdpdGh1Yi5jb206b2JqZWN0aW9uYXJ5L3Jlby5naXQ && pdd -v -f /tmp/20220815-26433-7og3jq [1]:
+ set -e
+ set -o pipefail
+ cd /tmp/0pdd20220815-14-t6ylgz/Z2l0QGdpdGh1Yi5jb206b2JqZWN0aW9uYXJ5L3Jlby5naXQ
+ pdd -v -f /tmp/20220815-26433-7og3jq

My version is 0.21.3
Ruby version is 2.7.5 at x86_64-linux
Reading from root dir /tmp/0pdd20220815-14-t6ylgz/Z2l0QGdpdGh1Yi5jb206b2JqZWN0aW9uYXJ5L3Jlby5naXQ
Reading .gitignore ...
Reading README.md ...
Reading .gitattributes ...
Reading build.rs ...
Reading Cargo.lock ...
Reading test-pom.xml ...
Reading .rultor.yml ...
Reading Cargo.toml ...
Reading LICENSE.txt ...
Reading .github/workflows/xcop.yml ...
Reading .github/workflows/cargo.yml ...
Reading .github/workflows/pdd.yml ...
Reading tests/reo_test.rs ...
Reading src/bin/reo.rs ...
Reading src/universe.rs ...
Reading src/gmi.rs ...
Reading src/data.rs ...
ERROR: src/data.rs; PDD::Error at src/data.rs:70: TODO found, but puzzle can't be parsed, most probably because TODO is not followed by a puzzle marker, as this page explains: https://github.com/cqfn/pdd#how-to-format
If you can't understand the cause of this issue or you don't know how to fix it, please submit a GitHub issue, we will try to help you: https://github.com/cqfn/pdd/issues. This tool is still in its beta version and we will appreciate your feedback. Here is where you can find more documentation: https://github.com/cqfn/pdd/blob/master/README.md.
Exit code is 1

/app/objects/git_repo.rb:73:in `rescue in block in xml'
/app/objects/git_repo.rb:70:in `block in xml'
/app/vendor/ruby-2.7.5/lib/ruby/2.7.0/tempfile.rb:291:in `open'
/app/objects/git_repo.rb:69:in `xml'
/app/objects/puzzles.rb:41:in `deploy'
/app/objects/jobs/job.rb:38:in `proceed'
/app/objects/jobs/job_starred.rb:32:in `proceed'
/app/objects/jobs/job_recorded.rb:31:in `proceed'
/app/objects/jobs/job_emailed.rb:33:in `proceed'
/app/objects/jobs/job_commiterrors.rb:33:in `proceed'
/app/objects/jobs/job_detached.rb:48:in `exclusive'
/app/objects/jobs/job_detached.rb:36:in `block in proceed'
/app/objects/jobs/job_detached.rb:36:in `fork'
/app/objects/jobs/job_detached.rb:36:in `proceed'
/app/0pdd.rb:519:in `process_request'
/app/0pdd.rb:356:in `block in <top (required)>'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1686:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1686:in `block in compile!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1023:in `block (3 levels) in route!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1042:in `route_eval'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1023:in `block (2 levels) in route!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1071:in `block in process_route'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1069:in `catch'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1069:in `process_route'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1021:in `block in route!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1018:in `each'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1018:in `route!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1140:in `block in dispatch!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `block in invoke'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `catch'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `invoke'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1135:in `dispatch!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:949:in `block in call!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `block in invoke'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `catch'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `invoke'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:949:in `call!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:938:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/xss_header.rb:18:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/path_traversal.rb:16:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/json_csrf.rb:26:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/base.rb:50:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/base.rb:50:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/frame_options.rb:31:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-2.2.4/lib/rack/logger.rb:17:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-2.2.4/lib/rack/common_logger.rb:38:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:255:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:248:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-2.2.4/lib/rack/head.rb:12:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-2.2.4/lib/rack/method_override.rb:24:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:218:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1993:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1553:in `block in call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1769:in `synchronize'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1553:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-2.2.4/lib/rack/handler/webrick.rb:95:in `service'
/app/vendor/ruby-2.7.5/lib/ruby/2.7.0/webrick/httpserver.rb:140:in `service'
/app/vendor/ruby-2.7.5/lib/ruby/2.7.0/webrick/httpserver.rb:96:in `run'
/app/vendor/ruby-2.7.5/lib/ruby/2.7.0/webrick/server.rb:307:in `block in start_thread'

Please sign in to comment.