From 85911c8e99992f4190636b1c0ce44abed04e5aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 19:36:13 +0200 Subject: [PATCH 01/12] add sexp into source tree --- Cargo.lock | 2 - crsn/Cargo.toml | 2 +- lib/spanned_sexp/.cargo-ok | 1 + lib/spanned_sexp/.editorconfig | 10 + lib/spanned_sexp/.gitignore | 2 + lib/spanned_sexp/.travis.yml | 36 +++ lib/spanned_sexp/Cargo.toml | 16 ++ lib/spanned_sexp/LICENSE | 19 ++ lib/spanned_sexp/README.md | 15 ++ lib/spanned_sexp/src/lib.rs | 417 +++++++++++++++++++++++++++++++++ 10 files changed, 517 insertions(+), 3 deletions(-) create mode 100644 lib/spanned_sexp/.cargo-ok create mode 100644 lib/spanned_sexp/.editorconfig create mode 100644 lib/spanned_sexp/.gitignore create mode 100644 lib/spanned_sexp/.travis.yml create mode 100644 lib/spanned_sexp/Cargo.toml create mode 100644 lib/spanned_sexp/LICENSE create mode 100644 lib/spanned_sexp/README.md create mode 100644 lib/spanned_sexp/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index f18d411..8f09537 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -879,8 +879,6 @@ dependencies = [ [[package]] name = "sexp" version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8fa7ac9df84000b0238cf497cb2d3056bac2ff2a7d8cf179d2803b4b58571f" [[package]] name = "sha-1" diff --git a/crsn/Cargo.toml b/crsn/Cargo.toml index 369e7aa..15c3d4e 100644 --- a/crsn/Cargo.toml +++ b/crsn/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" publish = false [dependencies] -sexp = "1.1.4" +sexp = { path = "../lib/spanned_sexp" } thiserror = "1.0.20" anyhow = "1.0.32" dyn-clonable = "0.9.0" diff --git a/lib/spanned_sexp/.cargo-ok b/lib/spanned_sexp/.cargo-ok new file mode 100644 index 0000000..b5754e2 --- /dev/null +++ b/lib/spanned_sexp/.cargo-ok @@ -0,0 +1 @@ +ok \ No newline at end of file diff --git a/lib/spanned_sexp/.editorconfig b/lib/spanned_sexp/.editorconfig new file mode 100644 index 0000000..fa9becf --- /dev/null +++ b/lib/spanned_sexp/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 +tab_width = 4 +trim_trailing_whitespace = true diff --git a/lib/spanned_sexp/.gitignore b/lib/spanned_sexp/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/lib/spanned_sexp/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/lib/spanned_sexp/.travis.yml b/lib/spanned_sexp/.travis.yml new file mode 100644 index 0000000..a4430b7 --- /dev/null +++ b/lib/spanned_sexp/.travis.yml @@ -0,0 +1,36 @@ +language: rust +sudo: false +# necessary for `travis-cargo coveralls --no-sudo` +addons: + apt: + packages: + - libcurl4-openssl-dev + - libelf-dev + - libdw-dev +rust: +- nightly +os: +- linux + +env: + global: + - TRAVIS_CARGO_NIGHTLY_FEATURE="" + - secure: C2P1wLHzBxccS3jrimsG2TaDy4sAhYiKSq1g+cwYHhAKZKkiIpL7Ez5iEHH6BbEvvg4HiUJy4j0w83luZ/FXUuxkD2GZsXWoG+20DFBTLQvCJE/LPahVNbb5i+NdmyIsZPHLloXNvT63hXwu8KNV4U0hrYAgViIXkumoLnOiQD/jim81i7gxUOSe65AzMHcfPRaAwKHn+NGIvUfwMzU2hKZbnH/BPIi2PNtQ6e0VZEvAqA5Ad3hRV0YaBKZ3HZn8tr8UnHKmLbPffb/01EVWAFBU+rFMVYrdzDsiVp7UHMPtVV9aNXUVszB+a/ASWHsAZEdX8XsbmH9RSEBCzsUq2j2HFM2R7yYZnkL3FPcpf/ZKgy4ZVw6gKO42DCvBRGwhI1JMjeKBmrzCGZHE70FxD0zAZRwX9n9M7mUKhakzMvs/LSKMQKlOJslSR+OLEUpr3MCBthpKIiajNYDrJL5P/3KrFOF2R4H/2Z91/3osEIRqzYiEKdeJU01Yef5FCI+H6SLvbhIlVAQTM0IJKGAP0B2N6J4Ot7XrYuGDQag48oPzWzJ2dOGwYjwkda1rgW7pdjtWuullOi2ob1zdI6y/i/CdAS8AE0yRz7VCK4grwonUICzdVaaIAaTTd0yq9PRWAjSjZqNG5EOLADzABIihPnkBw4WygoDq18rSkk0pRbE= + +before_script: + - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH + +script: +- travis-cargo build +- travis-cargo test +- travis-cargo bench +- travis-cargo doc +after_success: +- | + [ $TRAVIS_BRANCH = master ] && + [ $TRAVIS_PULL_REQUEST = false ] && + echo '' > target/doc/index.html && + git clone --depth 1 https://github.com/davisp/ghp-import && + ./ghp-import/ghp-import -n target/doc && + git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages +- travis-cargo coveralls --no-sudo diff --git a/lib/spanned_sexp/Cargo.toml b/lib/spanned_sexp/Cargo.toml new file mode 100644 index 0000000..26c6fc8 --- /dev/null +++ b/lib/spanned_sexp/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "sexp" +version = "1.1.4" +authors = ["Clark Gaebel "] + +documentation = "https://cgaebel.github.io/sexp" +homepage = "https://github.com/cgaebel/sexp" +repository = "https://github.com/cgaebel/sexp" + +readme = "README.md" + +keywords = [ "sexp", "parsing", "s-expression", "file-format" ] + +description = "A small, simple, self-contained, s-expression parser and pretty-printer." + +license = "MIT" diff --git a/lib/spanned_sexp/LICENSE b/lib/spanned_sexp/LICENSE new file mode 100644 index 0000000..683f7fa --- /dev/null +++ b/lib/spanned_sexp/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Clark Gaebel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/spanned_sexp/README.md b/lib/spanned_sexp/README.md new file mode 100644 index 0000000..6da62fb --- /dev/null +++ b/lib/spanned_sexp/README.md @@ -0,0 +1,15 @@ +Sexp +===== + +A small, simple, self-contained, s-expression parser and pretty-printer. + +[![crates.io](https://img.shields.io/crates/v/sexp.svg)](https://crates.io/crates/sexp/) + +[![Build Status](https://travis-ci.org/cgaebel/sexp.svg?branch=master)](https://travis-ci.org/cgaebel/sexp) + +[![Coverage Status](https://coveralls.io/repos/cgaebel/sexp/badge.svg?branch=master&service=github)](https://coveralls.io/github/cgaebel/sexp?branch=master) + +Documentation +------------- + +See the [API Docs](https://cgaebel.github.io/sexp/). diff --git a/lib/spanned_sexp/src/lib.rs b/lib/spanned_sexp/src/lib.rs new file mode 100644 index 0000000..d514af7 --- /dev/null +++ b/lib/spanned_sexp/src/lib.rs @@ -0,0 +1,417 @@ +//! A lightweight, self-contained s-expression parser and data format. +//! Use `parse` to get an s-expression from its string representation, and the +//! `Display` trait to serialize it, potentially by doing `sexp.to_string()`. + +#![deny(missing_docs)] +#![deny(unsafe_code)] + +use std::borrow::Cow; +use std::cmp; +use std::error; +use std::fmt; +use std::str::{self, FromStr}; + +/// A single data element in an s-expression. Floats are excluded to ensure +/// atoms may be used as keys in ordered and hashed data structures. +/// +/// All strings must be valid utf-8. +#[derive(PartialEq, Clone, PartialOrd)] +#[allow(missing_docs)] +pub enum Atom { + S(String), + I(i64), + F(f64), +} + +/// An s-expression is either an atom or a list of s-expressions. This is +/// similar to the data format used by lisp. +#[derive(PartialEq, Clone, PartialOrd)] +#[allow(missing_docs)] +pub enum Sexp { + Atom(Atom), + List(Vec), +} + +#[test] +fn sexp_size() { + // I just want to see when this changes, in the diff. + use std::mem; + assert_eq!(mem::size_of::(), mem::size_of::()*5); +} + +/// The representation of an s-expression parse error. +pub struct Error { + /// The error message. + pub message: &'static str, + /// The line number on which the error occurred. + pub line: usize, + /// The column number on which the error occurred. + pub column: usize, + /// The index in the given string which caused the error. + pub index: usize, +} + +impl error::Error for Error { + fn description(&self) -> &str { self.message } + fn cause(&self) -> Option<&error::Error> { None } +} + +/// Since errors are the uncommon case, they're boxed. This keeps the size of +/// structs down, which helps performance in the common case. +/// +/// For example, an `ERes<()>` becomes 8 bytes, instead of the 24 bytes it would +/// be if `Err` were unboxed. +type Err = Box; + +/// Helps clean up type signatures, but shouldn't be exposed to the outside +/// world. +type ERes = Result; + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}:{}: {}", self.line, self.column, self.message) + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self) + } +} + +#[test] +fn show_an_error() { + assert_eq!(format!("{:?}", parse("(aaaa").unwrap_err()), "1:4: unexpected eof"); +} + +fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) { + let mut line: usize = 1; + let mut col: isize = -1; + for c in s.chars().take(pos+1) { + if c == '\n' { + line += 1; + col = -1; + } else { + col += 1; + } + } + (line, cmp::max(col, 0) as usize) +} + +#[test] +fn line_and_col_test() { + let s = "0123456789\n0123456789\n\n6"; + assert_eq!(get_line_and_column(s, 4), (1, 4)); + + assert_eq!(get_line_and_column(s, 10), (2, 0)); + assert_eq!(get_line_and_column(s, 11), (2, 0)); + assert_eq!(get_line_and_column(s, 15), (2, 4)); + + assert_eq!(get_line_and_column(s, 21), (3, 0)); + assert_eq!(get_line_and_column(s, 22), (4, 0)); + assert_eq!(get_line_and_column(s, 23), (4, 0)); + assert_eq!(get_line_and_column(s, 500), (4, 0)); +} + +#[cold] +fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err { + let (line, column) = get_line_and_column(s, *pos); + Box::new(Error { + message: message, + line: line, + column: column, + index: *pos, + }) +} + +fn err(message: &'static str, s: &str, pos: &usize) -> ERes { + Err(err_impl(message, s, pos)) +} + +/// A helpful utility to trace the execution of a parser while testing. It will +/// be compiled out in release builds. +#[allow(unused_variables)] +fn dbg(msg: &str, pos: &usize) { + //println!("{} @ {}", msg, pos) +} + +fn atom_of_string(s: String) -> Atom { + match FromStr::from_str(&s) { + Ok(i) => return Atom::I(i), + Err(_) => {}, + }; + + match FromStr::from_str(&s) { + Ok(f) => return Atom::F(f), + Err(_) => {}, + }; + + Atom::S(s) +} + +// returns the char it found, and the new size if you wish to consume that char +fn peek(s: &str, pos: &usize) -> ERes<(char, usize)> { + dbg("peek", pos); + if *pos == s.len() { return err("unexpected eof", s, pos) } + if s.is_char_boundary(*pos) { + let ch = s[*pos..].chars().next().unwrap(); + let next = *pos + ch.len_utf8(); + Ok((ch, next)) + } else { + // strings must be composed of valid utf-8 chars. + unreachable!() + } +} + +fn expect(s: &str, pos: &mut usize, c: char) -> ERes<()> { + dbg("expect", pos); + let (ch, next) = try!(peek(s, pos)); + *pos = next; + if ch == c { Ok(()) } else { err("unexpected character", s, pos) } +} + +fn consume_until_newline(s: &str, pos: &mut usize) -> ERes<()> { + loop { + if *pos == s.len() { return Ok(()) } + let (ch, next) = try!(peek(s, pos)); + *pos = next; + if ch == '\n' { return Ok(()) } + } +} + +// zero or more spaces +fn zspace(s: &str, pos: &mut usize) -> ERes<()> { + dbg("zspace", pos); + loop { + if *pos == s.len() { return Ok(()) } + let (ch, next) = try!(peek(s, pos)); + + if ch == ';' { try!(consume_until_newline(s, pos)) } + else if ch.is_whitespace() { *pos = next; } + else { return Ok(()) } + } +} + +fn parse_quoted_atom(s: &str, pos: &mut usize) -> ERes { + dbg("parse_quoted_atom", pos); + let mut cs: String = String::new(); + + try!(expect(s, pos, '"')); + + loop { + let (ch, next) = try!(peek(s, pos)); + if ch == '"' { + *pos = next; + break; + } else if ch == '\\' { + let (postslash, nextnext) = try!(peek(s, &next)); + if postslash == '"' || postslash == '\\' { + cs.push(postslash); + } else { + cs.push(ch); + cs.push(postslash); + } + *pos = nextnext; + } else { + cs.push(ch); + *pos = next; + } + } + + // Do not try i64 conversion, since this atom was explicitly quoted. + Ok(Atom::S(cs)) +} + +fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes { + dbg("parse_unquoted_atom", pos); + let mut cs: String = String::new(); + + loop { + if *pos == s.len() { break } + let (c, next) = try!(peek(s, pos)); + + if c == ';' { try!(consume_until_newline(s, pos)); break } + if c.is_whitespace() || c == '(' || c == ')' { break } + cs.push(c); + *pos = next; + } + + Ok(atom_of_string(cs)) +} + +fn parse_atom(s: &str, pos: &mut usize) -> ERes { + dbg("parse_atom", pos); + let (ch, _) = try!(peek(s, pos)); + + if ch == '"' { parse_quoted_atom (s, pos) } + else { parse_unquoted_atom(s, pos) } +} + +fn parse_list(s: &str, pos: &mut usize) -> ERes> { + dbg("parse_list", pos); + try!(zspace(s, pos)); + try!(expect(s, pos, '(')); + + let mut sexps: Vec = Vec::new(); + + loop { + try!(zspace(s, pos)); + let (c, next) = try!(peek(s, pos)); + if c == ')' { + *pos = next; + break; + } + sexps.push(try!(parse_sexp(s, pos))); + } + + try!(zspace(s, pos)); + + Ok(sexps) +} + +fn parse_sexp(s: &str, pos: &mut usize) -> ERes { + dbg("parse_sexp", pos); + try!(zspace(s, pos)); + let (c, _) = try!(peek(s, pos)); + let r = + if c == '(' { Ok(Sexp::List(try!(parse_list(s, pos)))) } + else { Ok(Sexp::Atom(try!(parse_atom(s, pos)))) }; + try!(zspace(s, pos)); + r +} + +/// Constructs an atomic s-expression from a string. +pub fn atom_s(s: &str) -> Sexp { + Sexp::Atom(Atom::S(s.to_owned())) +} + +/// Constructs an atomic s-expression from an int. +pub fn atom_i(i: i64) -> Sexp { + Sexp::Atom(Atom::I(i)) +} + +/// Constructs an atomic s-expression from a float. +pub fn atom_f(f: f64) -> Sexp { + Sexp::Atom(Atom::F(f)) +} + +/// Constructs a list s-expression given a slice of s-expressions. +pub fn list(xs: &[Sexp]) -> Sexp { + Sexp::List(xs.to_owned()) +} + +/// Reads an s-expression out of a `&str`. +#[inline(never)] +pub fn parse(s: &str) -> Result> { + let mut pos = 0; + let ret = try!(parse_sexp(s, &mut pos)); + if pos == s.len() { Ok(ret) } else { err("unrecognized post-s-expression data", s, &pos) } +} + +// TODO: Pretty print in lisp convention, instead of all on the same line, +// packed as tightly as possible. It's kinda ugly. + +fn is_num_string(s: &str) -> bool { + let x: Result = FromStr::from_str(&s); + let y: Result = FromStr::from_str(&s); + x.is_ok() || y.is_ok() +} + +fn string_contains_whitespace(s: &str) -> bool { + for c in s.chars() { + if c.is_whitespace() { return true } + } + false +} + +fn quote(s: &str) -> Cow { + if !s.contains("\"") + && !string_contains_whitespace(s) + && !is_num_string(s) { + Cow::Borrowed(s) + } else { + let mut r: String = "\"".to_string(); + r.push_str(&s.replace("\\", "\\\\").replace("\"", "\\\"")); + r.push_str("\""); + Cow::Owned(r) + } +} + +impl fmt::Display for Atom { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + Atom::S(ref s) => write!(f, "{}", quote(s)), + Atom::I(i) => write!(f, "{}", i), + Atom::F(d) => write!(f, "{}", d), + } + } +} + +impl fmt::Display for Sexp { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + Sexp::Atom(ref a) => write!(f, "{}", a), + Sexp::List(ref xs) => { + try!(write!(f, "(")); + for (i, x) in xs.iter().enumerate() { + let s = if i == 0 { "" } else { " " }; + try!(write!(f, "{}{}", s, x)); + } + write!(f, ")") + }, + } + } +} + +impl fmt::Debug for Atom { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self) + } +} + +impl fmt::Debug for Sexp { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self) + } +} + +#[test] +fn test_hello_world() { + assert_eq!( + parse("(hello -42\n\t -4.0 \"world\") ; comment").unwrap(), + list(&[ atom_s("hello"), atom_i(-42), atom_f(-4.0), atom_s("world") ])); +} + +#[test] +fn test_escaping() { + assert_eq!( + parse("(\"\\\"\\q\" \"1234\" 1234)").unwrap(), + list(&[ atom_s("\"\\q"), atom_s("1234"), atom_i(1234) ])); +} + +#[test] +fn test_pp() { + let s = "(hello world (what is (up) (4 6.4 you \"123\\\\ \\\"\")))"; + let sexp = parse(s).unwrap(); + assert_eq!(s, sexp.to_string()); + assert_eq!(s, format!("{:?}", sexp)); +} + +#[test] +fn test_tight_parens() { + let s = "(hello(world))"; + let sexp = parse(s).unwrap(); + assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into())), + Sexp::List(vec![Sexp::Atom(Atom::S("world".into()))])])); + let s = "(this (has)tight(parens))"; + let s2 = "( this ( has ) tight ( parens ) )"; + assert_eq!(parse(s).unwrap(), parse(s2).unwrap()); +} + +#[test] +fn test_space_in_atom() { + let sexp = list(&[ atom_s("hello world")]); + let sexp_as_string = sexp.to_string(); + assert_eq!("(\"hello world\")", sexp_as_string); + assert_eq!(sexp, parse(&sexp_as_string).unwrap()); +} From 250f0f22c0e2e8ff75f7d3d713b96bffac1eed84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 19:36:59 +0200 Subject: [PATCH 02/12] cargo fix --- lib/spanned_sexp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/spanned_sexp/src/lib.rs b/lib/spanned_sexp/src/lib.rs index d514af7..9bbe758 100644 --- a/lib/spanned_sexp/src/lib.rs +++ b/lib/spanned_sexp/src/lib.rs @@ -53,7 +53,7 @@ pub struct Error { impl error::Error for Error { fn description(&self) -> &str { self.message } - fn cause(&self) -> Option<&error::Error> { None } + fn cause(&self) -> Option<&dyn error::Error> { None } } /// Since errors are the uncommon case, they're boxed. This keeps the size of From a2dada519d5059a57dcc4e769ab04cdaac536fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 19:38:13 +0200 Subject: [PATCH 03/12] sexp: replace try! with ? --- lib/spanned_sexp/src/lib.rs | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/spanned_sexp/src/lib.rs b/lib/spanned_sexp/src/lib.rs index 9bbe758..5662aa8 100644 --- a/lib/spanned_sexp/src/lib.rs +++ b/lib/spanned_sexp/src/lib.rs @@ -165,7 +165,7 @@ fn peek(s: &str, pos: &usize) -> ERes<(char, usize)> { fn expect(s: &str, pos: &mut usize, c: char) -> ERes<()> { dbg("expect", pos); - let (ch, next) = try!(peek(s, pos)); + let (ch, next) = peek(s, pos)?; *pos = next; if ch == c { Ok(()) } else { err("unexpected character", s, pos) } } @@ -173,7 +173,7 @@ fn expect(s: &str, pos: &mut usize, c: char) -> ERes<()> { fn consume_until_newline(s: &str, pos: &mut usize) -> ERes<()> { loop { if *pos == s.len() { return Ok(()) } - let (ch, next) = try!(peek(s, pos)); + let (ch, next) = peek(s, pos)?; *pos = next; if ch == '\n' { return Ok(()) } } @@ -184,9 +184,9 @@ fn zspace(s: &str, pos: &mut usize) -> ERes<()> { dbg("zspace", pos); loop { if *pos == s.len() { return Ok(()) } - let (ch, next) = try!(peek(s, pos)); + let (ch, next) = peek(s, pos)?; - if ch == ';' { try!(consume_until_newline(s, pos)) } + if ch == ';' { consume_until_newline(s, pos)? } else if ch.is_whitespace() { *pos = next; } else { return Ok(()) } } @@ -196,15 +196,15 @@ fn parse_quoted_atom(s: &str, pos: &mut usize) -> ERes { dbg("parse_quoted_atom", pos); let mut cs: String = String::new(); - try!(expect(s, pos, '"')); + expect(s, pos, '"')?; loop { - let (ch, next) = try!(peek(s, pos)); + let (ch, next) = peek(s, pos)?; if ch == '"' { *pos = next; break; } else if ch == '\\' { - let (postslash, nextnext) = try!(peek(s, &next)); + let (postslash, nextnext) = peek(s, &next)?; if postslash == '"' || postslash == '\\' { cs.push(postslash); } else { @@ -228,9 +228,9 @@ fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes { loop { if *pos == s.len() { break } - let (c, next) = try!(peek(s, pos)); + let (c, next) = peek(s, pos)?; - if c == ';' { try!(consume_until_newline(s, pos)); break } + if c == ';' { consume_until_newline(s, pos)?; break } if c.is_whitespace() || c == '(' || c == ')' { break } cs.push(c); *pos = next; @@ -241,7 +241,7 @@ fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes { fn parse_atom(s: &str, pos: &mut usize) -> ERes { dbg("parse_atom", pos); - let (ch, _) = try!(peek(s, pos)); + let (ch, _) = peek(s, pos)?; if ch == '"' { parse_quoted_atom (s, pos) } else { parse_unquoted_atom(s, pos) } @@ -249,34 +249,34 @@ fn parse_atom(s: &str, pos: &mut usize) -> ERes { fn parse_list(s: &str, pos: &mut usize) -> ERes> { dbg("parse_list", pos); - try!(zspace(s, pos)); - try!(expect(s, pos, '(')); + zspace(s, pos)?; + expect(s, pos, '(')?; let mut sexps: Vec = Vec::new(); loop { - try!(zspace(s, pos)); - let (c, next) = try!(peek(s, pos)); + zspace(s, pos)?; + let (c, next) = peek(s, pos)?; if c == ')' { *pos = next; break; } - sexps.push(try!(parse_sexp(s, pos))); + sexps.push(parse_sexp(s, pos)?); } - try!(zspace(s, pos)); + zspace(s, pos)?; Ok(sexps) } fn parse_sexp(s: &str, pos: &mut usize) -> ERes { dbg("parse_sexp", pos); - try!(zspace(s, pos)); - let (c, _) = try!(peek(s, pos)); + zspace(s, pos)?; + let (c, _) = peek(s, pos)?; let r = - if c == '(' { Ok(Sexp::List(try!(parse_list(s, pos)))) } - else { Ok(Sexp::Atom(try!(parse_atom(s, pos)))) }; - try!(zspace(s, pos)); + if c == '(' { Ok(Sexp::List(parse_list(s, pos)?)) } + else { Ok(Sexp::Atom(parse_atom(s, pos)?)) }; + zspace(s, pos)?; r } @@ -304,7 +304,7 @@ pub fn list(xs: &[Sexp]) -> Sexp { #[inline(never)] pub fn parse(s: &str) -> Result> { let mut pos = 0; - let ret = try!(parse_sexp(s, &mut pos)); + let ret = parse_sexp(s, &mut pos)?; if pos == s.len() { Ok(ret) } else { err("unrecognized post-s-expression data", s, &pos) } } @@ -352,10 +352,10 @@ impl fmt::Display for Sexp { match *self { Sexp::Atom(ref a) => write!(f, "{}", a), Sexp::List(ref xs) => { - try!(write!(f, "(")); + write!(f, "(")?; for (i, x) in xs.iter().enumerate() { let s = if i == 0 { "" } else { " " }; - try!(write!(f, "{}{}", s, x)); + write!(f, "{}{}", s, x)?; } write!(f, ")") }, From 3cb445a31cb0db5661508c09e5b42fa13360894c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 19:40:16 +0200 Subject: [PATCH 04/12] sexp: tests to submodule --- lib/spanned_sexp/src/lib.rs | 128 +++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 61 deletions(-) diff --git a/lib/spanned_sexp/src/lib.rs b/lib/spanned_sexp/src/lib.rs index 5662aa8..0fabba1 100644 --- a/lib/spanned_sexp/src/lib.rs +++ b/lib/spanned_sexp/src/lib.rs @@ -32,13 +32,6 @@ pub enum Sexp { List(Vec), } -#[test] -fn sexp_size() { - // I just want to see when this changes, in the diff. - use std::mem; - assert_eq!(mem::size_of::(), mem::size_of::()*5); -} - /// The representation of an s-expression parse error. pub struct Error { /// The error message. @@ -79,11 +72,6 @@ impl fmt::Debug for Error { } } -#[test] -fn show_an_error() { - assert_eq!(format!("{:?}", parse("(aaaa").unwrap_err()), "1:4: unexpected eof"); -} - fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) { let mut line: usize = 1; let mut col: isize = -1; @@ -98,20 +86,6 @@ fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) { (line, cmp::max(col, 0) as usize) } -#[test] -fn line_and_col_test() { - let s = "0123456789\n0123456789\n\n6"; - assert_eq!(get_line_and_column(s, 4), (1, 4)); - - assert_eq!(get_line_and_column(s, 10), (2, 0)); - assert_eq!(get_line_and_column(s, 11), (2, 0)); - assert_eq!(get_line_and_column(s, 15), (2, 4)); - - assert_eq!(get_line_and_column(s, 21), (3, 0)); - assert_eq!(get_line_and_column(s, 22), (4, 0)); - assert_eq!(get_line_and_column(s, 23), (4, 0)); - assert_eq!(get_line_and_column(s, 500), (4, 0)); -} #[cold] fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err { @@ -375,43 +349,75 @@ impl fmt::Debug for Sexp { } } -#[test] -fn test_hello_world() { - assert_eq!( - parse("(hello -42\n\t -4.0 \"world\") ; comment").unwrap(), - list(&[ atom_s("hello"), atom_i(-42), atom_f(-4.0), atom_s("world") ])); -} +#[cfg(test)] +mod test { + use super::*; -#[test] -fn test_escaping() { - assert_eq!( - parse("(\"\\\"\\q\" \"1234\" 1234)").unwrap(), - list(&[ atom_s("\"\\q"), atom_s("1234"), atom_i(1234) ])); -} + #[test] + fn test_hello_world() { + assert_eq!( + parse("(hello -42\n\t -4.0 \"world\") ; comment").unwrap(), + list(&[ atom_s("hello"), atom_i(-42), atom_f(-4.0), atom_s("world") ])); + } -#[test] -fn test_pp() { - let s = "(hello world (what is (up) (4 6.4 you \"123\\\\ \\\"\")))"; - let sexp = parse(s).unwrap(); - assert_eq!(s, sexp.to_string()); - assert_eq!(s, format!("{:?}", sexp)); -} + #[test] + fn test_escaping() { + assert_eq!( + parse("(\"\\\"\\q\" \"1234\" 1234)").unwrap(), + list(&[ atom_s("\"\\q"), atom_s("1234"), atom_i(1234) ])); + } -#[test] -fn test_tight_parens() { - let s = "(hello(world))"; - let sexp = parse(s).unwrap(); - assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into())), - Sexp::List(vec![Sexp::Atom(Atom::S("world".into()))])])); - let s = "(this (has)tight(parens))"; - let s2 = "( this ( has ) tight ( parens ) )"; - assert_eq!(parse(s).unwrap(), parse(s2).unwrap()); -} + #[test] + fn test_pp() { + let s = "(hello world (what is (up) (4 6.4 you \"123\\\\ \\\"\")))"; + let sexp = parse(s).unwrap(); + assert_eq!(s, sexp.to_string()); + assert_eq!(s, format!("{:?}", sexp)); + } + + #[test] + fn test_tight_parens() { + let s = "(hello(world))"; + let sexp = parse(s).unwrap(); + assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into())), + Sexp::List(vec![Sexp::Atom(Atom::S("world".into()))])])); + let s = "(this (has)tight(parens))"; + let s2 = "( this ( has ) tight ( parens ) )"; + assert_eq!(parse(s).unwrap(), parse(s2).unwrap()); + } + + #[test] + fn test_space_in_atom() { + let sexp = list(&[ atom_s("hello world")]); + let sexp_as_string = sexp.to_string(); + assert_eq!("(\"hello world\")", sexp_as_string); + assert_eq!(sexp, parse(&sexp_as_string).unwrap()); + } + + #[test] + fn show_an_error() { + assert_eq!(format!("{:?}", parse("(aaaa").unwrap_err()), "1:4: unexpected eof"); + } + + #[test] + fn line_and_col_test() { + let s = "0123456789\n0123456789\n\n6"; + assert_eq!(get_line_and_column(s, 4), (1, 4)); + + assert_eq!(get_line_and_column(s, 10), (2, 0)); + assert_eq!(get_line_and_column(s, 11), (2, 0)); + assert_eq!(get_line_and_column(s, 15), (2, 4)); -#[test] -fn test_space_in_atom() { - let sexp = list(&[ atom_s("hello world")]); - let sexp_as_string = sexp.to_string(); - assert_eq!("(\"hello world\")", sexp_as_string); - assert_eq!(sexp, parse(&sexp_as_string).unwrap()); + assert_eq!(get_line_and_column(s, 21), (3, 0)); + assert_eq!(get_line_and_column(s, 22), (4, 0)); + assert_eq!(get_line_and_column(s, 23), (4, 0)); + assert_eq!(get_line_and_column(s, 500), (4, 0)); + } + + #[test] + fn sexp_size() { + // I just want to see when this changes, in the diff. + use std::mem; + assert_eq!(mem::size_of::(), mem::size_of::()*5); + } } From 7272eecc481757535289993c9db5c616cfd1fe3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 19:40:56 +0200 Subject: [PATCH 05/12] sexp automated format & cleanup --- lib/spanned_sexp/src/lib.rs | 379 ++++++++++++++++++------------------ 1 file changed, 189 insertions(+), 190 deletions(-) diff --git a/lib/spanned_sexp/src/lib.rs b/lib/spanned_sexp/src/lib.rs index 0fabba1..7adab8c 100644 --- a/lib/spanned_sexp/src/lib.rs +++ b/lib/spanned_sexp/src/lib.rs @@ -18,9 +18,9 @@ use std::str::{self, FromStr}; #[derive(PartialEq, Clone, PartialOrd)] #[allow(missing_docs)] pub enum Atom { - S(String), - I(i64), - F(f64), + S(String), + I(i64), + F(f64), } /// An s-expression is either an atom or a list of s-expressions. This is @@ -28,25 +28,25 @@ pub enum Atom { #[derive(PartialEq, Clone, PartialOrd)] #[allow(missing_docs)] pub enum Sexp { - Atom(Atom), - List(Vec), + Atom(Atom), + List(Vec), } /// The representation of an s-expression parse error. pub struct Error { - /// The error message. - pub message: &'static str, - /// The line number on which the error occurred. - pub line: usize, - /// The column number on which the error occurred. - pub column: usize, - /// The index in the given string which caused the error. - pub index: usize, + /// The error message. + pub message: &'static str, + /// The line number on which the error occurred. + pub line: usize, + /// The column number on which the error occurred. + pub column: usize, + /// The index in the given string which caused the error. + pub index: usize, } impl error::Error for Error { - fn description(&self) -> &str { self.message } - fn cause(&self) -> Option<&dyn error::Error> { None } + fn description(&self) -> &str { self.message } + fn cause(&self) -> Option<&dyn error::Error> { None } } /// Since errors are the uncommon case, they're boxed. This keeps the size of @@ -61,292 +61,291 @@ type Err = Box; type ERes = Result; impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}:{}: {}", self.line, self.column, self.message) - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}:{}: {}", self.line, self.column, self.message) + } } impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", self) - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self) + } } fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) { - let mut line: usize = 1; - let mut col: isize = -1; - for c in s.chars().take(pos+1) { - if c == '\n' { - line += 1; - col = -1; - } else { - col += 1; + let mut line: usize = 1; + let mut col: isize = -1; + for c in s.chars().take(pos + 1) { + if c == '\n' { + line += 1; + col = -1; + } else { + col += 1; + } } - } - (line, cmp::max(col, 0) as usize) + (line, cmp::max(col, 0) as usize) } #[cold] fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err { - let (line, column) = get_line_and_column(s, *pos); - Box::new(Error { - message: message, - line: line, - column: column, - index: *pos, - }) + let (line, column) = get_line_and_column(s, *pos); + Box::new(Error { + message: message, + line: line, + column: column, + index: *pos, + }) } fn err(message: &'static str, s: &str, pos: &usize) -> ERes { - Err(err_impl(message, s, pos)) + Err(err_impl(message, s, pos)) } /// A helpful utility to trace the execution of a parser while testing. It will /// be compiled out in release builds. #[allow(unused_variables)] fn dbg(msg: &str, pos: &usize) { - //println!("{} @ {}", msg, pos) + //println!("{} @ {}", msg, pos) } fn atom_of_string(s: String) -> Atom { - match FromStr::from_str(&s) { - Ok(i) => return Atom::I(i), - Err(_) => {}, - }; + match FromStr::from_str(&s) { + Ok(i) => return Atom::I(i), + Err(_) => {} + }; - match FromStr::from_str(&s) { - Ok(f) => return Atom::F(f), - Err(_) => {}, - }; + match FromStr::from_str(&s) { + Ok(f) => return Atom::F(f), + Err(_) => {} + }; - Atom::S(s) + Atom::S(s) } // returns the char it found, and the new size if you wish to consume that char fn peek(s: &str, pos: &usize) -> ERes<(char, usize)> { - dbg("peek", pos); - if *pos == s.len() { return err("unexpected eof", s, pos) } - if s.is_char_boundary(*pos) { - let ch = s[*pos..].chars().next().unwrap(); - let next = *pos + ch.len_utf8(); - Ok((ch, next)) - } else { - // strings must be composed of valid utf-8 chars. - unreachable!() - } + dbg("peek", pos); + if *pos == s.len() { return err("unexpected eof", s, pos); } + if s.is_char_boundary(*pos) { + let ch = s[*pos..].chars().next().unwrap(); + let next = *pos + ch.len_utf8(); + Ok((ch, next)) + } else { + // strings must be composed of valid utf-8 chars. + unreachable!() + } } fn expect(s: &str, pos: &mut usize, c: char) -> ERes<()> { - dbg("expect", pos); - let (ch, next) = peek(s, pos)?; - *pos = next; - if ch == c { Ok(()) } else { err("unexpected character", s, pos) } + dbg("expect", pos); + let (ch, next) = peek(s, pos)?; + *pos = next; + if ch == c { Ok(()) } else { err("unexpected character", s, pos) } } fn consume_until_newline(s: &str, pos: &mut usize) -> ERes<()> { - loop { - if *pos == s.len() { return Ok(()) } - let (ch, next) = peek(s, pos)?; - *pos = next; - if ch == '\n' { return Ok(()) } - } + loop { + if *pos == s.len() { return Ok(()); } + let (ch, next) = peek(s, pos)?; + *pos = next; + if ch == '\n' { return Ok(()); } + } } // zero or more spaces fn zspace(s: &str, pos: &mut usize) -> ERes<()> { - dbg("zspace", pos); - loop { - if *pos == s.len() { return Ok(()) } - let (ch, next) = peek(s, pos)?; + dbg("zspace", pos); + loop { + if *pos == s.len() { return Ok(()); } + let (ch, next) = peek(s, pos)?; - if ch == ';' { consume_until_newline(s, pos)? } - else if ch.is_whitespace() { *pos = next; } - else { return Ok(()) } - } + if ch == ';' { consume_until_newline(s, pos)? } else if ch.is_whitespace() { *pos = next; } else { return Ok(()); } + } } fn parse_quoted_atom(s: &str, pos: &mut usize) -> ERes { - dbg("parse_quoted_atom", pos); - let mut cs: String = String::new(); - - expect(s, pos, '"')?; - - loop { - let (ch, next) = peek(s, pos)?; - if ch == '"' { - *pos = next; - break; - } else if ch == '\\' { - let (postslash, nextnext) = peek(s, &next)?; - if postslash == '"' || postslash == '\\' { - cs.push(postslash); - } else { - cs.push(ch); - cs.push(postslash); - } - *pos = nextnext; - } else { - cs.push(ch); - *pos = next; + dbg("parse_quoted_atom", pos); + let mut cs: String = String::new(); + + expect(s, pos, '"')?; + + loop { + let (ch, next) = peek(s, pos)?; + if ch == '"' { + *pos = next; + break; + } else if ch == '\\' { + let (postslash, nextnext) = peek(s, &next)?; + if postslash == '"' || postslash == '\\' { + cs.push(postslash); + } else { + cs.push(ch); + cs.push(postslash); + } + *pos = nextnext; + } else { + cs.push(ch); + *pos = next; + } } - } - // Do not try i64 conversion, since this atom was explicitly quoted. - Ok(Atom::S(cs)) + // Do not try i64 conversion, since this atom was explicitly quoted. + Ok(Atom::S(cs)) } fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes { - dbg("parse_unquoted_atom", pos); - let mut cs: String = String::new(); + dbg("parse_unquoted_atom", pos); + let mut cs: String = String::new(); - loop { - if *pos == s.len() { break } - let (c, next) = peek(s, pos)?; + loop { + if *pos == s.len() { break; } + let (c, next) = peek(s, pos)?; - if c == ';' { consume_until_newline(s, pos)?; break } - if c.is_whitespace() || c == '(' || c == ')' { break } - cs.push(c); - *pos = next; - } + if c == ';' { + consume_until_newline(s, pos)?; + break; + } + if c.is_whitespace() || c == '(' || c == ')' { break; } + cs.push(c); + *pos = next; + } - Ok(atom_of_string(cs)) + Ok(atom_of_string(cs)) } fn parse_atom(s: &str, pos: &mut usize) -> ERes { - dbg("parse_atom", pos); - let (ch, _) = peek(s, pos)?; + dbg("parse_atom", pos); + let (ch, _) = peek(s, pos)?; - if ch == '"' { parse_quoted_atom (s, pos) } - else { parse_unquoted_atom(s, pos) } + if ch == '"' { parse_quoted_atom(s, pos) } else { parse_unquoted_atom(s, pos) } } fn parse_list(s: &str, pos: &mut usize) -> ERes> { - dbg("parse_list", pos); - zspace(s, pos)?; - expect(s, pos, '(')?; + dbg("parse_list", pos); + zspace(s, pos)?; + expect(s, pos, '(')?; - let mut sexps: Vec = Vec::new(); + let mut sexps: Vec = Vec::new(); - loop { - zspace(s, pos)?; - let (c, next) = peek(s, pos)?; - if c == ')' { - *pos = next; - break; + loop { + zspace(s, pos)?; + let (c, next) = peek(s, pos)?; + if c == ')' { + *pos = next; + break; + } + sexps.push(parse_sexp(s, pos)?); } - sexps.push(parse_sexp(s, pos)?); - } - zspace(s, pos)?; + zspace(s, pos)?; - Ok(sexps) + Ok(sexps) } fn parse_sexp(s: &str, pos: &mut usize) -> ERes { - dbg("parse_sexp", pos); - zspace(s, pos)?; - let (c, _) = peek(s, pos)?; - let r = - if c == '(' { Ok(Sexp::List(parse_list(s, pos)?)) } - else { Ok(Sexp::Atom(parse_atom(s, pos)?)) }; - zspace(s, pos)?; - r + dbg("parse_sexp", pos); + zspace(s, pos)?; + let (c, _) = peek(s, pos)?; + let r = + if c == '(' { Ok(Sexp::List(parse_list(s, pos)?)) } else { Ok(Sexp::Atom(parse_atom(s, pos)?)) }; + zspace(s, pos)?; + r } /// Constructs an atomic s-expression from a string. pub fn atom_s(s: &str) -> Sexp { - Sexp::Atom(Atom::S(s.to_owned())) + Sexp::Atom(Atom::S(s.to_owned())) } /// Constructs an atomic s-expression from an int. pub fn atom_i(i: i64) -> Sexp { - Sexp::Atom(Atom::I(i)) + Sexp::Atom(Atom::I(i)) } /// Constructs an atomic s-expression from a float. pub fn atom_f(f: f64) -> Sexp { - Sexp::Atom(Atom::F(f)) + Sexp::Atom(Atom::F(f)) } /// Constructs a list s-expression given a slice of s-expressions. pub fn list(xs: &[Sexp]) -> Sexp { - Sexp::List(xs.to_owned()) + Sexp::List(xs.to_owned()) } /// Reads an s-expression out of a `&str`. #[inline(never)] pub fn parse(s: &str) -> Result> { - let mut pos = 0; - let ret = parse_sexp(s, &mut pos)?; - if pos == s.len() { Ok(ret) } else { err("unrecognized post-s-expression data", s, &pos) } + let mut pos = 0; + let ret = parse_sexp(s, &mut pos)?; + if pos == s.len() { Ok(ret) } else { err("unrecognized post-s-expression data", s, &pos) } } // TODO: Pretty print in lisp convention, instead of all on the same line, // packed as tightly as possible. It's kinda ugly. fn is_num_string(s: &str) -> bool { - let x: Result = FromStr::from_str(&s); - let y: Result = FromStr::from_str(&s); - x.is_ok() || y.is_ok() + let x: Result = FromStr::from_str(&s); + let y: Result = FromStr::from_str(&s); + x.is_ok() || y.is_ok() } fn string_contains_whitespace(s: &str) -> bool { - for c in s.chars() { - if c.is_whitespace() { return true } - } - false + for c in s.chars() { + if c.is_whitespace() { return true; } + } + false } fn quote(s: &str) -> Cow { - if !s.contains("\"") - && !string_contains_whitespace(s) - && !is_num_string(s) { - Cow::Borrowed(s) - } else { - let mut r: String = "\"".to_string(); - r.push_str(&s.replace("\\", "\\\\").replace("\"", "\\\"")); - r.push_str("\""); - Cow::Owned(r) - } + if !s.contains("\"") + && !string_contains_whitespace(s) + && !is_num_string(s) { + Cow::Borrowed(s) + } else { + let mut r: String = "\"".to_string(); + r.push_str(&s.replace("\\", "\\\\").replace("\"", "\\\"")); + r.push_str("\""); + Cow::Owned(r) + } } impl fmt::Display for Atom { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Atom::S(ref s) => write!(f, "{}", quote(s)), - Atom::I(i) => write!(f, "{}", i), - Atom::F(d) => write!(f, "{}", d), + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + Atom::S(ref s) => write!(f, "{}", quote(s)), + Atom::I(i) => write!(f, "{}", i), + Atom::F(d) => write!(f, "{}", d), + } } - } } impl fmt::Display for Sexp { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Sexp::Atom(ref a) => write!(f, "{}", a), - Sexp::List(ref xs) => { - write!(f, "(")?; - for (i, x) in xs.iter().enumerate() { - let s = if i == 0 { "" } else { " " }; - write!(f, "{}{}", s, x)?; + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + Sexp::Atom(ref a) => write!(f, "{}", a), + Sexp::List(ref xs) => { + write!(f, "(")?; + for (i, x) in xs.iter().enumerate() { + let s = if i == 0 { "" } else { " " }; + write!(f, "{}{}", s, x)?; + } + write!(f, ")") + } } - write!(f, ")") - }, } - } } impl fmt::Debug for Atom { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", self) - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self) + } } impl fmt::Debug for Sexp { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", self) - } + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self) + } } #[cfg(test)] @@ -357,14 +356,14 @@ mod test { fn test_hello_world() { assert_eq!( parse("(hello -42\n\t -4.0 \"world\") ; comment").unwrap(), - list(&[ atom_s("hello"), atom_i(-42), atom_f(-4.0), atom_s("world") ])); + list(&[atom_s("hello"), atom_i(-42), atom_f(-4.0), atom_s("world")])); } #[test] fn test_escaping() { assert_eq!( parse("(\"\\\"\\q\" \"1234\" 1234)").unwrap(), - list(&[ atom_s("\"\\q"), atom_s("1234"), atom_i(1234) ])); + list(&[atom_s("\"\\q"), atom_s("1234"), atom_i(1234)])); } #[test] @@ -388,7 +387,7 @@ mod test { #[test] fn test_space_in_atom() { - let sexp = list(&[ atom_s("hello world")]); + let sexp = list(&[atom_s("hello world")]); let sexp_as_string = sexp.to_string(); assert_eq!("(\"hello world\")", sexp_as_string); assert_eq!(sexp, parse(&sexp_as_string).unwrap()); @@ -418,6 +417,6 @@ mod test { fn sexp_size() { // I just want to see when this changes, in the diff. use std::mem; - assert_eq!(mem::size_of::(), mem::size_of::()*5); + assert_eq!(mem::size_of::(), mem::size_of::() * 5); } } From 6dfe22152ce50cd28ec648e66fb892fe68ad8081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 19:44:15 +0200 Subject: [PATCH 06/12] sexp: replace logging with log crate trace macro --- Cargo.lock | 3 ++ lib/spanned_sexp/Cargo.toml | 3 ++ lib/spanned_sexp/src/lib.rs | 102 +++++------------------------------ lib/spanned_sexp/src/test.rs | 69 ++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 88 deletions(-) create mode 100644 lib/spanned_sexp/src/test.rs diff --git a/Cargo.lock b/Cargo.lock index 8f09537..9dcf80c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -879,6 +879,9 @@ dependencies = [ [[package]] name = "sexp" version = "1.1.4" +dependencies = [ + "log", +] [[package]] name = "sha-1" diff --git a/lib/spanned_sexp/Cargo.toml b/lib/spanned_sexp/Cargo.toml index 26c6fc8..4742a78 100644 --- a/lib/spanned_sexp/Cargo.toml +++ b/lib/spanned_sexp/Cargo.toml @@ -14,3 +14,6 @@ keywords = [ "sexp", "parsing", "s-expression", "file-format" ] description = "A small, simple, self-contained, s-expression parser and pretty-printer." license = "MIT" + +[dependencies] +log = "0.4.11" diff --git a/lib/spanned_sexp/src/lib.rs b/lib/spanned_sexp/src/lib.rs index 7adab8c..23b0789 100644 --- a/lib/spanned_sexp/src/lib.rs +++ b/lib/spanned_sexp/src/lib.rs @@ -5,12 +5,18 @@ #![deny(missing_docs)] #![deny(unsafe_code)] +#[macro_use] +extern crate log; + use std::borrow::Cow; use std::cmp; use std::error; use std::fmt; use std::str::{self, FromStr}; +#[cfg(test)] +mod test; + /// A single data element in an s-expression. Floats are excluded to ensure /// atoms may be used as keys in ordered and hashed data structures. /// @@ -102,13 +108,6 @@ fn err(message: &'static str, s: &str, pos: &usize) -> ERes { Err(err_impl(message, s, pos)) } -/// A helpful utility to trace the execution of a parser while testing. It will -/// be compiled out in release builds. -#[allow(unused_variables)] -fn dbg(msg: &str, pos: &usize) { - //println!("{} @ {}", msg, pos) -} - fn atom_of_string(s: String) -> Atom { match FromStr::from_str(&s) { Ok(i) => return Atom::I(i), @@ -125,7 +124,7 @@ fn atom_of_string(s: String) -> Atom { // returns the char it found, and the new size if you wish to consume that char fn peek(s: &str, pos: &usize) -> ERes<(char, usize)> { - dbg("peek", pos); + trace!("peek {}", pos); if *pos == s.len() { return err("unexpected eof", s, pos); } if s.is_char_boundary(*pos) { let ch = s[*pos..].chars().next().unwrap(); @@ -138,7 +137,7 @@ fn peek(s: &str, pos: &usize) -> ERes<(char, usize)> { } fn expect(s: &str, pos: &mut usize, c: char) -> ERes<()> { - dbg("expect", pos); + trace!("expect {}", pos); let (ch, next) = peek(s, pos)?; *pos = next; if ch == c { Ok(()) } else { err("unexpected character", s, pos) } @@ -155,7 +154,7 @@ fn consume_until_newline(s: &str, pos: &mut usize) -> ERes<()> { // zero or more spaces fn zspace(s: &str, pos: &mut usize) -> ERes<()> { - dbg("zspace", pos); + trace!("zspace {}", pos); loop { if *pos == s.len() { return Ok(()); } let (ch, next) = peek(s, pos)?; @@ -165,7 +164,7 @@ fn zspace(s: &str, pos: &mut usize) -> ERes<()> { } fn parse_quoted_atom(s: &str, pos: &mut usize) -> ERes { - dbg("parse_quoted_atom", pos); + trace!("parse_quoted_atom {}", pos); let mut cs: String = String::new(); expect(s, pos, '"')?; @@ -195,7 +194,7 @@ fn parse_quoted_atom(s: &str, pos: &mut usize) -> ERes { } fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes { - dbg("parse_unquoted_atom", pos); + trace!("parse_unquoted_atom {}", pos); let mut cs: String = String::new(); loop { @@ -215,14 +214,14 @@ fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes { } fn parse_atom(s: &str, pos: &mut usize) -> ERes { - dbg("parse_atom", pos); + trace!("parse_atom {}", pos); let (ch, _) = peek(s, pos)?; if ch == '"' { parse_quoted_atom(s, pos) } else { parse_unquoted_atom(s, pos) } } fn parse_list(s: &str, pos: &mut usize) -> ERes> { - dbg("parse_list", pos); + trace!("parse_list {}", pos); zspace(s, pos)?; expect(s, pos, '(')?; @@ -244,7 +243,7 @@ fn parse_list(s: &str, pos: &mut usize) -> ERes> { } fn parse_sexp(s: &str, pos: &mut usize) -> ERes { - dbg("parse_sexp", pos); + trace!("parse_sexp {}", pos); zspace(s, pos)?; let (c, _) = peek(s, pos)?; let r = @@ -347,76 +346,3 @@ impl fmt::Debug for Sexp { write!(f, "{}", self) } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_hello_world() { - assert_eq!( - parse("(hello -42\n\t -4.0 \"world\") ; comment").unwrap(), - list(&[atom_s("hello"), atom_i(-42), atom_f(-4.0), atom_s("world")])); - } - - #[test] - fn test_escaping() { - assert_eq!( - parse("(\"\\\"\\q\" \"1234\" 1234)").unwrap(), - list(&[atom_s("\"\\q"), atom_s("1234"), atom_i(1234)])); - } - - #[test] - fn test_pp() { - let s = "(hello world (what is (up) (4 6.4 you \"123\\\\ \\\"\")))"; - let sexp = parse(s).unwrap(); - assert_eq!(s, sexp.to_string()); - assert_eq!(s, format!("{:?}", sexp)); - } - - #[test] - fn test_tight_parens() { - let s = "(hello(world))"; - let sexp = parse(s).unwrap(); - assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into())), - Sexp::List(vec![Sexp::Atom(Atom::S("world".into()))])])); - let s = "(this (has)tight(parens))"; - let s2 = "( this ( has ) tight ( parens ) )"; - assert_eq!(parse(s).unwrap(), parse(s2).unwrap()); - } - - #[test] - fn test_space_in_atom() { - let sexp = list(&[atom_s("hello world")]); - let sexp_as_string = sexp.to_string(); - assert_eq!("(\"hello world\")", sexp_as_string); - assert_eq!(sexp, parse(&sexp_as_string).unwrap()); - } - - #[test] - fn show_an_error() { - assert_eq!(format!("{:?}", parse("(aaaa").unwrap_err()), "1:4: unexpected eof"); - } - - #[test] - fn line_and_col_test() { - let s = "0123456789\n0123456789\n\n6"; - assert_eq!(get_line_and_column(s, 4), (1, 4)); - - assert_eq!(get_line_and_column(s, 10), (2, 0)); - assert_eq!(get_line_and_column(s, 11), (2, 0)); - assert_eq!(get_line_and_column(s, 15), (2, 4)); - - assert_eq!(get_line_and_column(s, 21), (3, 0)); - assert_eq!(get_line_and_column(s, 22), (4, 0)); - assert_eq!(get_line_and_column(s, 23), (4, 0)); - assert_eq!(get_line_and_column(s, 500), (4, 0)); - } - - #[test] - fn sexp_size() { - // I just want to see when this changes, in the diff. - use std::mem; - assert_eq!(mem::size_of::(), mem::size_of::() * 5); - } -} diff --git a/lib/spanned_sexp/src/test.rs b/lib/spanned_sexp/src/test.rs new file mode 100644 index 0000000..2ed1e98 --- /dev/null +++ b/lib/spanned_sexp/src/test.rs @@ -0,0 +1,69 @@ +use super::*; + +#[test] +fn test_hello_world() { + assert_eq!( + parse("(hello -42\n\t -4.0 \"world\") ; comment").unwrap(), + list(&[atom_s("hello"), atom_i(-42), atom_f(-4.0), atom_s("world")])); +} + +#[test] +fn test_escaping() { + assert_eq!( + parse("(\"\\\"\\q\" \"1234\" 1234)").unwrap(), + list(&[atom_s("\"\\q"), atom_s("1234"), atom_i(1234)])); +} + +#[test] +fn test_pp() { + let s = "(hello world (what is (up) (4 6.4 you \"123\\\\ \\\"\")))"; + let sexp = parse(s).unwrap(); + assert_eq!(s, sexp.to_string()); + assert_eq!(s, format!("{:?}", sexp)); +} + +#[test] +fn test_tight_parens() { + let s = "(hello(world))"; + let sexp = parse(s).unwrap(); + assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into())), + Sexp::List(vec![Sexp::Atom(Atom::S("world".into()))])])); + let s = "(this (has)tight(parens))"; + let s2 = "( this ( has ) tight ( parens ) )"; + assert_eq!(parse(s).unwrap(), parse(s2).unwrap()); +} + +#[test] +fn test_space_in_atom() { + let sexp = list(&[atom_s("hello world")]); + let sexp_as_string = sexp.to_string(); + assert_eq!("(\"hello world\")", sexp_as_string); + assert_eq!(sexp, parse(&sexp_as_string).unwrap()); +} + +#[test] +fn show_an_error() { + assert_eq!(format!("{:?}", parse("(aaaa").unwrap_err()), "1:4: unexpected eof"); +} + +#[test] +fn line_and_col_test() { + let s = "0123456789\n0123456789\n\n6"; + assert_eq!(get_line_and_column(s, 4), (1, 4)); + + assert_eq!(get_line_and_column(s, 10), (2, 0)); + assert_eq!(get_line_and_column(s, 11), (2, 0)); + assert_eq!(get_line_and_column(s, 15), (2, 4)); + + assert_eq!(get_line_and_column(s, 21), (3, 0)); + assert_eq!(get_line_and_column(s, 22), (4, 0)); + assert_eq!(get_line_and_column(s, 23), (4, 0)); + assert_eq!(get_line_and_column(s, 500), (4, 0)); +} + +#[test] +fn sexp_size() { + // I just want to see when this changes, in the diff. + use std::mem; + assert_eq!(mem::size_of::(), mem::size_of::() * 5); +} From 0fd1c9980b7db2aeaaac235b96c50464e266f05b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 19:52:00 +0200 Subject: [PATCH 07/12] sexp: move error defs to a module --- lib/spanned_sexp/src/error.rs | 68 ++++++++++++++++++++++++++++++ lib/spanned_sexp/src/lib.rs | 78 +++-------------------------------- lib/spanned_sexp/src/test.rs | 1 + 3 files changed, 75 insertions(+), 72 deletions(-) create mode 100644 lib/spanned_sexp/src/error.rs diff --git a/lib/spanned_sexp/src/error.rs b/lib/spanned_sexp/src/error.rs new file mode 100644 index 0000000..f4cdca5 --- /dev/null +++ b/lib/spanned_sexp/src/error.rs @@ -0,0 +1,68 @@ +use std::{cmp, fmt}; + +/// The representation of an s-expression parse error. +pub struct Error { + /// The error message. + pub message: &'static str, + /// The line number on which the error occurred. + pub line: usize, + /// The column number on which the error occurred. + pub column: usize, + /// The index in the given string which caused the error. + pub index: usize, +} + +/// Since errors are the uncommon case, they're boxed. This keeps the size of +/// structs down, which helps performance in the common case. +/// +/// For example, an `ERes<()>` becomes 8 bytes, instead of the 24 bytes it would +/// be if `Err` were unboxed. +type Err = Box; + +/// Helps clean up type signatures, but shouldn't be exposed to the outside +/// world. +pub(crate) type ERes = Result; + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}:{}: {}", self.line, self.column, self.message) + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self) + } +} + +impl std::error::Error for Error {} + +pub(crate) fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) { + let mut line: usize = 1; + let mut col: isize = -1; + for c in s.chars().take(pos + 1) { + if c == '\n' { + line += 1; + col = -1; + } else { + col += 1; + } + } + (line, cmp::max(col, 0) as usize) +} + +#[cold] +fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err { + let (line, column) = get_line_and_column(s, *pos); + Box::new(Error { + message: message, + line: line, + column: column, + index: *pos, + }) +} + +/// Build an error with span information +pub(crate) fn err(message: &'static str, s: &str, pos: &usize) -> ERes { + Err(err_impl(message, s, pos)) +} diff --git a/lib/spanned_sexp/src/lib.rs b/lib/spanned_sexp/src/lib.rs index 23b0789..35dee98 100644 --- a/lib/spanned_sexp/src/lib.rs +++ b/lib/spanned_sexp/src/lib.rs @@ -9,14 +9,17 @@ extern crate log; use std::borrow::Cow; -use std::cmp; -use std::error; use std::fmt; use std::str::{self, FromStr}; +pub use error::Error; +use error::{ERes, err}; + #[cfg(test)] mod test; +mod error; + /// A single data element in an s-expression. Floats are excluded to ensure /// atoms may be used as keys in ordered and hashed data structures. /// @@ -38,75 +41,6 @@ pub enum Sexp { List(Vec), } -/// The representation of an s-expression parse error. -pub struct Error { - /// The error message. - pub message: &'static str, - /// The line number on which the error occurred. - pub line: usize, - /// The column number on which the error occurred. - pub column: usize, - /// The index in the given string which caused the error. - pub index: usize, -} - -impl error::Error for Error { - fn description(&self) -> &str { self.message } - fn cause(&self) -> Option<&dyn error::Error> { None } -} - -/// Since errors are the uncommon case, they're boxed. This keeps the size of -/// structs down, which helps performance in the common case. -/// -/// For example, an `ERes<()>` becomes 8 bytes, instead of the 24 bytes it would -/// be if `Err` were unboxed. -type Err = Box; - -/// Helps clean up type signatures, but shouldn't be exposed to the outside -/// world. -type ERes = Result; - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}:{}: {}", self.line, self.column, self.message) - } -} - -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", self) - } -} - -fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) { - let mut line: usize = 1; - let mut col: isize = -1; - for c in s.chars().take(pos + 1) { - if c == '\n' { - line += 1; - col = -1; - } else { - col += 1; - } - } - (line, cmp::max(col, 0) as usize) -} - - -#[cold] -fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err { - let (line, column) = get_line_and_column(s, *pos); - Box::new(Error { - message: message, - line: line, - column: column, - index: *pos, - }) -} - -fn err(message: &'static str, s: &str, pos: &usize) -> ERes { - Err(err_impl(message, s, pos)) -} fn atom_of_string(s: String) -> Atom { match FromStr::from_str(&s) { @@ -281,7 +215,7 @@ pub fn parse(s: &str) -> Result> { } // TODO: Pretty print in lisp convention, instead of all on the same line, -// packed as tightly as possible. It's kinda ugly. +// packed as tightly as possible. It's kinda ugly. fn is_num_string(s: &str) -> bool { let x: Result = FromStr::from_str(&s); diff --git a/lib/spanned_sexp/src/test.rs b/lib/spanned_sexp/src/test.rs index 2ed1e98..ec6c23c 100644 --- a/lib/spanned_sexp/src/test.rs +++ b/lib/spanned_sexp/src/test.rs @@ -1,4 +1,5 @@ use super::*; +use super::error::get_line_and_column; #[test] fn test_hello_world() { From d4534c9c663e8474ae1369d309554e8f22cd76af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 20:23:43 +0200 Subject: [PATCH 08/12] sexp: add span info to Sexps --- lib/spanned_sexp/src/error.rs | 33 ++++++++++++++++++------- lib/spanned_sexp/src/lib.rs | 45 +++++++++++++++++++++++++---------- lib/spanned_sexp/src/test.rs | 20 ++++++++-------- 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/lib/spanned_sexp/src/error.rs b/lib/spanned_sexp/src/error.rs index f4cdca5..f3d87ad 100644 --- a/lib/spanned_sexp/src/error.rs +++ b/lib/spanned_sexp/src/error.rs @@ -4,6 +4,13 @@ use std::{cmp, fmt}; pub struct Error { /// The error message. pub message: &'static str, + /// Position in the source string where the error was detected + pub pos: SourcePosition, +} + +/// Position in the input string +#[derive(Debug, PartialEq, Clone)] +pub struct SourcePosition { /// The line number on which the error occurred. pub line: usize, /// The column number on which the error occurred. @@ -25,7 +32,7 @@ pub(crate) type ERes = Result; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}:{}: {}", self.line, self.column, self.message) + write!(f, "{}:{}: {}", self.pos.line, self.pos.column, self.message) } } @@ -37,7 +44,7 @@ impl fmt::Debug for Error { impl std::error::Error for Error {} -pub(crate) fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) { +pub(crate) fn get_line_and_column(s: &str, pos: usize) -> SourcePosition { let mut line: usize = 1; let mut col: isize = -1; for c in s.chars().take(pos + 1) { @@ -48,17 +55,18 @@ pub(crate) fn get_line_and_column(s: &str, pos: usize) -> (usize, usize) { col += 1; } } - (line, cmp::max(col, 0) as usize) + SourcePosition { + line, + column: cmp::max(col, 0) as usize, + index: pos, + } } #[cold] fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err { - let (line, column) = get_line_and_column(s, *pos); Box::new(Error { - message: message, - line: line, - column: column, - index: *pos, + message, + pos: get_line_and_column(s, *pos) }) } @@ -66,3 +74,12 @@ fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err { pub(crate) fn err(message: &'static str, s: &str, pos: &usize) -> ERes { Err(err_impl(message, s, pos)) } + +/// Build a span +pub(crate) fn spos(s: &str, pos: &usize) -> Option> { + if *pos >= s.len() { + None + } else { + Some(Box::new(get_line_and_column(s, *pos))) + } +} diff --git a/lib/spanned_sexp/src/lib.rs b/lib/spanned_sexp/src/lib.rs index 35dee98..d3b8c91 100644 --- a/lib/spanned_sexp/src/lib.rs +++ b/lib/spanned_sexp/src/lib.rs @@ -13,7 +13,8 @@ use std::fmt; use std::str::{self, FromStr}; pub use error::Error; -use error::{ERes, err}; +pub use error::SourcePosition; +use error::{ERes, err, spos}; #[cfg(test)] mod test; @@ -34,11 +35,26 @@ pub enum Atom { /// An s-expression is either an atom or a list of s-expressions. This is /// similar to the data format used by lisp. -#[derive(PartialEq, Clone, PartialOrd)] -#[allow(missing_docs)] +#[derive(Clone)] pub enum Sexp { - Atom(Atom), - List(Vec), + /// Atom + Atom(Atom, Option>), + /// List of expressions + List(Vec, Option>), +} + +impl PartialEq for Sexp { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Sexp::Atom(a, _), Sexp::Atom(b, _)) => { + a == b + } + (Sexp::List(a, _), Sexp::List(b, _)) => { + a == b + } + _ => false + } + } } @@ -180,30 +196,33 @@ fn parse_sexp(s: &str, pos: &mut usize) -> ERes { trace!("parse_sexp {}", pos); zspace(s, pos)?; let (c, _) = peek(s, pos)?; - let r = - if c == '(' { Ok(Sexp::List(parse_list(s, pos)?)) } else { Ok(Sexp::Atom(parse_atom(s, pos)?)) }; + let r = if c == '(' { + Ok(Sexp::List(parse_list(s, pos)?, spos(s, pos))) + } else { + Ok(Sexp::Atom(parse_atom(s, pos)?, spos(s, pos))) + }; zspace(s, pos)?; r } /// Constructs an atomic s-expression from a string. pub fn atom_s(s: &str) -> Sexp { - Sexp::Atom(Atom::S(s.to_owned())) + Sexp::Atom(Atom::S(s.to_owned()), None) } /// Constructs an atomic s-expression from an int. pub fn atom_i(i: i64) -> Sexp { - Sexp::Atom(Atom::I(i)) + Sexp::Atom(Atom::I(i), None) } /// Constructs an atomic s-expression from a float. pub fn atom_f(f: f64) -> Sexp { - Sexp::Atom(Atom::F(f)) + Sexp::Atom(Atom::F(f), None) } /// Constructs a list s-expression given a slice of s-expressions. pub fn list(xs: &[Sexp]) -> Sexp { - Sexp::List(xs.to_owned()) + Sexp::List(xs.to_owned(), None) } /// Reads an s-expression out of a `&str`. @@ -256,8 +275,8 @@ impl fmt::Display for Atom { impl fmt::Display for Sexp { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { - Sexp::Atom(ref a) => write!(f, "{}", a), - Sexp::List(ref xs) => { + Sexp::Atom(ref a, _) => write!(f, "{}", a), + Sexp::List(ref xs, _) => { write!(f, "(")?; for (i, x) in xs.iter().enumerate() { let s = if i == 0 { "" } else { " " }; diff --git a/lib/spanned_sexp/src/test.rs b/lib/spanned_sexp/src/test.rs index ec6c23c..f96beca 100644 --- a/lib/spanned_sexp/src/test.rs +++ b/lib/spanned_sexp/src/test.rs @@ -27,8 +27,8 @@ fn test_pp() { fn test_tight_parens() { let s = "(hello(world))"; let sexp = parse(s).unwrap(); - assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into())), - Sexp::List(vec![Sexp::Atom(Atom::S("world".into()))])])); + assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into()), None), + Sexp::List(vec![Sexp::Atom(Atom::S("world".into()), None)], None)], None)); let s = "(this (has)tight(parens))"; let s2 = "( this ( has ) tight ( parens ) )"; assert_eq!(parse(s).unwrap(), parse(s2).unwrap()); @@ -50,16 +50,16 @@ fn show_an_error() { #[test] fn line_and_col_test() { let s = "0123456789\n0123456789\n\n6"; - assert_eq!(get_line_and_column(s, 4), (1, 4)); + assert_eq!(get_line_and_column(s, 4), SourcePosition { line: 1, column: 4, index: 4 }); - assert_eq!(get_line_and_column(s, 10), (2, 0)); - assert_eq!(get_line_and_column(s, 11), (2, 0)); - assert_eq!(get_line_and_column(s, 15), (2, 4)); + assert_eq!(get_line_and_column(s, 10), SourcePosition { line: 2, column: 0, index: 10 }); + assert_eq!(get_line_and_column(s, 11), SourcePosition { line: 2, column: 0, index: 11 }); + assert_eq!(get_line_and_column(s, 15), SourcePosition { line: 2, column: 4, index: 15 }); - assert_eq!(get_line_and_column(s, 21), (3, 0)); - assert_eq!(get_line_and_column(s, 22), (4, 0)); - assert_eq!(get_line_and_column(s, 23), (4, 0)); - assert_eq!(get_line_and_column(s, 500), (4, 0)); + assert_eq!(get_line_and_column(s, 21), SourcePosition { line: 3, column: 0, index: 21 }); + assert_eq!(get_line_and_column(s, 22), SourcePosition { line: 4, column: 0, index: 22 }); + assert_eq!(get_line_and_column(s, 23), SourcePosition { line: 4, column: 0, index: 23 }); + assert_eq!(get_line_and_column(s, 500), SourcePosition { line: 4, column: 0, index: 500 }); } #[test] From 91573140a47539cf2688f3d240af8b096568aa67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 22:54:34 +0200 Subject: [PATCH 09/12] all errors now contain source location --- crsn/src/asm/data/reg.rs | 18 +++-- crsn/src/asm/error.rs | 39 +++------- crsn/src/asm/instr/cond.rs | 5 +- crsn/src/asm/instr/flatten.rs | 52 ++++++++++--- crsn/src/asm/instr/mod.rs | 3 + crsn/src/asm/instr/op.rs | 7 +- crsn/src/asm/mod.rs | 3 +- crsn/src/asm/parse/arg_parser.rs | 42 +++++++--- crsn/src/asm/parse/mod.rs | 7 +- crsn/src/asm/parse/parse_cond.rs | 8 +- crsn/src/asm/parse/parse_data.rs | 115 +++++++++++++--------------- crsn/src/asm/parse/parse_instr.rs | 21 ++--- crsn/src/asm/parse/parse_op.rs | 12 +-- crsn/src/asm/parse/parse_routine.rs | 25 +++--- crsn/src/asm/parse/sexp_expect.rs | 56 +++++--------- crsn/src/asm/patches/mod.rs | 61 ++++++++++++++- crsn/src/asm/patches/sexp_is_a.rs | 23 ------ crsn/src/asm/patches/try_remove.rs | 17 ---- crsn/src/builtin/defs.rs | 8 +- crsn/src/builtin/mod.rs | 5 +- crsn/src/builtin/parse.rs | 62 +++++++-------- crsn/src/module/mod.rs | 6 +- crsn/src/runtime/program.rs | 22 ++++-- crsn/src/utils/mod.rs | 6 +- crsn_arith/src/lib.rs | 5 +- crsn_arith/src/parse.rs | 33 ++++---- crsn_screen/src/lib.rs | 5 +- crsn_screen/src/parse.rs | 3 +- crsn_stacks/src/lib.rs | 5 +- crsn_stacks/src/parse.rs | 3 +- lib/spanned_sexp/src/error.rs | 8 +- lib/spanned_sexp/src/lib.rs | 37 +++++++-- 32 files changed, 400 insertions(+), 322 deletions(-) delete mode 100644 crsn/src/asm/patches/sexp_is_a.rs delete mode 100644 crsn/src/asm/patches/try_remove.rs diff --git a/crsn/src/asm/data/reg.rs b/crsn/src/asm/data/reg.rs index de82774..b14c593 100644 --- a/crsn/src/asm/data/reg.rs +++ b/crsn/src/asm/data/reg.rs @@ -1,6 +1,8 @@ use std::fmt::{self, Display, Formatter}; use crate::asm::error::CrsnError; +use sexp::SourcePosition; +use crate::asm::patches::ErrWithPos; /// Register name #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] @@ -23,27 +25,27 @@ impl Display for Register { } } -pub fn parse_reg(name: &str) -> anyhow::Result { +pub fn parse_reg(name: &str, at: SourcePosition) -> Result { // TODO deduplicate code if let Some(rn) = name.strip_prefix("arg") { if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() { - Err(CrsnError::Parse(format!("Bad register: {}", name).into()))?; + return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at))?; } - let val: u8 = rn.parse()?; + let val: u8 = rn.parse().err_pos(at)?; Ok(Register::Arg(val)) } else if let Some(rn) = name.strip_prefix("res") { if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() { - Err(CrsnError::Parse(format!("Bad register: {}", name).into()))?; + return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at))?; } - let val: u8 = rn.parse()?; + let val: u8 = rn.parse().err_pos(at)?; Ok(Register::Res(val)) } else if let Some(rn) = name.strip_prefix("r") { if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() { - Err(CrsnError::Parse(format!("Bad register: {}", name).into()))?; + return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at))?; } - let val: u8 = rn.parse()?; + let val: u8 = rn.parse().err_pos(at)?; Ok(Register::Gen(val)) } else { - Err(CrsnError::Parse(format!("Bad reg name: {}", name).into()))? + Err(CrsnError::Parse(format!("Bad reg name: {}", name).into(), at))? } } diff --git a/crsn/src/asm/error.rs b/crsn/src/asm/error.rs index d121c92..12744a5 100644 --- a/crsn/src/asm/error.rs +++ b/crsn/src/asm/error.rs @@ -6,28 +6,20 @@ use thiserror::Error; use crate::asm::data::{Mask, Register}; use crate::asm::data::literal::Label; use crate::asm::instr::Cond; +use sexp::SourcePosition; +use std::error::Error; /// csn_asm unified error type #[derive(Error, Debug)] pub enum CrsnError { - #[error("S-expression syntax error: {0:?}")] - PreParse(#[from] Box), - #[error("Parse error: {0:?}")] - Parse(Cow<'static, str>), - #[error("Parse error in {1:?}: {0:?}")] - ParseIn(Cow<'static, str>, sexp::Sexp), - #[error("Assembler error: {0:?}")] - Asm(AsmError), - #[error("Architecture error: {0:?}")] - Arch(ArchError), - #[error(transparent)] - Other(#[from] anyhow::Error), -} - -impl From for CrsnError { - fn from(e: ParseIntError) -> Self { - CrsnError::Other(anyhow::anyhow!(e)) - } + #[error("S-expression parsing error: {0:?}")] + Sexp(#[from] Box), + #[error("Parse error: {0:?} at {1:?}")] + Parse(Cow<'static, str>, SourcePosition), + #[error("Parse error: {0:?} at {1:?}")] + ParseOther(Box, SourcePosition), + #[error("Assembler error: {0:?} at {1:?}")] + Asm(AsmError, SourcePosition), } /// Error from the assembler stage (after parsing S-expressions and basic validation) @@ -50,14 +42,3 @@ pub enum AsmError { #[error("Bad register type: {0}")] BadRegisterType(Register), } - -/// Architectural error - the code is syntactically OK, but cannot run -#[derive(Error, Debug)] -pub enum ArchError { - #[error("Register {0} does not exist")] - RegisterNotExist(Register), - #[error("Register {0} is not writable")] - RegisterNotWritable(Register), - #[error("Register {0} is not readable")] - RegisterNotReadable(Register), -} diff --git a/crsn/src/asm/instr/cond.rs b/crsn/src/asm/instr/cond.rs index 4da4ddf..e4f7774 100644 --- a/crsn/src/asm/instr/cond.rs +++ b/crsn/src/asm/instr/cond.rs @@ -2,6 +2,7 @@ use std::fmt::{self, Display, Formatter}; use std::ops::Not; use crate::asm::error::CrsnError; +use sexp::SourcePosition; /// Condition flag #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] @@ -45,7 +46,7 @@ pub enum Cond { NotCarry, } -pub fn parse_cond(text: &str) -> Result { +pub fn parse_cond(text: &str, pos : SourcePosition) -> Result { Ok(match text.trim_end_matches('?') { "eq" | "=" | "==" => Cond::Equal, "ne" | "<>" | "!=" | "≠" => Cond::NotEqual, @@ -66,7 +67,7 @@ pub fn parse_cond(text: &str) -> Result { "ov" | "^" => Cond::Overflow, "nov" | "!ov" | "!^" => Cond::NotOverflow, _ => { - return Err(CrsnError::Parse(format!("Unknown cond: {}", text).into())); + return Err(CrsnError::Parse(format!("Unknown cond: {}", text).into(), pos)); } }) } diff --git a/crsn/src/asm/instr/flatten.rs b/crsn/src/asm/instr/flatten.rs index e2c8312..95f1a27 100644 --- a/crsn/src/asm/instr/flatten.rs +++ b/crsn/src/asm/instr/flatten.rs @@ -9,21 +9,33 @@ use crate::asm::instr::op::OpKind; use crate::builtin::defs::Barrier; use crate::builtin::defs::BuiltinOp; use std::fmt::Debug; +use sexp::SourcePosition; /// A trait for something that can turn into multiple instructions pub trait Flatten : Debug { fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError>; + + fn pos(&self) -> SourcePosition; } impl Flatten for () { fn flatten(self: Box, _label_num: &AtomicU32) -> Result, CrsnError> { Ok(vec![]) } + + fn pos(&self) -> SourcePosition { + SourcePosition::default() + } } impl Flatten for InstrWithBranches { + fn pos(&self) -> SourcePosition { + self.pos.clone() + } + fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError> { let mut ops = vec![self.op]; + let parent_pos = self.pos; if let Some(branches) = self.branches { let labels = HashMap::::new(); @@ -31,7 +43,7 @@ impl Flatten for InstrWithBranches { let end_lbl = Label::unique(label_num); for (cnt, (cond, branch)) in branches.into_iter().enumerate() { if labels.contains_key(&cond) { - return Err(CrsnError::Asm(AsmError::ConditionalAlreadyUsed(cond))); + return Err(CrsnError::Asm(AsmError::ConditionalAlreadyUsed(cond), branch.pos())); } let next_lbl = if cnt == branch_count - 1 { @@ -40,27 +52,29 @@ impl Flatten for InstrWithBranches { Label::unique(label_num) }; + let pos = branch.pos().clone(); let mut flattened = branch.flatten(label_num)?; if flattened.len() == 0 { - ops.push(Op { cond: Some(cond), kind: BuiltinOp::Jump(end_lbl.clone()).into() }); + ops.push(Op { cond: Some(cond), pos: pos.clone(), kind: BuiltinOp::Jump(end_lbl.clone()).into() }); } else if flattened.len() == 1 && flattened[0].cond.is_none() && branch_count == 1 { // optimization for single-branch conditionals with a single instruction - ops.push(Op { cond: Some(cond), kind: flattened.remove(0).kind }); + ops.push(Op { cond: Some(cond), pos: pos.clone(), kind: flattened.remove(0).kind }); } else { ops.push(Op { kind: OpKind::BuiltIn(BuiltinOp::Jump(next_lbl.clone())), + pos: pos.clone(), cond: Some(!cond), }); ops.extend(flattened); } if cnt != branch_count - 1 { - ops.push(BuiltinOp::Jump(end_lbl.clone()).into()); - ops.push(BuiltinOp::Label(next_lbl).into()); + ops.push(BuiltinOp::Jump(end_lbl.clone()).into_op(pos.clone())); + ops.push(BuiltinOp::Label(next_lbl).into_op(pos.clone())); } } - ops.push(BuiltinOp::Label(end_lbl).into()); + ops.push(BuiltinOp::Label(end_lbl).into_op(parent_pos)); } Ok(ops) @@ -68,6 +82,17 @@ impl Flatten for InstrWithBranches { } impl Flatten for Vec> { + fn pos(&self) -> SourcePosition { + match self.first() { + None => { + Default::default() + } + Some(f) => { + f.pos() + } + } + } + fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError> { let mut ops = vec![]; for item in self.into_iter() { @@ -78,15 +103,21 @@ impl Flatten for Vec> { } impl Flatten for Routine { + fn pos(&self) -> SourcePosition { + self.pos.clone() + } + fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError> { let skip_label = Label::unique(label_num); + let self_pos = self.pos(); + let mut ops: Vec = vec![ BuiltinOp::Barrier { kind: Barrier::Open(skip_label.clone()), msg: Some(format!("proc {} start", self.name).into()), - }.into(), - BuiltinOp::Routine(self.name.clone()).into(), + }.into_op(self.pos()), + BuiltinOp::Routine(self.name.clone()).into_op(self.pos()), ]; ops.extend(self.body.flatten(label_num)?); @@ -95,7 +126,7 @@ impl Flatten for Routine { BuiltinOp::Barrier { kind: Barrier::Close(skip_label.clone()), msg: Some(format!("proc {} end", self.name).into()), - }.into() + }.into_op(self_pos) ); labels_to_skips(ops) @@ -123,10 +154,11 @@ pub fn labels_to_skips(ops: Vec) -> Result, CrsnError> { let skip = *dest as isize - n as isize + skipped; cleaned.push(Op { cond: op.cond, + pos : op.pos.clone(), kind: OpKind::BuiltIn(BuiltinOp::Skip(Rd::new(RdData::Immediate(skip as Value)))), }); } else { - return Err(CrsnError::Asm(AsmError::LabelNotDefined(target))); + return Err(CrsnError::Asm(AsmError::LabelNotDefined(target), op.pos)); } } _ => { diff --git a/crsn/src/asm/instr/mod.rs b/crsn/src/asm/instr/mod.rs index 6cf8a29..28190e9 100644 --- a/crsn/src/asm/instr/mod.rs +++ b/crsn/src/asm/instr/mod.rs @@ -3,6 +3,7 @@ pub use flatten::Flatten; pub use op::Op; use crate::asm::data::literal::RoutineName; +use sexp::SourcePosition; pub mod op; pub mod cond; @@ -12,6 +13,7 @@ pub mod flatten; #[derive(Debug)] pub struct InstrWithBranches { pub op: Op, + pub pos: SourcePosition, pub branches: Option)>>, } @@ -19,5 +21,6 @@ pub struct InstrWithBranches { #[derive(Debug)] pub struct Routine { pub name: RoutineName, + pub pos: SourcePosition, pub body: Box, } diff --git a/crsn/src/asm/instr/op.rs b/crsn/src/asm/instr/op.rs index 0c6e982..e432da4 100644 --- a/crsn/src/asm/instr/op.rs +++ b/crsn/src/asm/instr/op.rs @@ -5,7 +5,7 @@ use crate::builtin::defs::BuiltinOp; use crate::module::{EvalRes, OpTrait}; use crate::runtime::fault::Fault; use crate::runtime::run_thread::{info::ThreadInfo, state::RunState}; -use sexp::{Sexp, Atom}; +use sexp::{Sexp, Atom, SourcePosition}; /// A higher level simple opration #[derive(Debug)] @@ -18,6 +18,7 @@ pub enum OpKind { #[derive(Debug)] pub struct Op { pub cond: Option, + pub pos : SourcePosition, pub kind: OpKind, } @@ -49,8 +50,8 @@ impl OpTrait for Op { }; if let Some(cond) = self.cond { - if let Sexp::List(items) = &mut se { - if let Some(Sexp::Atom(Atom::S(s))) = &mut items.get_mut(0) { + if let Sexp::List(items, _) = &mut se { + if let Some(Sexp::Atom(Atom::S(s), _)) = &mut items.get_mut(0) { s.push('.'); s.push_str(&cond.to_string()); } diff --git a/crsn/src/asm/mod.rs b/crsn/src/asm/mod.rs index 9c420c8..7217054 100644 --- a/crsn/src/asm/mod.rs +++ b/crsn/src/asm/mod.rs @@ -5,6 +5,7 @@ use crate::asm::parse::{ParserContext, ParserState}; use crate::module::CrsnExtension; use crate::runtime::program::Program; use crate::asm::instr::flatten::labels_to_skips; +use sexp::SourcePosition; pub mod data; pub mod error; @@ -23,7 +24,7 @@ pub fn assemble(source: &str, parsers: Arc>>) -> Resu }), }; - let ops = parse::parse(source, &pcx)?; + let ops = parse::parse(source, SourcePosition::default(), &pcx)?; let ops = labels_to_skips(ops)?; Ok(Program::new(ops, parsers)?) diff --git a/crsn/src/asm/parse/arg_parser.rs b/crsn/src/asm/parse/arg_parser.rs index 4ea19f0..1bbfb7e 100644 --- a/crsn/src/asm/parse/arg_parser.rs +++ b/crsn/src/asm/parse/arg_parser.rs @@ -1,14 +1,17 @@ -use sexp::Sexp; +use sexp::{Sexp, SourcePosition}; use crate::asm::data::{Mask, Rd, RdData, RdObj, Wr}; +use crate::asm::error::CrsnError; use crate::asm::parse::parse_data::{parse_rd, parse_wr}; use crate::asm::parse::ParserContext; use crate::asm::parse::sexp_expect::expect_string_atom; +use crate::asm::patches::NextOrErr; /// Utility for argument parsing pub struct TokenParser<'a> { orig_len: usize, args: Vec, + start_pos: SourcePosition, pub pcx: &'a ParserContext<'a>, } @@ -26,10 +29,11 @@ impl<'a> IntoIterator for TokenParser<'a> { impl<'a> TokenParser<'a> { /// Create a new argument parser - pub fn new(mut args: Vec, pcx: &'a ParserContext) -> Self { + pub fn new(mut args: Vec, start_pos: SourcePosition, pcx: &'a ParserContext) -> Self { args.reverse(); Self { orig_len: args.len(), + start_pos, args, pcx, } @@ -59,37 +63,51 @@ impl<'a> TokenParser<'a> { self.args.pop() } + /// Get the next entry, or raise an error + pub fn next_or_err(&mut self) -> Result { + match self.next() { + None => { + Err(CrsnError::Parse("Unexpected end of token list".into(), self.start_pos.clone())) + } + Some(removed) => Ok(removed) + } + } + /// Look at the next entry pub fn peek(&mut self) -> Option<&Sexp> { self.args.last() } /// Get the next string entry - pub fn next_string(&mut self) -> anyhow::Result { - let next = self.next(); + pub fn next_string(&mut self) -> Result<(String, SourcePosition), CrsnError> { + let next = self.next_or_err()?; let esa = expect_string_atom(next)?; Ok(esa) } /// Get the next entry as read location - pub fn next_rd(&mut self) -> anyhow::Result { - parse_rd(self.next(), self.pcx) + pub fn next_rd(&mut self) -> Result { + let next = self.next_or_err()?; + parse_rd(next, self.pcx) } /// Get the next entry as read location - pub fn next_rdobj(&mut self) -> anyhow::Result { - match parse_rd(self.next(), self.pcx)? { + pub fn next_rdobj(&mut self) -> Result { + match parse_rd(self.next_or_err()?, self.pcx)? { Rd(RdData::ObjectPtr(reg), Mask::FULL) => { - return Ok(RdObj::new(reg)); + Ok(RdObj::new(reg)) } other => { - anyhow::bail!("Not a valid object handle syntax: {:?}", other); + Err(CrsnError::Parse( + format!("Not a valid object handle syntax: {:?}", other).into(), + self.start_pos.clone(), + )) } } } /// Get the next entry as write location - pub fn next_wr(&mut self) -> anyhow::Result { - parse_wr(self.next(), self.pcx) + pub fn next_wr(&mut self) -> Result { + parse_wr(self.next_or_err()?, self.pcx) } } diff --git a/crsn/src/asm/parse/mod.rs b/crsn/src/asm/parse/mod.rs index 1c41507..cf87193 100644 --- a/crsn/src/asm/parse/mod.rs +++ b/crsn/src/asm/parse/mod.rs @@ -10,6 +10,7 @@ use crate::asm::error::CrsnError; use crate::asm::instr::Op; use crate::asm::parse::sexp_expect::expect_list; use crate::module::CrsnExtension; +use sexp::SourcePosition; pub mod parse_cond; pub mod parse_instr; @@ -38,12 +39,12 @@ pub struct ParserState { pub constants: HashMap, } -pub fn parse(source: &str, parsers: &ParserContext) -> Result, CrsnError> { - let items = expect_list(Some(sexp::parse(source)?), true)?; +pub fn parse(source: &str, pos: SourcePosition, parsers: &ParserContext) -> Result, CrsnError> { + let (items, _pos) = expect_list(sexp::parse(source)?, true)?; /* numbered labels start with a weird high number to avoid conflicts with user-defined numbered labels */ let label_num = AtomicU32::new(0x7890_0000); - parse_instructions(items.into_iter(), parsers)? + parse_instructions(items.into_iter(), pos, parsers)? .flatten(&label_num) } diff --git a/crsn/src/asm/parse/parse_cond.rs b/crsn/src/asm/parse/parse_cond.rs index 6ddc64b..bbbeb9e 100644 --- a/crsn/src/asm/parse/parse_cond.rs +++ b/crsn/src/asm/parse/parse_cond.rs @@ -8,13 +8,13 @@ use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom}; use crate::asm::patches::TryRemove; pub fn parse_cond_branch(tok: Sexp, parsers: &ParserContext) -> Result<(Cond, Box), CrsnError> { - let mut list = expect_list(Some(tok), false)?; - let kw = expect_string_atom(list.try_remove(0))?; + let (mut list, pos) = expect_list(tok, false)?; + let (kw, kw_pos) = expect_string_atom(list.remove_or_err(0, pos.clone(), "Missing \"cond?\" keyword in conditional branch")?)?; if !kw.ends_with('?') { - return Err(CrsnError::Parse(format!("Condition must end with '?': {}", kw).into())); + return Err(CrsnError::Parse(format!("Condition must end with '?': {}", kw).into(), kw_pos)); } - Ok((cond::parse_cond(&kw)?, parse_instructions(list.into_iter(), parsers)?)) + Ok((cond::parse_cond(&kw, kw_pos)?, parse_instructions(list.into_iter(), pos, parsers)?)) } diff --git a/crsn/src/asm/parse/parse_data.rs b/crsn/src/asm/parse/parse_data.rs index 8b9bbf4..436fc31 100644 --- a/crsn/src/asm/parse/parse_data.rs +++ b/crsn/src/asm/parse/parse_data.rs @@ -1,13 +1,15 @@ use std::borrow::Cow; use std::convert::TryFrom; -use sexp::{Atom, Sexp}; +use sexp::{Atom, Sexp, SourcePosition}; use crate::asm::data::{DataDisp, Rd, RdData, reg, Wr, WrData}; use crate::asm::data::literal::{ConstantName, Label, RegisterAlias, Value}; use crate::asm::error::CrsnError; use crate::asm::parse::ParserContext; use crate::asm::parse::sexp_expect::expect_string_atom; +use std::num::TryFromIntError; +use crate::asm::patches::ErrWithPos; fn is_valid_identifier(name: &str) -> bool { name.starts_with(|c: char| c.is_ascii_alphabetic() || c == '_') @@ -15,65 +17,60 @@ fn is_valid_identifier(name: &str) -> bool { } /// Parse register alias -pub fn parse_reg_alias(name: Option) -> Result { +pub fn parse_reg_alias(name: Sexp) -> Result<(RegisterAlias, SourcePosition), CrsnError> { // trace!("parse reg alias: {:?}", name); - let name = expect_string_atom(name)?; + let (name, namepos) = expect_string_atom(name)?; if !is_valid_identifier(&name) { - return Err(CrsnError::Parse(format!("\"{}\" is not an allowed register alias.", name).into())); + return Err(CrsnError::Parse(format!("\"{}\" is not an allowed register alias.", name).into(), namepos)); } - Ok(name) + Ok((name, namepos)) } /// Parse constant name -pub fn parse_constant_name(name: Option) -> Result { +pub fn parse_constant_name(name: Sexp) -> Result<(ConstantName, SourcePosition), CrsnError> { // trace!("parse const name: {:?}", name); - let name = expect_string_atom(name)?; + let (name, namepos) = expect_string_atom(name)?; if !is_valid_identifier(&name) { - return Err(CrsnError::Parse(format!("\"{}\" is not an allowed constant name.", name).into())); + return Err(CrsnError::Parse(format!("\"{}\" is not an allowed constant name.", name).into(), namepos)); } - Ok(name) + Ok((name, namepos)) } /// Parse a label -pub fn parse_label(name: Option) -> Result { +pub fn parse_label(name: Sexp) -> Result { // trace!("parse label: {:?}", name); - let name = expect_string_atom(name)?; - Ok(parse_label_str(&name)?) + let (name, namepos) = expect_string_atom(name)?; + Ok(parse_label_str(&name, namepos)?) } -pub fn parse_label_str(name: &str) -> Result { +pub fn parse_label_str(name: &str, pos: SourcePosition) -> Result { let label = name.trim_start_matches(':'); Ok(if label.starts_with('#') { - Label::Numbered(u32::try_from(parse_u64(&label[1..])?).expect("numbered label fit in u32")) + let val = parse_u64(&label[1..], pos.clone())?; + Label::Numbered(u32::try_from(val).err_pos(pos)?) } else { Label::Named(label.to_string()) }) } /// Parse data disposition (address/value, without the read/write restriction) -pub fn parse_data_disp(tok: Option, pcx: &ParserContext) -> Result { +pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result { // trace!("parse data: {:?}", tok); - let tok = if let Some(tok) = tok { - tok - } else { - return Err(CrsnError::Parse("Expected data disposition token".into())); - }; - // TODO implement masks - match &tok { - Sexp::Atom(Atom::I(val)) => { - Ok(DataDisp::Immediate(unsafe { std::mem::transmute(*val) })) + match tok { + Sexp::Atom(Atom::I(val), pos) => { + Ok(DataDisp::Immediate(unsafe { std::mem::transmute(val) })) } - Sexp::Atom(Atom::S(s)) => { + Sexp::Atom(Atom::S(s), pos) => { if s == "_" { return Ok(DataDisp::Discard); } @@ -81,11 +78,11 @@ pub fn parse_data_disp(tok: Option, pcx: &ParserContext) -> Result, pcx: &ParserContext) -> Result { - Err(CrsnError::Parse(format!("bad data disp: {:?}", tok).into())) + other => { + Err(CrsnError::Parse(format!("bad data disp: {:?}", other).into(), other.pos().clone())) } } } /// Parse immediate value -pub fn parse_value(tok: Option, pcx: &ParserContext) -> Result { - let tok = if let Some(tok) = tok { - tok - } else { - return Err(CrsnError::Parse("Expected value token".into())); - }; - - // trace!("parse value: {:?}", tok); - - match &tok { - Sexp::Atom(Atom::I(val)) => { - Ok(unsafe { std::mem::transmute(*val) }) +pub fn parse_value(tok: Sexp, pcx: &ParserContext) -> Result { + match tok { + Sexp::Atom(Atom::I(val), pos) => { + Ok(unsafe { std::mem::transmute(val) }) } - Sexp::Atom(Atom::S(s)) => { + Sexp::Atom(Atom::S(s), pos) => { let pstate = pcx.state.borrow(); - if let Some(val) = pstate.constants.get(s) { + if let Some(val) = pstate.constants.get(&s) { return Ok(*val); } - Ok(unsafe { std::mem::transmute(parse_i64(s)?) }) + Ok(unsafe { std::mem::transmute(parse_i64(&s, pos)?) }) } - _ => { - Err(CrsnError::Parse(format!("bad value format: {:?}", tok).into())) + other => { + Err(CrsnError::Parse(format!("bad value format: {:?}", other).into(), other.pos().clone())) } } } -pub fn parse_u64(literal: &str) -> anyhow::Result { +pub fn parse_u64(literal: &str, pos: SourcePosition) -> Result { // trace!("parse u64 from {}", literal); let mut without_underscores = Cow::Borrowed(literal); if without_underscores.contains('_') { @@ -159,27 +148,29 @@ pub fn parse_u64(literal: &str) -> anyhow::Result { } if let Some(hex) = without_underscores.strip_prefix("0x") { - Ok(u64::from_str_radix(hex, 16)?) + Ok(u64::from_str_radix(hex, 16).err_pos(pos)?) } else if let Some(hex) = without_underscores.strip_prefix("0b") { - Ok(u64::from_str_radix(hex, 2)?) + Ok(u64::from_str_radix(hex, 2).err_pos(pos)?) } else { - Ok(u64::from_str_radix(&without_underscores, 10)?) + Ok(u64::from_str_radix(&without_underscores, 10).err_pos(pos)?) } } -pub fn parse_i64(literal: &str) -> anyhow::Result { +pub fn parse_i64(literal: &str, pos: SourcePosition) -> Result { // trace!("parse i64 from {}", literal); if let Some(_value) = literal.strip_prefix("-") { - Ok(-1 * i64::try_from(parse_u64(literal)?)?) + Ok(-1 * i64::try_from(parse_u64(literal, pos.clone())?).err_pos(pos)?) } else { - Ok(i64::try_from(parse_u64(literal)?)?) + Ok(i64::try_from(parse_u64(literal, pos.clone())?).err_pos(pos)?) } } -pub fn parse_rd(tok: Option, pcx: &ParserContext) -> anyhow::Result { - Ok(Rd::new(RdData::try_from(parse_data_disp(tok, pcx)?)?)) +pub fn parse_rd(tok: Sexp, pcx: &ParserContext) -> Result { + let pos = tok.pos().clone(); + Ok(Rd::new(RdData::try_from(parse_data_disp(tok, pcx)?).err_pos(pos)?)) } -pub fn parse_wr(tok: Option, pcx: &ParserContext) -> anyhow::Result { - Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?)?)) +pub fn parse_wr(tok: Sexp, pcx: &ParserContext) -> Result { + let pos = tok.pos().clone(); + Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?).err_pos(pos)?)) } diff --git a/crsn/src/asm/parse/parse_instr.rs b/crsn/src/asm/parse/parse_instr.rs index ae0e3aa..7b78183 100644 --- a/crsn/src/asm/parse/parse_instr.rs +++ b/crsn/src/asm/parse/parse_instr.rs @@ -1,4 +1,4 @@ -use sexp::Sexp; +use sexp::{Sexp, SourcePosition}; use crate::asm::error::CrsnError; use crate::asm::instr::{Flatten, InstrWithBranches}; @@ -7,27 +7,27 @@ use crate::asm::parse::parse_cond::parse_cond_branch; use crate::asm::parse::parse_routine::parse_routine; use crate::asm::parse::ParserContext; use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom}; -use crate::asm::patches::SexpIsA; use crate::module::ParseRes; use super::parse_op::parse_op; +use crate::asm::patches::NextOrErr; -pub fn parse_instructions(items: impl Iterator, pcx: &ParserContext) -> Result, CrsnError> { +pub fn parse_instructions(items: impl Iterator, pos: SourcePosition, pcx: &ParserContext) -> Result, CrsnError> { let mut parsed = vec![]; for expr in items { - let tokens = expect_list(Some(expr), false)?; + let (tokens, listpos) = expect_list(expr, false)?; let mut toki = tokens.into_iter(); - let name = expect_string_atom(toki.next())?; + let (name, namepos) = expect_string_atom(toki.next_or_err(listpos.clone(), "Expected instruction name token")?)?; if name == "proc" { - parsed.push(parse_routine(toki, pcx)?); + parsed.push(parse_routine(toki, pos.clone(), pcx)?); continue; } - let mut token_parser = TokenParser::new(toki.collect(), pcx); + let mut token_parser = TokenParser::new(toki.collect(), listpos.clone(), pcx); for p in pcx.parsers { - token_parser = match p.parse_syntax(&name, token_parser) { + token_parser = match p.parse_syntax(pos.clone(), &name, token_parser) { Ok(ParseRes::Parsed(op)) => return Ok(op), Ok(ParseRes::ParsedNone) => return Ok(Box::new(())), Ok(ParseRes::Unknown(to_reuse)) => { @@ -45,7 +45,7 @@ pub fn parse_instructions(items: impl Iterator, pcx: &ParserContext) // Get back the original iterator let toki = token_parser.into_iter(); - let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), pcx); + let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), listpos.clone(), pcx); let branch_tokens = toki .skip_while(|e| e.is_atom()) .take_while(|e| e.is_list()); @@ -62,9 +62,10 @@ pub fn parse_instructions(items: impl Iterator, pcx: &ParserContext) } }; - if let Some(op) = parse_op(name.as_str(), arg_tokens)? { + if let Some(op) = parse_op(name.as_str(), arg_tokens, namepos.clone())? { parsed.push(Box::new(InstrWithBranches { op, + pos: namepos, branches, })); } diff --git a/crsn/src/asm/parse/parse_op.rs b/crsn/src/asm/parse/parse_op.rs index 0e88cac..1ceb58c 100644 --- a/crsn/src/asm/parse/parse_op.rs +++ b/crsn/src/asm/parse/parse_op.rs @@ -4,27 +4,29 @@ use crate::asm::instr::Op; use crate::asm::parse::arg_parser::TokenParser; use crate::module::ParseRes; use crate::builtin::BuiltinOps; +use sexp::SourcePosition; -pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>) -> Result, CrsnError> { +pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>, spos: SourcePosition) -> Result, CrsnError> { // Include built-in instructions let builtins = [BuiltinOps::new()]; let mut cond = None; if let Some(pos) = keyword.find('.') { - cond = Some(parse_cond(&keyword[(pos + 1)..])?); + cond = Some(parse_cond(&keyword[(pos + 1)..], spos.clone())?); keyword = &keyword[..pos]; } for p in builtins.iter().chain(arg_tokens.pcx.parsers) { - arg_tokens = match p.parse_op(keyword, arg_tokens) { + arg_tokens = match p.parse_op(spos.clone(), keyword, arg_tokens) { Ok(ParseRes::Parsed(kind)) => return Ok(Some(Op { cond, + pos: spos, kind, })), Ok(ParseRes::ParsedNone) => return Ok(None), Ok(ParseRes::Unknown(to_reuse)) => { if to_reuse.parsing_started() { - panic!("Module \"{}\" started parsing {}, but returned Unknown!", p.name(), keyword); + return Err(CrsnError::Parse(format!("Module \"{}\" started parsing {}, but returned Unknown!", p.name(), keyword).into(), spos)); } to_reuse } @@ -34,5 +36,5 @@ pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>) -> Resul } } - return Err(CrsnError::Parse(format!("Unknown instruction: {}", keyword).into())); + return Err(CrsnError::Parse(format!("Unknown instruction: {}", keyword).into(), spos)); } diff --git a/crsn/src/asm/parse/parse_routine.rs b/crsn/src/asm/parse/parse_routine.rs index 3e79e30..34819aa 100644 --- a/crsn/src/asm/parse/parse_routine.rs +++ b/crsn/src/asm/parse/parse_routine.rs @@ -1,4 +1,4 @@ -use sexp::Sexp; +use sexp::{Sexp, SourcePosition}; use crate::asm::data::Register; use crate::asm::error::CrsnError; @@ -7,14 +7,14 @@ use crate::asm::parse::{parse_instructions, ParserContext}; use crate::asm::parse::arg_parser::TokenParser; use crate::asm::parse::parse_data::parse_reg_alias; use crate::asm::parse::sexp_expect::expect_string_atom; -use crate::asm::patches::SexpIsA; use crate::builtin::parse::parse_routine_name; +use crate::asm::patches::NextOrErr; -pub fn parse_routine(mut toki: impl Iterator + Clone, pcx: &ParserContext) -> Result, CrsnError> { - let name = expect_string_atom(toki.next())?; - let mut name = parse_routine_name(name)?; +pub fn parse_routine(mut toki: impl Iterator + Clone, rt_pos: SourcePosition, pcx: &ParserContext) -> Result, CrsnError> { + let (name, namepos) = expect_string_atom(toki.next_or_err(rt_pos.clone(), "Expected routine name")?)?; + let mut name = parse_routine_name(name, namepos)?; - let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), pcx); + let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), rt_pos.clone(), pcx); // If arity is explicitly given, then either no named argument must be provided, // or their count must match the arity. If no arity is given, then arity is determined @@ -22,7 +22,7 @@ pub fn parse_routine(mut toki: impl Iterator + Clone, pcx: &ParserCon if name.arity == 0 && arg_tokens.len() != 0 { name.arity = arg_tokens.len() as u8; } else if arg_tokens.len() != 0 && name.arity as usize != arg_tokens.len() { - return Err(CrsnError::Parse(format!("arity mismatch in routine {}", name.name).into())); + return Err(CrsnError::Parse(format!("arity mismatch in routine {}", name.name).into(), rt_pos)); } let toki = toki.skip_while(|e| e.is_atom()); @@ -34,17 +34,17 @@ pub fn parse_routine(mut toki: impl Iterator + Clone, pcx: &ParserCon pstate.reg_alias_stack.push(old); for (n, tok) in arg_tokens.into_iter().enumerate() { - let alias = parse_reg_alias(Some(tok))?; + let alias = parse_reg_alias(tok)?; - if pstate.constants.contains_key(&alias) { - return Err(CrsnError::Parse(format!("Symbol \"{}\" already used for a constant.", alias).into())); + if pstate.constants.contains_key(&alias.0) { + return Err(CrsnError::Parse(format!("Symbol \"{}\" already used for a constant.", alias.0).into(), alias.1)); } - pstate.reg_aliases.insert(alias, Register::Arg(n as u8)); + pstate.reg_aliases.insert(alias.0, Register::Arg(n as u8)); } } - let body = parse_instructions(toki, pcx)?; + let body = parse_instructions(toki, rt_pos.clone(), pcx)?; { let mut pstate = pcx.state.borrow_mut(); @@ -54,6 +54,7 @@ pub fn parse_routine(mut toki: impl Iterator + Clone, pcx: &ParserCon return Ok(Box::new(Routine { name, + pos: rt_pos, body, })); } diff --git a/crsn/src/asm/parse/sexp_expect.rs b/crsn/src/asm/parse/sexp_expect.rs index c0d8969..eb2da85 100644 --- a/crsn/src/asm/parse/sexp_expect.rs +++ b/crsn/src/asm/parse/sexp_expect.rs @@ -1,53 +1,37 @@ -use sexp::{Atom, Sexp}; +use sexp::{Atom, Sexp, SourcePosition}; use crate::asm::error::CrsnError; -pub fn expect_list(expr: Option, allow_empty: bool) -> Result, CrsnError> { - if let Some(expr) = expr { - match &expr { - Sexp::Atom(_) => { - return Err(CrsnError::ParseIn("Expected a list".into(), expr)); +pub fn expect_list(expr: Sexp, allow_empty: bool) -> Result<(Vec, SourcePosition), CrsnError> { + match expr { + Sexp::Atom(_, pos) => { + return Err(CrsnError::Parse("Expected a list".into(), pos)); + } + Sexp::List(list, pos) => { + if !allow_empty && list.is_empty() { + return Err(CrsnError::Parse("Routine: Empty list".into(), pos)); } - Sexp::List(list) => { - if !allow_empty && list.is_empty() { - return Err(CrsnError::ParseIn("Routine: Empty list".into(), expr)); - } - if let Sexp::List(list) = expr { - return Ok(list); - } else { - unreachable!(); - } - } + Ok((list, pos)) } } - - Err(CrsnError::Parse("Expected a list, got nothing".into())) } -pub fn expect_atom(expr: Option) -> Result { - if let Some(expr) = expr { - match &expr { - Sexp::Atom(_atom) => { - if let Sexp::Atom(a) = expr { - return Ok(a); - } else { - unreachable!(); - } - } - Sexp::List(_) => { - return Err(CrsnError::ParseIn("Expected atom got list".into(), expr)); - } +pub fn expect_atom(expr: Sexp) -> Result<(Atom, SourcePosition), CrsnError> { + match expr { + Sexp::Atom(a, pos) => { + Ok((a, pos)) + } + Sexp::List(_, pos) => { + return Err(CrsnError::Parse("Expected atom got list".into(), pos)); } } - - Err(CrsnError::Parse("Expected atom, got nothing".into())) } -pub fn expect_string_atom(expr: Option) -> Result { +pub fn expect_string_atom(expr: Sexp) -> Result<(String, SourcePosition), CrsnError> { match expect_atom(expr) { - Ok(Atom::S(s)) => Ok(s), - Ok(atom) => Err(CrsnError::ParseIn("Expected string atom".into(), Sexp::Atom(atom))), + Ok((Atom::S(s), pos)) => Ok((s, pos)), + Ok((_, pos)) => Err(CrsnError::Parse("Expected string atom".into(), pos)), Err(e) => Err(e), } } diff --git a/crsn/src/asm/patches/mod.rs b/crsn/src/asm/patches/mod.rs index 287f150..aa299b5 100644 --- a/crsn/src/asm/patches/mod.rs +++ b/crsn/src/asm/patches/mod.rs @@ -1,6 +1,59 @@ -pub use sexp_is_a::SexpIsA; -pub use try_remove::TryRemove; +use sexp::SourcePosition; +use crate::asm::error::CrsnError; +use std::borrow::Cow; -mod try_remove; -mod sexp_is_a; +pub trait TryRemove { + type Item; + fn try_remove(&mut self, index: usize) -> Option; + fn remove_or_err(&mut self, index: usize, pos: SourcePosition, err : &'static str) -> Result; +} + +impl TryRemove for Vec { + type Item = T; + + fn try_remove(&mut self, index: usize) -> Option { + if self.is_empty() { + None + } else { + Some(self.remove(index)) + } + } + + fn remove_or_err(&mut self, index: usize, pos: SourcePosition, err : &'static str) -> Result { + match self.try_remove(index) { + None => { + Err(CrsnError::Parse(err.into(), pos)) + } + Some(removed) => Ok(removed) + } + } +} + +pub trait NextOrErr { + fn next_or_err(&mut self, pos: SourcePosition, err : &'static str) -> Result; +} + +impl> NextOrErr for K { + fn next_or_err(&mut self, pos: SourcePosition, err: &'static str) -> Result { + match self.next() { + None => { + Err(CrsnError::Parse(err.into(), pos)) + } + Some(removed) => Ok(removed) + } + } +} + +pub trait ErrWithPos { + fn err_pos(self, pos : SourcePosition) -> Result; +} + +impl ErrWithPos for Result { + fn err_pos(self, pos : SourcePosition) -> Result { + match self { + Ok(v) => Ok(v), + Err(e) => Err(CrsnError::ParseOther(Box::new(e), pos)) + } + } +} diff --git a/crsn/src/asm/patches/sexp_is_a.rs b/crsn/src/asm/patches/sexp_is_a.rs deleted file mode 100644 index f620eed..0000000 --- a/crsn/src/asm/patches/sexp_is_a.rs +++ /dev/null @@ -1,23 +0,0 @@ -use sexp::Sexp; - -pub trait SexpIsA { - fn is_atom(&self) -> bool; - - fn is_list(&self) -> bool; -} - -impl SexpIsA for Sexp { - fn is_atom(&self) -> bool { - match self { - Sexp::Atom(_) => true, - _ => false, - } - } - - fn is_list(&self) -> bool { - match self { - Sexp::List(_) => true, - _ => false, - } - } -} diff --git a/crsn/src/asm/patches/try_remove.rs b/crsn/src/asm/patches/try_remove.rs deleted file mode 100644 index 1ac3913..0000000 --- a/crsn/src/asm/patches/try_remove.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub trait TryRemove { - type Item; - fn try_remove(&mut self, index: usize) -> Option; -} - -impl TryRemove for Vec { - type Item = T; - - fn try_remove(&mut self, index: usize) -> Option { - if self.is_empty() { - None - } else { - Some(self.remove(index)) - } - } -} - diff --git a/crsn/src/builtin/defs.rs b/crsn/src/builtin/defs.rs index a20ae1d..686f432 100644 --- a/crsn/src/builtin/defs.rs +++ b/crsn/src/builtin/defs.rs @@ -2,6 +2,7 @@ use crate::asm::data::{Rd, RdObj, Wr}; use crate::asm::data::literal::{DebugMsg, Label, RoutineName}; use crate::asm::instr::Op; use crate::asm::instr::op::OpKind; +use sexp::SourcePosition; #[derive(Debug)] pub enum Barrier { @@ -60,10 +61,11 @@ pub enum BuiltinOp { LoadStatus { src: Rd }, } -impl From for Op { - fn from(bo: BuiltinOp) -> Self { +impl BuiltinOp { + pub fn into_op(self: BuiltinOp, pos: SourcePosition) -> Op { Op { - kind: bo.into(), + kind: self.into(), + pos, cond: None, } } diff --git a/crsn/src/builtin/mod.rs b/crsn/src/builtin/mod.rs index c9c42fa..0fa9a9f 100644 --- a/crsn/src/builtin/mod.rs +++ b/crsn/src/builtin/mod.rs @@ -2,6 +2,7 @@ use crate::module::{CrsnExtension, ParseRes}; use crate::asm::parse::arg_parser::TokenParser; use crate::asm::instr::op::OpKind; use crate::asm::error::CrsnError; +use sexp::SourcePosition; pub mod defs; pub mod exec; @@ -21,7 +22,7 @@ impl CrsnExtension for BuiltinOps { "builtin" } - fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { - parse::parse_op(keyword, args) + fn parse_op<'a>(&self, pos: SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { + parse::parse_op(pos, keyword, args) } } diff --git a/crsn/src/builtin/parse.rs b/crsn/src/builtin/parse.rs index 779bea6..873fd90 100644 --- a/crsn/src/builtin/parse.rs +++ b/crsn/src/builtin/parse.rs @@ -1,4 +1,4 @@ -use sexp::{Atom, Sexp}; +use sexp::{Atom, Sexp, SourcePosition}; use crate::asm::data::literal::{Label, RoutineName}; use crate::asm::data::reg::parse_reg; @@ -10,9 +10,10 @@ use crate::asm::parse::sexp_expect::expect_string_atom; use crate::builtin::defs::{Barrier, BuiltinOp}; use crate::module::{ParseRes}; use crate::utils::A; +use crate::asm::patches::ErrWithPos; -pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { +pub(crate) fn parse_op<'a>(op_pos: SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { let pcx = args.pcx; Ok(ParseRes::Parsed(OpKind::BuiltIn(match keyword { @@ -31,22 +32,23 @@ pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result

{ - let alias = parse_reg_alias(args.next())?; + let (alias, aliaspos) = parse_reg_alias(args.next_or_err()?)?; trace!("alias={:?}", alias); - let register = parse_reg(&args.next_string()?)?; + let (rn, rpos) = args.next_string()?; + let register = parse_reg(&rn, rpos.clone())?; trace!("register={:?}", alias); let mut pstate = pcx.state.borrow_mut(); if pstate.reg_aliases.contains_key(&alias) { - return Err(CrsnError::Parse(format!("Register alias \"{}\" already defined!", alias).into())); + return Err(CrsnError::Parse(format!("Register alias \"{}\" already defined!", alias).into(), rpos)); } if pstate.constants.contains_key(&alias) { - return Err(CrsnError::Parse(format!("Name \"{}\" already used for a constant!", alias).into())); + return Err(CrsnError::Parse(format!("Name \"{}\" already used for a constant!", alias).into(), aliaspos)); } if pstate.reg_aliases.iter().find(|x| x.1 == ®ister).is_some() { - return Err(CrsnError::Parse(format!("Register \"{}\" already aliased!", register).into())); + return Err(CrsnError::Parse(format!("Register \"{}\" already aliased!", register).into(), rpos)); } pstate.reg_aliases.insert(alias, register); @@ -55,26 +57,26 @@ pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result

{ - let alias = parse_reg_alias(args.next())?; + let alias = parse_reg_alias(args.next_or_err()?)?; let mut pstate = pcx.state.borrow_mut(); - if pstate.reg_aliases.remove(&alias).is_none() { - return Err(CrsnError::Parse(format!("Register alias \"{}\" not defined!", alias).into())); + if pstate.reg_aliases.remove(&alias.0).is_none() { + return Err(CrsnError::Parse(format!("Register alias \"{}\" not defined!", alias.0).into(), alias.1)); } return Ok(ParseRes::ParsedNone); } "def" => { - let name = parse_constant_name(args.next())?; - let value = parse_value(args.next(), pcx)?; + let (name, namepos) = parse_constant_name(args.next_or_err()?)?; + let value = parse_value(args.next_or_err()?, pcx)?; let mut pstate = pcx.state.borrow_mut(); if pstate.constants.contains_key(&name) { - return Err(CrsnError::Parse(format!("Constant \"{}\" already defined!", name).into())); + return Err(CrsnError::Parse(format!("Constant \"{}\" already defined!", name).into(), namepos)); } if pstate.reg_aliases.contains_key(&name) { - return Err(CrsnError::Parse(format!("Name \"{}\" already used for a register alias!", name).into())); + return Err(CrsnError::Parse(format!("Name \"{}\" already used for a register alias!", name).into(), namepos)); } pstate.constants.insert(name, value); @@ -83,31 +85,31 @@ pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result

{ - let name = parse_constant_name(args.next())?; + let (name, namepos) = parse_constant_name(args.next_or_err()?)?; let mut pstate = pcx.state.borrow_mut(); if pstate.constants.remove(&name).is_none() { - return Err(CrsnError::Parse(format!("Constant \"{}\" not defined!", name).into())); + return Err(CrsnError::Parse(format!("Constant \"{}\" not defined!", name).into(), namepos)); } return Ok(ParseRes::ParsedNone); } "j" => { - let dest = parse_label(args.next())?; + let dest = parse_label(args.next_or_err()?)?; BuiltinOp::Jump(dest) } "fj" => { - let dest = parse_label(args.next())?; + let dest = parse_label(args.next_or_err()?)?; BuiltinOp::FarJump(dest) } "call" => { - let dest = RoutineName { name: args.next_string()?, arity: args.len() as u8 }; + let dest = RoutineName { name: args.next_string()?.0, arity: args.len() as u8 }; let mut call_args = vec![]; for t in args { - call_args.push(parse_rd(Some(t), pcx)?); + call_args.push(parse_rd(t, pcx)?); } BuiltinOp::Call(dest, call_args) } @@ -115,14 +117,14 @@ pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result

{ let mut ret_vals = vec![]; for t in args { - ret_vals.push(parse_rd(Some(t), pcx)?); + ret_vals.push(parse_rd(t, pcx)?); } BuiltinOp::Ret(ret_vals) } "routine" => { let name = args.next_string()?; - BuiltinOp::Routine(parse_routine_name(name)?) + BuiltinOp::Routine(parse_routine_name(name.0, name.1)?) } "skip" => { @@ -134,21 +136,21 @@ pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result

None, - Some(s) => Some(expect_string_atom(Some(s))?.into()), + Some(s) => Some(expect_string_atom(s)?.0.into()), }, } } "barrier-open" => { BuiltinOp::Barrier { - kind: Barrier::Open(parse_label(args.next())?), + kind: Barrier::Open(parse_label(args.next_or_err()?)?), msg: None, } } "barrier-close" => { BuiltinOp::Barrier { - kind: Barrier::Close(parse_label(args.next())?), + kind: Barrier::Close(parse_label(args.next_or_err()?)?), msg: None, } } @@ -156,7 +158,7 @@ pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result

{ BuiltinOp::Fault(match args.next() { None => None, - Some(s) => Some(expect_string_atom(Some(s))?.into()), + Some(s) => Some(expect_string_atom(s)?.0.into()), }) } @@ -184,7 +186,7 @@ pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result

{ - if let Some(Sexp::Atom(Atom::S(ref label))) = args.peek() { + if let Some(Sexp::Atom(Atom::S(ref label), _)) = args.peek() { if let Some(label) = label.strip_prefix(':') { let label = Label::Named(label.to_string()); BuiltinOp::FarLabel(label) @@ -198,7 +200,7 @@ pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result

{ if let Some(label) = other.strip_prefix(':') { - BuiltinOp::Label(parse_label_str(label)?) + BuiltinOp::Label(parse_label_str(label, op_pos)?) } else { return Ok(ParseRes::Unknown(args)); } @@ -206,11 +208,11 @@ pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result

Result { +pub(crate) fn parse_routine_name(name: String, pos: SourcePosition) -> Result { let (name, arity) = if let Some(n) = name.find('/') { ( (&name[0..n]).to_string(), - (&name[(n + 1)..]).parse::()? + (&name[(n + 1)..]).parse::().err_pos(pos)? ) } else { (name, 0) diff --git a/crsn/src/module/mod.rs b/crsn/src/module/mod.rs index 257406a..77ff1ac 100644 --- a/crsn/src/module/mod.rs +++ b/crsn/src/module/mod.rs @@ -13,7 +13,7 @@ use crate::asm::parse::arg_parser::TokenParser; use crate::runtime::fault::Fault; use crate::runtime::run_thread::state::RunState; use crate::runtime::run_thread::ThreadInfo; -use sexp::Sexp; +use sexp::{Sexp, SourcePosition}; mod eval_res; @@ -53,12 +53,12 @@ pub trait CrsnExtension: Debug + Send + Sync + 'static { /// If the instruction keyword is not recognized, return Unknown with the unchanged argument list. /// /// pcx is available on the arg_tokens parser - fn parse_op<'a>(&self, keyword: &str, arg_tokens: TokenParser<'a>) -> Result, CrsnError>; + fn parse_op<'a>(&self, pos: SourcePosition, keyword: &str, arg_tokens: TokenParser<'a>) -> Result, CrsnError>; /// Parse a generic S-expression (non-op) /// /// pcx is available on the arg_tokens parser - fn parse_syntax<'a>(&self, keyword: &str, tokens: TokenParser<'a>) + fn parse_syntax<'a>(&self, pos: SourcePosition, keyword: &str, tokens: TokenParser<'a>) -> Result>, CrsnError> { Ok(ParseRes::Unknown(tokens)) diff --git a/crsn/src/runtime/program.rs b/crsn/src/runtime/program.rs index b45d89e..279e43e 100644 --- a/crsn/src/runtime/program.rs +++ b/crsn/src/runtime/program.rs @@ -7,6 +7,8 @@ use crate::asm::instr::op::OpKind; use crate::builtin::defs::{Barrier, BuiltinOp}; use crate::module::CrsnExtension; use crate::runtime::fault::Fault; +use sexp::SourcePosition; +use crate::asm::error::CrsnError; #[derive(Debug)] pub struct Program { @@ -20,7 +22,7 @@ pub struct Program { } impl Program { - pub fn new(ops: Vec, extensions: Arc>>) -> anyhow::Result> { + pub fn new(ops: Vec, extensions: Arc>>) -> Result, CrsnError> { let mut p = Self { ops, extensions, @@ -33,7 +35,7 @@ impl Program { } /// Find all the named things - fn scan(&mut self) -> anyhow::Result<()> { + fn scan(&mut self) -> Result<(), CrsnError> { let mut barrier_starts: HashMap<&Label, Addr> = HashMap::new(); for (pos, op) in self.ops.iter().enumerate() { match &op.kind { @@ -60,7 +62,7 @@ impl Program { self.barriers.push((start_pos, pos.into())); self.far_labels.insert(lbl.clone(), pos.into()); } else { - anyhow::bail!("Block barrier \"{:?}\" closed without being open!", msg); + return Err(CrsnError::Parse(format!("Block barrier \"{:?}\" closed without being open!", msg).into(), op.pos.clone())); } } OpKind::BuiltIn( @@ -76,7 +78,9 @@ impl Program { } if !barrier_starts.is_empty() { - anyhow::bail!("Some block barriers open without being closed!"); + return Err(CrsnError::Parse(format!("Block barrier open without being closed: {}", + barrier_starts.iter().next().unwrap().0).into(), + Default::default())); } trace!("Program scanned: {:?}", self); @@ -87,7 +91,15 @@ impl Program { /// Read a program instruction at address pub fn read(&self, addr: Addr) -> &Op { if addr.0 >= self.ops.len() as u64 { - &Op { kind: OpKind::BuiltIn(BuiltinOp::Halt), cond: None } + &Op { + kind: OpKind::BuiltIn(BuiltinOp::Halt), + pos: SourcePosition { + line: 0, + column: 0, + index: 0 + }, + cond: None + } } else { &self.ops[addr.0 as usize] } diff --git a/crsn/src/utils/mod.rs b/crsn/src/utils/mod.rs index d2e94f1..7a365ce 100644 --- a/crsn/src/utils/mod.rs +++ b/crsn/src/utils/mod.rs @@ -12,13 +12,13 @@ pub fn A(s: impl Display) -> Sexp { let x: Result = FromStr::from_str(&s); if let Ok(x) = x { - return Sexp::Atom(Atom::I(x)); + return Sexp::Atom(Atom::I(x), Default::default()); } let y: Result = FromStr::from_str(&s); if let Ok(y) = y { - return Sexp::Atom(Atom::F(y)); + return Sexp::Atom(Atom::F(y), Default::default()); } - Sexp::Atom(Atom::S(s)) + Sexp::Atom(Atom::S(s), Default::default()) } diff --git a/crsn_arith/src/lib.rs b/crsn_arith/src/lib.rs index 12a87e8..ba06694 100644 --- a/crsn_arith/src/lib.rs +++ b/crsn_arith/src/lib.rs @@ -2,6 +2,7 @@ use crsn::asm::error::CrsnError; use crsn::asm::instr::op::OpKind; use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::{CrsnExtension, ParseRes}; +use crsn::sexp::SourcePosition; mod defs; mod parse; @@ -21,7 +22,7 @@ impl CrsnExtension for ArithOps { "arith" } - fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { - parse::parse(keyword, args) + fn parse_op<'a>(&self, pos: SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { + parse::parse(pos, keyword, args) } } diff --git a/crsn_arith/src/parse.rs b/crsn_arith/src/parse.rs index 2b3b6fe..4248a11 100644 --- a/crsn_arith/src/parse.rs +++ b/crsn_arith/src/parse.rs @@ -5,8 +5,9 @@ use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::ParseRes; use crate::defs::ArithOp; +use crsn::sexp::SourcePosition; -pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { +pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { "cmp" => { ArithOp::Compare { @@ -56,7 +57,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Add requires 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Add requires 2 or 3 arguments".into(), pos)); } } } @@ -79,7 +80,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Sub requires 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Sub requires 2 or 3 arguments".into(), pos)); } } } @@ -102,7 +103,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Mul requires 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Mul requires 2 or 3 arguments".into(), pos)); } } } @@ -129,7 +130,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("DivR requires 3 or 4 arguments".into())); + return Err(CrsnError::Parse("DivR requires 3 or 4 arguments".into(), pos)); } } } @@ -155,7 +156,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Div requires 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Div requires 2 or 3 arguments".into(), pos)); } } } @@ -179,7 +180,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Mod requires 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Mod requires 2 or 3 arguments".into(), pos)); } } } @@ -202,7 +203,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("And requires 2 or 3 arguments".into())); + return Err(CrsnError::Parse("And requires 2 or 3 arguments".into(), pos)); } } } @@ -225,7 +226,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Or requires 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Or requires 2 or 3 arguments".into(), pos)); } } } @@ -248,7 +249,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Xor requires 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Xor requires 2 or 3 arguments".into(), pos)); } } } @@ -269,7 +270,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Cpl requires 1 or 2 arguments".into())); + return Err(CrsnError::Parse("Cpl requires 1 or 2 arguments".into(), pos)); } } } @@ -300,7 +301,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Rol requires 1, 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Rol requires 1, 2 or 3 arguments".into(), pos)); } } } @@ -331,7 +332,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Ror requires 1, 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Ror requires 1, 2 or 3 arguments".into(), pos)); } } } @@ -362,7 +363,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Lsl requires 1, 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Lsl requires 1, 2 or 3 arguments".into(), pos)); } } } @@ -393,7 +394,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Lsr requires 1, 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Lsr requires 1, 2 or 3 arguments".into(), pos)); } } } @@ -424,7 +425,7 @@ pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result { - return Err(CrsnError::Parse("Asr requires 1, 2 or 3 arguments".into())); + return Err(CrsnError::Parse("Asr requires 1, 2 or 3 arguments".into(), pos)); } } } diff --git a/crsn_screen/src/lib.rs b/crsn_screen/src/lib.rs index c694e55..9b569f5 100644 --- a/crsn_screen/src/lib.rs +++ b/crsn_screen/src/lib.rs @@ -5,6 +5,7 @@ use crsn::asm::error::CrsnError; use crsn::asm::instr::op::OpKind; use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::{CrsnExtension, ParseRes}; +use crsn::sexp::SourcePosition; mod defs; mod parse; @@ -24,7 +25,7 @@ impl CrsnExtension for ScreenOps { "screen" } - fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { - parse::parse(keyword, args) + fn parse_op<'a>(&self, pos: SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { + parse::parse(pos, keyword, args) } } diff --git a/crsn_screen/src/parse.rs b/crsn_screen/src/parse.rs index ea1afe2..f73c20b 100644 --- a/crsn_screen/src/parse.rs +++ b/crsn_screen/src/parse.rs @@ -5,8 +5,9 @@ use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::ParseRes; use crate::defs::ScreenOp; +use crsn::sexp::SourcePosition; -pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { +pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { "sc-init" => { ScreenOp::ScreenInit { diff --git a/crsn_stacks/src/lib.rs b/crsn_stacks/src/lib.rs index c61336e..6059f4e 100644 --- a/crsn_stacks/src/lib.rs +++ b/crsn_stacks/src/lib.rs @@ -5,6 +5,7 @@ use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::{CrsnExtension, ParseRes}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{RunState, ThreadInfo}; +use crsn::sexp::SourcePosition; mod defs; mod parse; @@ -24,8 +25,8 @@ impl CrsnExtension for StackOps { "stacks" } - fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { - parse::parse(keyword, args) + fn parse_op<'a>(&self, pos: SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { + parse::parse(pos, keyword, args) } fn drop_obj(&self, _ti: &ThreadInfo, state: &mut RunState, handle: Value) -> Result, Fault> { diff --git a/crsn_stacks/src/parse.rs b/crsn_stacks/src/parse.rs index b1206e3..0458180 100644 --- a/crsn_stacks/src/parse.rs +++ b/crsn_stacks/src/parse.rs @@ -4,8 +4,9 @@ use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::ParseRes; use crate::defs::StackOp; +use crsn::sexp::SourcePosition; -pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { +pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { "stack" => { StackOp::NewStack { diff --git a/lib/spanned_sexp/src/error.rs b/lib/spanned_sexp/src/error.rs index f3d87ad..34ab7aa 100644 --- a/lib/spanned_sexp/src/error.rs +++ b/lib/spanned_sexp/src/error.rs @@ -9,7 +9,7 @@ pub struct Error { } /// Position in the input string -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Default)] pub struct SourcePosition { /// The line number on which the error occurred. pub line: usize, @@ -76,10 +76,10 @@ pub(crate) fn err(message: &'static str, s: &str, pos: &usize) -> ERes { } /// Build a span -pub(crate) fn spos(s: &str, pos: &usize) -> Option> { +pub(crate) fn spos(s: &str, pos: &usize) -> SourcePosition { if *pos >= s.len() { - None + Default::default() } else { - Some(Box::new(get_line_and_column(s, *pos))) + get_line_and_column(s, *pos) } } diff --git a/lib/spanned_sexp/src/lib.rs b/lib/spanned_sexp/src/lib.rs index d3b8c91..ee0ba18 100644 --- a/lib/spanned_sexp/src/lib.rs +++ b/lib/spanned_sexp/src/lib.rs @@ -2,7 +2,6 @@ //! Use `parse` to get an s-expression from its string representation, and the //! `Display` trait to serialize it, potentially by doing `sexp.to_string()`. -#![deny(missing_docs)] #![deny(unsafe_code)] #[macro_use] @@ -38,9 +37,33 @@ pub enum Atom { #[derive(Clone)] pub enum Sexp { /// Atom - Atom(Atom, Option>), + Atom(Atom, SourcePosition), /// List of expressions - List(Vec, Option>), + List(Vec, SourcePosition), +} + +impl Sexp { + pub fn pos(&self) -> &SourcePosition { + match self { + Sexp::List(_, pos) | Sexp::Atom(_, pos) => pos + } + } + + /// Check fi thsi Sexp is an atom + pub fn is_atom(&self) -> bool { + match self { + Sexp::Atom(_, _) => true, + _ => false, + } + } + + /// Check fi thsi Sexp is a list + pub fn is_list(&self) -> bool { + match self { + Sexp::List(_, _) => true, + _ => false, + } + } } impl PartialEq for Sexp { @@ -207,22 +230,22 @@ fn parse_sexp(s: &str, pos: &mut usize) -> ERes { /// Constructs an atomic s-expression from a string. pub fn atom_s(s: &str) -> Sexp { - Sexp::Atom(Atom::S(s.to_owned()), None) + Sexp::Atom(Atom::S(s.to_owned()), Default::default()) } /// Constructs an atomic s-expression from an int. pub fn atom_i(i: i64) -> Sexp { - Sexp::Atom(Atom::I(i), None) + Sexp::Atom(Atom::I(i), Default::default()) } /// Constructs an atomic s-expression from a float. pub fn atom_f(f: f64) -> Sexp { - Sexp::Atom(Atom::F(f), None) + Sexp::Atom(Atom::F(f), Default::default()) } /// Constructs a list s-expression given a slice of s-expressions. pub fn list(xs: &[Sexp]) -> Sexp { - Sexp::List(xs.to_owned(), None) + Sexp::List(xs.to_owned(), Default::default()) } /// Reads an s-expression out of a `&str`. From 33ec1461e4b6eb2443330d5a74474b23e4fff338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 23:12:11 +0200 Subject: [PATCH 10/12] remove unnecessary cloning of SourcePosition, reduce SourcePosition size. clean, format --- crsn/src/asm/data/rd.rs | 2 +- crsn/src/asm/data/reg.rs | 13 ++++---- crsn/src/asm/data/wr.rs | 2 +- crsn/src/asm/error.rs | 6 ++-- crsn/src/asm/instr/cond.rs | 7 +++-- crsn/src/asm/instr/flatten.rs | 11 ++++--- crsn/src/asm/instr/mod.rs | 2 +- crsn/src/asm/instr/op.rs | 5 +-- crsn/src/asm/mod.rs | 7 +++-- crsn/src/asm/parse/arg_parser.rs | 4 +-- crsn/src/asm/parse/mod.rs | 4 +-- crsn/src/asm/parse/parse_cond.rs | 4 +-- crsn/src/asm/parse/parse_data.rs | 31 +++++++++---------- crsn/src/asm/parse/parse_instr.rs | 14 ++++----- crsn/src/asm/parse/parse_op.rs | 17 +++++----- crsn/src/asm/parse/parse_routine.rs | 14 ++++----- crsn/src/asm/patches/mod.rs | 20 ++++++------ crsn/src/builtin/defs.rs | 3 +- crsn/src/builtin/exec.rs | 7 ++--- crsn/src/builtin/mod.rs | 11 ++++--- crsn/src/builtin/parse.rs | 48 +++++++++++++++-------------- crsn/src/module/mod.rs | 9 +++--- crsn/src/runtime/program.rs | 9 +++--- crsn/src/utils/mod.rs | 5 +-- crsn_arith/src/exec.rs | 10 +++--- crsn_arith/src/lib.rs | 2 +- crsn_arith/src/parse.rs | 34 ++++++++++---------- crsn_screen/src/exec.rs | 16 +++------- crsn_screen/src/lib.rs | 2 +- crsn_screen/src/parse.rs | 10 +++--- crsn_stacks/src/exec.rs | 12 ++++---- crsn_stacks/src/lib.rs | 2 +- crsn_stacks/src/parse.rs | 4 +-- launcher/src/main.rs | 2 +- lib/spanned_sexp/.travis.yml | 28 ++++++++--------- lib/spanned_sexp/Cargo.toml | 6 ++-- lib/spanned_sexp/src/error.rs | 14 ++++----- lib/spanned_sexp/src/lib.rs | 2 +- lib/spanned_sexp/src/test.rs | 6 ++-- 39 files changed, 203 insertions(+), 202 deletions(-) diff --git a/crsn/src/asm/data/rd.rs b/crsn/src/asm/data/rd.rs index 435c267..589bb62 100644 --- a/crsn/src/asm/data/rd.rs +++ b/crsn/src/asm/data/rd.rs @@ -1,4 +1,4 @@ -use std::fmt::{Debug, Formatter, Display}; +use std::fmt::{Debug, Display, Formatter}; use std::fmt; use crate::asm::data::{DataDisp, Mask, RdData, Register}; diff --git a/crsn/src/asm/data/reg.rs b/crsn/src/asm/data/reg.rs index b14c593..a43acba 100644 --- a/crsn/src/asm/data/reg.rs +++ b/crsn/src/asm/data/reg.rs @@ -1,7 +1,8 @@ use std::fmt::{self, Display, Formatter}; -use crate::asm::error::CrsnError; use sexp::SourcePosition; + +use crate::asm::error::CrsnError; use crate::asm::patches::ErrWithPos; /// Register name @@ -25,27 +26,27 @@ impl Display for Register { } } -pub fn parse_reg(name: &str, at: SourcePosition) -> Result { +pub fn parse_reg(name: &str, at: &SourcePosition) -> Result { // TODO deduplicate code if let Some(rn) = name.strip_prefix("arg") { if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() { - return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at))?; + return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at.clone()))?; } let val: u8 = rn.parse().err_pos(at)?; Ok(Register::Arg(val)) } else if let Some(rn) = name.strip_prefix("res") { if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() { - return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at))?; + return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at.clone()))?; } let val: u8 = rn.parse().err_pos(at)?; Ok(Register::Res(val)) } else if let Some(rn) = name.strip_prefix("r") { if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() { - return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at))?; + return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at.clone()))?; } let val: u8 = rn.parse().err_pos(at)?; Ok(Register::Gen(val)) } else { - Err(CrsnError::Parse(format!("Bad reg name: {}", name).into(), at))? + Err(CrsnError::Parse(format!("Bad reg name: {}", name).into(), at.clone()))? } } diff --git a/crsn/src/asm/data/wr.rs b/crsn/src/asm/data/wr.rs index 75509d6..8a7d8e8 100644 --- a/crsn/src/asm/data/wr.rs +++ b/crsn/src/asm/data/wr.rs @@ -1,4 +1,4 @@ -use std::fmt::{Debug, Formatter, Display}; +use std::fmt::{Debug, Display, Formatter}; use std::fmt; use crate::asm::data::{DataDisp, Mask, Rd, WrData}; diff --git a/crsn/src/asm/error.rs b/crsn/src/asm/error.rs index 12744a5..0f8bc49 100644 --- a/crsn/src/asm/error.rs +++ b/crsn/src/asm/error.rs @@ -1,13 +1,13 @@ use std::borrow::Cow; -use std::num::ParseIntError; +use std::error::Error; use thiserror::Error; +use sexp::SourcePosition; + use crate::asm::data::{Mask, Register}; use crate::asm::data::literal::Label; use crate::asm::instr::Cond; -use sexp::SourcePosition; -use std::error::Error; /// csn_asm unified error type #[derive(Error, Debug)] diff --git a/crsn/src/asm/instr/cond.rs b/crsn/src/asm/instr/cond.rs index e4f7774..4aa68ab 100644 --- a/crsn/src/asm/instr/cond.rs +++ b/crsn/src/asm/instr/cond.rs @@ -1,9 +1,10 @@ use std::fmt::{self, Display, Formatter}; use std::ops::Not; -use crate::asm::error::CrsnError; use sexp::SourcePosition; +use crate::asm::error::CrsnError; + /// Condition flag #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum Cond { @@ -46,7 +47,7 @@ pub enum Cond { NotCarry, } -pub fn parse_cond(text: &str, pos : SourcePosition) -> Result { +pub fn parse_cond(text: &str, pos: &SourcePosition) -> Result { Ok(match text.trim_end_matches('?') { "eq" | "=" | "==" => Cond::Equal, "ne" | "<>" | "!=" | "≠" => Cond::NotEqual, @@ -67,7 +68,7 @@ pub fn parse_cond(text: &str, pos : SourcePosition) -> Result { "ov" | "^" => Cond::Overflow, "nov" | "!ov" | "!^" => Cond::NotOverflow, _ => { - return Err(CrsnError::Parse(format!("Unknown cond: {}", text).into(), pos)); + return Err(CrsnError::Parse(format!("Unknown cond: {}", text).into(), pos.clone())); } }) } diff --git a/crsn/src/asm/instr/flatten.rs b/crsn/src/asm/instr/flatten.rs index 95f1a27..07d03b7 100644 --- a/crsn/src/asm/instr/flatten.rs +++ b/crsn/src/asm/instr/flatten.rs @@ -1,5 +1,8 @@ use std::collections::HashMap; -use std::sync::atomic::{AtomicU32}; +use std::fmt::Debug; +use std::sync::atomic::AtomicU32; + +use sexp::SourcePosition; use crate::asm::data::{Rd, RdData}; use crate::asm::data::literal::{Label, Value}; @@ -8,11 +11,9 @@ use crate::asm::instr::{Cond, InstrWithBranches, Op, Routine}; use crate::asm::instr::op::OpKind; use crate::builtin::defs::Barrier; use crate::builtin::defs::BuiltinOp; -use std::fmt::Debug; -use sexp::SourcePosition; /// A trait for something that can turn into multiple instructions -pub trait Flatten : Debug { +pub trait Flatten: Debug { fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError>; fn pos(&self) -> SourcePosition; @@ -154,7 +155,7 @@ pub fn labels_to_skips(ops: Vec) -> Result, CrsnError> { let skip = *dest as isize - n as isize + skipped; cleaned.push(Op { cond: op.cond, - pos : op.pos.clone(), + pos: op.pos.clone(), kind: OpKind::BuiltIn(BuiltinOp::Skip(Rd::new(RdData::Immediate(skip as Value)))), }); } else { diff --git a/crsn/src/asm/instr/mod.rs b/crsn/src/asm/instr/mod.rs index 28190e9..f71e181 100644 --- a/crsn/src/asm/instr/mod.rs +++ b/crsn/src/asm/instr/mod.rs @@ -1,9 +1,9 @@ pub use cond::Cond; pub use flatten::Flatten; pub use op::Op; +use sexp::SourcePosition; use crate::asm::data::literal::RoutineName; -use sexp::SourcePosition; pub mod op; pub mod cond; diff --git a/crsn/src/asm/instr/op.rs b/crsn/src/asm/instr/op.rs index e432da4..9a96bc2 100644 --- a/crsn/src/asm/instr/op.rs +++ b/crsn/src/asm/instr/op.rs @@ -1,11 +1,12 @@ use std::fmt::Debug; +use sexp::{Atom, Sexp, SourcePosition}; + use crate::asm::instr::Cond; use crate::builtin::defs::BuiltinOp; use crate::module::{EvalRes, OpTrait}; use crate::runtime::fault::Fault; use crate::runtime::run_thread::{info::ThreadInfo, state::RunState}; -use sexp::{Sexp, Atom, SourcePosition}; /// A higher level simple opration #[derive(Debug)] @@ -18,7 +19,7 @@ pub enum OpKind { #[derive(Debug)] pub struct Op { pub cond: Option, - pub pos : SourcePosition, + pub pos: SourcePosition, pub kind: OpKind, } diff --git a/crsn/src/asm/mod.rs b/crsn/src/asm/mod.rs index 7217054..292713e 100644 --- a/crsn/src/asm/mod.rs +++ b/crsn/src/asm/mod.rs @@ -1,11 +1,12 @@ use std::cell::RefCell; use std::sync::Arc; +use sexp::SourcePosition; + +use crate::asm::instr::flatten::labels_to_skips; use crate::asm::parse::{ParserContext, ParserState}; use crate::module::CrsnExtension; use crate::runtime::program::Program; -use crate::asm::instr::flatten::labels_to_skips; -use sexp::SourcePosition; pub mod data; pub mod error; @@ -24,7 +25,7 @@ pub fn assemble(source: &str, parsers: Arc>>) -> Resu }), }; - let ops = parse::parse(source, SourcePosition::default(), &pcx)?; + let ops = parse::parse(source, &SourcePosition::default(), &pcx)?; let ops = labels_to_skips(ops)?; Ok(Program::new(ops, parsers)?) diff --git a/crsn/src/asm/parse/arg_parser.rs b/crsn/src/asm/parse/arg_parser.rs index 1bbfb7e..a5d911c 100644 --- a/crsn/src/asm/parse/arg_parser.rs +++ b/crsn/src/asm/parse/arg_parser.rs @@ -11,7 +11,7 @@ use crate::asm::patches::NextOrErr; pub struct TokenParser<'a> { orig_len: usize, args: Vec, - start_pos: SourcePosition, + start_pos: &'a SourcePosition, pub pcx: &'a ParserContext<'a>, } @@ -29,7 +29,7 @@ impl<'a> IntoIterator for TokenParser<'a> { impl<'a> TokenParser<'a> { /// Create a new argument parser - pub fn new(mut args: Vec, start_pos: SourcePosition, pcx: &'a ParserContext) -> Self { + pub fn new(mut args: Vec, start_pos: &'a SourcePosition, pcx: &'a ParserContext) -> Self { args.reverse(); Self { orig_len: args.len(), diff --git a/crsn/src/asm/parse/mod.rs b/crsn/src/asm/parse/mod.rs index cf87193..bcfbbd0 100644 --- a/crsn/src/asm/parse/mod.rs +++ b/crsn/src/asm/parse/mod.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use std::sync::atomic::AtomicU32; pub use parse_instr::parse_instructions; +use sexp::SourcePosition; use crate::asm::data::literal::{ConstantName, RegisterAlias, Value}; use crate::asm::data::Register; @@ -10,7 +11,6 @@ use crate::asm::error::CrsnError; use crate::asm::instr::Op; use crate::asm::parse::sexp_expect::expect_list; use crate::module::CrsnExtension; -use sexp::SourcePosition; pub mod parse_cond; pub mod parse_instr; @@ -39,7 +39,7 @@ pub struct ParserState { pub constants: HashMap, } -pub fn parse(source: &str, pos: SourcePosition, parsers: &ParserContext) -> Result, CrsnError> { +pub fn parse(source: &str, pos: &SourcePosition, parsers: &ParserContext) -> Result, CrsnError> { let (items, _pos) = expect_list(sexp::parse(source)?, true)?; /* numbered labels start with a weird high number diff --git a/crsn/src/asm/parse/parse_cond.rs b/crsn/src/asm/parse/parse_cond.rs index bbbeb9e..0205b17 100644 --- a/crsn/src/asm/parse/parse_cond.rs +++ b/crsn/src/asm/parse/parse_cond.rs @@ -9,12 +9,12 @@ use crate::asm::patches::TryRemove; pub fn parse_cond_branch(tok: Sexp, parsers: &ParserContext) -> Result<(Cond, Box), CrsnError> { let (mut list, pos) = expect_list(tok, false)?; - let (kw, kw_pos) = expect_string_atom(list.remove_or_err(0, pos.clone(), "Missing \"cond?\" keyword in conditional branch")?)?; + let (kw, kw_pos) = expect_string_atom(list.remove_or_err(0, &pos, "Missing \"cond?\" keyword in conditional branch")?)?; if !kw.ends_with('?') { return Err(CrsnError::Parse(format!("Condition must end with '?': {}", kw).into(), kw_pos)); } - Ok((cond::parse_cond(&kw, kw_pos)?, parse_instructions(list.into_iter(), pos, parsers)?)) + Ok((cond::parse_cond(&kw, &kw_pos)?, parse_instructions(list.into_iter(), &pos, parsers)?)) } diff --git a/crsn/src/asm/parse/parse_data.rs b/crsn/src/asm/parse/parse_data.rs index 436fc31..bc0e6d4 100644 --- a/crsn/src/asm/parse/parse_data.rs +++ b/crsn/src/asm/parse/parse_data.rs @@ -8,7 +8,6 @@ use crate::asm::data::literal::{ConstantName, Label, RegisterAlias, Value}; use crate::asm::error::CrsnError; use crate::asm::parse::ParserContext; use crate::asm::parse::sexp_expect::expect_string_atom; -use std::num::TryFromIntError; use crate::asm::patches::ErrWithPos; fn is_valid_identifier(name: &str) -> bool { @@ -46,14 +45,14 @@ pub fn parse_constant_name(name: Sexp) -> Result<(ConstantName, SourcePosition), pub fn parse_label(name: Sexp) -> Result { // trace!("parse label: {:?}", name); let (name, namepos) = expect_string_atom(name)?; - Ok(parse_label_str(&name, namepos)?) + Ok(parse_label_str(&name, &namepos)?) } -pub fn parse_label_str(name: &str, pos: SourcePosition) -> Result { +pub fn parse_label_str(name: &str, pos: &SourcePosition) -> Result { let label = name.trim_start_matches(':'); Ok(if label.starts_with('#') { - let val = parse_u64(&label[1..], pos.clone())?; + let val = parse_u64(&label[1..], pos)?; Label::Numbered(u32::try_from(val).err_pos(pos)?) } else { Label::Named(label.to_string()) @@ -67,7 +66,7 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result { + Sexp::Atom(Atom::I(val), _pos) => { Ok(DataDisp::Immediate(unsafe { std::mem::transmute(val) })) } Sexp::Atom(Atom::S(s), pos) => { @@ -92,7 +91,7 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result Result Result Result { match tok { - Sexp::Atom(Atom::I(val), pos) => { + Sexp::Atom(Atom::I(val), _pos) => { Ok(unsafe { std::mem::transmute(val) }) } Sexp::Atom(Atom::S(s), pos) => { @@ -131,7 +130,7 @@ pub fn parse_value(tok: Sexp, pcx: &ParserContext) -> Result { return Ok(*val); } - Ok(unsafe { std::mem::transmute(parse_i64(&s, pos)?) }) + Ok(unsafe { std::mem::transmute(parse_i64(&s, &pos)?) }) } other => { Err(CrsnError::Parse(format!("bad value format: {:?}", other).into(), other.pos().clone())) @@ -140,7 +139,7 @@ pub fn parse_value(tok: Sexp, pcx: &ParserContext) -> Result { } -pub fn parse_u64(literal: &str, pos: SourcePosition) -> Result { +pub fn parse_u64(literal: &str, pos: &SourcePosition) -> Result { // trace!("parse u64 from {}", literal); let mut without_underscores = Cow::Borrowed(literal); if without_underscores.contains('_') { @@ -156,21 +155,21 @@ pub fn parse_u64(literal: &str, pos: SourcePosition) -> Result { } } -pub fn parse_i64(literal: &str, pos: SourcePosition) -> Result { +pub fn parse_i64(literal: &str, pos: &SourcePosition) -> Result { // trace!("parse i64 from {}", literal); if let Some(_value) = literal.strip_prefix("-") { - Ok(-1 * i64::try_from(parse_u64(literal, pos.clone())?).err_pos(pos)?) + Ok(-1 * i64::try_from(parse_u64(literal, pos)?).err_pos(pos)?) } else { - Ok(i64::try_from(parse_u64(literal, pos.clone())?).err_pos(pos)?) + Ok(i64::try_from(parse_u64(literal, pos)?).err_pos(pos)?) } } pub fn parse_rd(tok: Sexp, pcx: &ParserContext) -> Result { let pos = tok.pos().clone(); - Ok(Rd::new(RdData::try_from(parse_data_disp(tok, pcx)?).err_pos(pos)?)) + Ok(Rd::new(RdData::try_from(parse_data_disp(tok, pcx)?).err_pos(&pos)?)) } pub fn parse_wr(tok: Sexp, pcx: &ParserContext) -> Result { let pos = tok.pos().clone(); - Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?).err_pos(pos)?)) + Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?).err_pos(&pos)?)) } diff --git a/crsn/src/asm/parse/parse_instr.rs b/crsn/src/asm/parse/parse_instr.rs index 7b78183..8f24234 100644 --- a/crsn/src/asm/parse/parse_instr.rs +++ b/crsn/src/asm/parse/parse_instr.rs @@ -7,12 +7,12 @@ use crate::asm::parse::parse_cond::parse_cond_branch; use crate::asm::parse::parse_routine::parse_routine; use crate::asm::parse::ParserContext; use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom}; +use crate::asm::patches::NextOrErr; use crate::module::ParseRes; use super::parse_op::parse_op; -use crate::asm::patches::NextOrErr; -pub fn parse_instructions(items: impl Iterator, pos: SourcePosition, pcx: &ParserContext) -> Result, CrsnError> { +pub fn parse_instructions(items: impl Iterator, pos: &SourcePosition, pcx: &ParserContext) -> Result, CrsnError> { let mut parsed = vec![]; for expr in items { let (tokens, listpos) = expect_list(expr, false)?; @@ -21,13 +21,13 @@ pub fn parse_instructions(items: impl Iterator, pos: SourcePosition, let (name, namepos) = expect_string_atom(toki.next_or_err(listpos.clone(), "Expected instruction name token")?)?; if name == "proc" { - parsed.push(parse_routine(toki, pos.clone(), pcx)?); + parsed.push(parse_routine(toki, pos, pcx)?); continue; } - let mut token_parser = TokenParser::new(toki.collect(), listpos.clone(), pcx); + let mut token_parser = TokenParser::new(toki.collect(), &listpos, pcx); for p in pcx.parsers { - token_parser = match p.parse_syntax(pos.clone(), &name, token_parser) { + token_parser = match p.parse_syntax(pos, &name, token_parser) { Ok(ParseRes::Parsed(op)) => return Ok(op), Ok(ParseRes::ParsedNone) => return Ok(Box::new(())), Ok(ParseRes::Unknown(to_reuse)) => { @@ -45,7 +45,7 @@ pub fn parse_instructions(items: impl Iterator, pos: SourcePosition, // Get back the original iterator let toki = token_parser.into_iter(); - let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), listpos.clone(), pcx); + let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), &listpos, pcx); let branch_tokens = toki .skip_while(|e| e.is_atom()) .take_while(|e| e.is_list()); @@ -62,7 +62,7 @@ pub fn parse_instructions(items: impl Iterator, pos: SourcePosition, } }; - if let Some(op) = parse_op(name.as_str(), arg_tokens, namepos.clone())? { + if let Some(op) = parse_op(name.as_str(), arg_tokens, &namepos)? { parsed.push(Box::new(InstrWithBranches { op, pos: namepos, diff --git a/crsn/src/asm/parse/parse_op.rs b/crsn/src/asm/parse/parse_op.rs index 1ceb58c..bf26826 100644 --- a/crsn/src/asm/parse/parse_op.rs +++ b/crsn/src/asm/parse/parse_op.rs @@ -1,32 +1,33 @@ +use sexp::SourcePosition; + use crate::asm::error::CrsnError; use crate::asm::instr::cond::parse_cond; use crate::asm::instr::Op; use crate::asm::parse::arg_parser::TokenParser; -use crate::module::ParseRes; use crate::builtin::BuiltinOps; -use sexp::SourcePosition; +use crate::module::ParseRes; -pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>, spos: SourcePosition) -> Result, CrsnError> { +pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>, spos: &SourcePosition) -> Result, CrsnError> { // Include built-in instructions let builtins = [BuiltinOps::new()]; let mut cond = None; if let Some(pos) = keyword.find('.') { - cond = Some(parse_cond(&keyword[(pos + 1)..], spos.clone())?); + cond = Some(parse_cond(&keyword[(pos + 1)..], spos)?); keyword = &keyword[..pos]; } for p in builtins.iter().chain(arg_tokens.pcx.parsers) { - arg_tokens = match p.parse_op(spos.clone(), keyword, arg_tokens) { + arg_tokens = match p.parse_op(spos, keyword, arg_tokens) { Ok(ParseRes::Parsed(kind)) => return Ok(Some(Op { cond, - pos: spos, + pos: spos.clone(), kind, })), Ok(ParseRes::ParsedNone) => return Ok(None), Ok(ParseRes::Unknown(to_reuse)) => { if to_reuse.parsing_started() { - return Err(CrsnError::Parse(format!("Module \"{}\" started parsing {}, but returned Unknown!", p.name(), keyword).into(), spos)); + return Err(CrsnError::Parse(format!("Module \"{}\" started parsing {}, but returned Unknown!", p.name(), keyword).into(), spos.clone())); } to_reuse } @@ -36,5 +37,5 @@ pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>, spos: So } } - return Err(CrsnError::Parse(format!("Unknown instruction: {}", keyword).into(), spos)); + return Err(CrsnError::Parse(format!("Unknown instruction: {}", keyword).into(), spos.clone())); } diff --git a/crsn/src/asm/parse/parse_routine.rs b/crsn/src/asm/parse/parse_routine.rs index 34819aa..e849cff 100644 --- a/crsn/src/asm/parse/parse_routine.rs +++ b/crsn/src/asm/parse/parse_routine.rs @@ -7,14 +7,14 @@ use crate::asm::parse::{parse_instructions, ParserContext}; use crate::asm::parse::arg_parser::TokenParser; use crate::asm::parse::parse_data::parse_reg_alias; use crate::asm::parse::sexp_expect::expect_string_atom; -use crate::builtin::parse::parse_routine_name; use crate::asm::patches::NextOrErr; +use crate::builtin::parse::parse_routine_name; -pub fn parse_routine(mut toki: impl Iterator + Clone, rt_pos: SourcePosition, pcx: &ParserContext) -> Result, CrsnError> { +pub fn parse_routine(mut toki: impl Iterator + Clone, rt_pos: &SourcePosition, pcx: &ParserContext) -> Result, CrsnError> { let (name, namepos) = expect_string_atom(toki.next_or_err(rt_pos.clone(), "Expected routine name")?)?; - let mut name = parse_routine_name(name, namepos)?; + let mut name = parse_routine_name(name, &namepos)?; - let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), rt_pos.clone(), pcx); + let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), rt_pos, pcx); // If arity is explicitly given, then either no named argument must be provided, // or their count must match the arity. If no arity is given, then arity is determined @@ -22,7 +22,7 @@ pub fn parse_routine(mut toki: impl Iterator + Clone, rt_pos: SourceP if name.arity == 0 && arg_tokens.len() != 0 { name.arity = arg_tokens.len() as u8; } else if arg_tokens.len() != 0 && name.arity as usize != arg_tokens.len() { - return Err(CrsnError::Parse(format!("arity mismatch in routine {}", name.name).into(), rt_pos)); + return Err(CrsnError::Parse(format!("arity mismatch in routine {}", name.name).into(), rt_pos.clone())); } let toki = toki.skip_while(|e| e.is_atom()); @@ -44,7 +44,7 @@ pub fn parse_routine(mut toki: impl Iterator + Clone, rt_pos: SourceP } } - let body = parse_instructions(toki, rt_pos.clone(), pcx)?; + let body = parse_instructions(toki, rt_pos, pcx)?; { let mut pstate = pcx.state.borrow_mut(); @@ -54,7 +54,7 @@ pub fn parse_routine(mut toki: impl Iterator + Clone, rt_pos: SourceP return Ok(Box::new(Routine { name, - pos: rt_pos, + pos: rt_pos.clone(), body, })); } diff --git a/crsn/src/asm/patches/mod.rs b/crsn/src/asm/patches/mod.rs index aa299b5..b36202c 100644 --- a/crsn/src/asm/patches/mod.rs +++ b/crsn/src/asm/patches/mod.rs @@ -1,12 +1,12 @@ use sexp::SourcePosition; + use crate::asm::error::CrsnError; -use std::borrow::Cow; pub trait TryRemove { type Item; fn try_remove(&mut self, index: usize) -> Option; - fn remove_or_err(&mut self, index: usize, pos: SourcePosition, err : &'static str) -> Result; + fn remove_or_err(&mut self, index: usize, pos: &SourcePosition, err: &'static str) -> Result; } impl TryRemove for Vec { @@ -20,10 +20,10 @@ impl TryRemove for Vec { } } - fn remove_or_err(&mut self, index: usize, pos: SourcePosition, err : &'static str) -> Result { + fn remove_or_err(&mut self, index: usize, pos: &SourcePosition, err: &'static str) -> Result { match self.try_remove(index) { None => { - Err(CrsnError::Parse(err.into(), pos)) + Err(CrsnError::Parse(err.into(), pos.clone())) } Some(removed) => Ok(removed) } @@ -31,10 +31,10 @@ impl TryRemove for Vec { } pub trait NextOrErr { - fn next_or_err(&mut self, pos: SourcePosition, err : &'static str) -> Result; + fn next_or_err(&mut self, pos: SourcePosition, err: &'static str) -> Result; } -impl> NextOrErr for K { +impl> NextOrErr for K { fn next_or_err(&mut self, pos: SourcePosition, err: &'static str) -> Result { match self.next() { None => { @@ -46,14 +46,14 @@ impl> NextOrErr for K { } pub trait ErrWithPos { - fn err_pos(self, pos : SourcePosition) -> Result; + fn err_pos(self, pos: &SourcePosition) -> Result; } -impl ErrWithPos for Result { - fn err_pos(self, pos : SourcePosition) -> Result { +impl ErrWithPos for Result { + fn err_pos(self, pos: &SourcePosition) -> Result { match self { Ok(v) => Ok(v), - Err(e) => Err(CrsnError::ParseOther(Box::new(e), pos)) + Err(e) => Err(CrsnError::ParseOther(Box::new(e), pos.clone())) } } } diff --git a/crsn/src/builtin/defs.rs b/crsn/src/builtin/defs.rs index 686f432..db3de9d 100644 --- a/crsn/src/builtin/defs.rs +++ b/crsn/src/builtin/defs.rs @@ -1,8 +1,9 @@ +use sexp::SourcePosition; + use crate::asm::data::{Rd, RdObj, Wr}; use crate::asm::data::literal::{DebugMsg, Label, RoutineName}; use crate::asm::instr::Op; use crate::asm::instr::op::OpKind; -use sexp::SourcePosition; #[derive(Debug)] pub enum Barrier { diff --git a/crsn/src/builtin/exec.rs b/crsn/src/builtin/exec.rs index 7574c6a..45a0db5 100644 --- a/crsn/src/builtin/exec.rs +++ b/crsn/src/builtin/exec.rs @@ -1,5 +1,7 @@ use std::time::Duration; +use sexp::Sexp; + use crate::asm::data::{Rd, RdData}; use crate::asm::data::literal::Addr; use crate::asm::instr::Cond; @@ -8,11 +10,6 @@ use crate::module::{EvalRes, OpTrait}; use crate::runtime::fault::Fault; use crate::runtime::frame::StackFrame; use crate::runtime::run_thread::{state::RunState, ThreadInfo}; -use sexp::Sexp; - - - - impl OpTrait for BuiltinOp { fn execute(&self, info: &ThreadInfo, state: &mut RunState) -> Result { diff --git a/crsn/src/builtin/mod.rs b/crsn/src/builtin/mod.rs index 0fa9a9f..fdf1ea9 100644 --- a/crsn/src/builtin/mod.rs +++ b/crsn/src/builtin/mod.rs @@ -1,9 +1,10 @@ -use crate::module::{CrsnExtension, ParseRes}; -use crate::asm::parse::arg_parser::TokenParser; -use crate::asm::instr::op::OpKind; -use crate::asm::error::CrsnError; use sexp::SourcePosition; +use crate::asm::error::CrsnError; +use crate::asm::instr::op::OpKind; +use crate::asm::parse::arg_parser::TokenParser; +use crate::module::{CrsnExtension, ParseRes}; + pub mod defs; pub mod exec; pub mod parse; @@ -22,7 +23,7 @@ impl CrsnExtension for BuiltinOps { "builtin" } - fn parse_op<'a>(&self, pos: SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { + fn parse_op<'a>(&self, pos: &SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { parse::parse_op(pos, keyword, args) } } diff --git a/crsn/src/builtin/parse.rs b/crsn/src/builtin/parse.rs index 873fd90..ec5a57f 100644 --- a/crsn/src/builtin/parse.rs +++ b/crsn/src/builtin/parse.rs @@ -5,15 +5,14 @@ use crate::asm::data::reg::parse_reg; use crate::asm::error::CrsnError; use crate::asm::instr::op::OpKind; use crate::asm::parse::arg_parser::TokenParser; -use crate::asm::parse::parse_data::{parse_constant_name, parse_label, parse_rd, parse_reg_alias, parse_value, parse_label_str}; +use crate::asm::parse::parse_data::{parse_constant_name, parse_label, parse_label_str, parse_rd, parse_reg_alias, parse_value}; use crate::asm::parse::sexp_expect::expect_string_atom; +use crate::asm::patches::ErrWithPos; use crate::builtin::defs::{Barrier, BuiltinOp}; -use crate::module::{ParseRes}; +use crate::module::ParseRes; use crate::utils::A; -use crate::asm::patches::ErrWithPos; - -pub(crate) fn parse_op<'a>(op_pos: SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { +pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { let pcx = args.pcx; Ok(ParseRes::Parsed(OpKind::BuiltIn(match keyword { @@ -35,7 +34,7 @@ pub(crate) fn parse_op<'a>(op_pos: SourcePosition, keyword: &str, mut args: Toke let (alias, aliaspos) = parse_reg_alias(args.next_or_err()?)?; trace!("alias={:?}", alias); let (rn, rpos) = args.next_string()?; - let register = parse_reg(&rn, rpos.clone())?; + let register = parse_reg(&rn, &rpos)?; trace!("register={:?}", alias); let mut pstate = pcx.state.borrow_mut(); @@ -124,7 +123,7 @@ pub(crate) fn parse_op<'a>(op_pos: SourcePosition, keyword: &str, mut args: Toke "routine" => { let name = args.next_string()?; - BuiltinOp::Routine(parse_routine_name(name.0, name.1)?) + BuiltinOp::Routine(parse_routine_name(name.0, &name.1)?) } "skip" => { @@ -200,7 +199,7 @@ pub(crate) fn parse_op<'a>(op_pos: SourcePosition, keyword: &str, mut args: Toke other => { if let Some(label) = other.strip_prefix(':') { - BuiltinOp::Label(parse_label_str(label, op_pos)?) + BuiltinOp::Label(parse_label_str(label, &op_pos)?) } else { return Ok(ParseRes::Unknown(args)); } @@ -208,7 +207,7 @@ pub(crate) fn parse_op<'a>(op_pos: SourcePosition, keyword: &str, mut args: Toke }))) } -pub(crate) fn parse_routine_name(name: String, pos: SourcePosition) -> Result { +pub(crate) fn parse_routine_name(name: String, pos: &SourcePosition) -> Result { let (name, arity) = if let Some(n) = name.find('/') { ( (&name[0..n]).to_string(), @@ -238,7 +237,7 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp { inner.extend(args.iter().map(A)); sexp::list(&inner) } - }, + } BuiltinOp::Ret(values) => { if values.is_empty() { sexp::list(&[A("ret")]) @@ -287,16 +286,15 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp { #[cfg(test)] mod test { - - use std::sync::atomic::AtomicU32; - use crate::asm::instr::{Flatten}; + use sexp::SourcePosition; + + use crate::asm::instr::Flatten; use crate::asm::parse::{parse_instructions, ParserContext}; use crate::asm::parse::sexp_expect::expect_list; - - use crate::module::OpTrait; use crate::builtin::BuiltinOps; + use crate::module::OpTrait; #[test] fn roundtrip() { @@ -306,10 +304,10 @@ mod test { ("(nop)", "(nop)"), ("(halt)", "(halt)"), ("(sleep 1000)", "(sleep 1000)"), - ("(:x)\ - (j :x)", "(skip 0)"), - ("(:#7)\ - (j :#7)", "(skip 0)"), + ("(:x)", "(:x)"), + ("(j :x)", "(j :x)"), + ("(:#7)", "(:#7)"), + ("(j :#7)", "(j :#7)"), ("(fj :x)", "(fj :x)"), ("(skip 0)", "(skip 0)"), ("(skip r0)", "(skip r0)"), @@ -367,9 +365,9 @@ mod test { /* first cycle */ let s = sexp::parse(&format!("({})", sample)) .expect("parse sexp"); - let list = expect_list(Some(s), false).unwrap(); + let list = expect_list(s, false).unwrap(); let num = AtomicU32::new(0); - let parsed = parse_instructions(list.into_iter(), &pcx) + let parsed = parse_instructions(list.0.into_iter(), &SourcePosition::default(), &pcx) .expect("parse instr").flatten(&num) .expect("flatten").remove(0); @@ -379,13 +377,17 @@ mod test { assert_eq!(expected, exported); println!(" - 2nd cycle"); + let pcx = ParserContext { + parsers, + state: Default::default(), + }; /* second cycle, nothing should change */ let s = sexp::parse(&format!("({})", exported)) .expect("parse sexp (2c)"); - let list = expect_list(Some(s), false).unwrap(); + let list = expect_list(s, false).unwrap(); let num = AtomicU32::new(0); - let parsed = parse_instructions(list.into_iter(), &pcx) + let parsed = parse_instructions(list.0.into_iter(), &SourcePosition::default(), &pcx) .expect("parse instr (2c)").flatten(&num) .expect("flatten (2c)").remove(0); diff --git a/crsn/src/module/mod.rs b/crsn/src/module/mod.rs index 77ff1ac..2861ace 100644 --- a/crsn/src/module/mod.rs +++ b/crsn/src/module/mod.rs @@ -1,8 +1,9 @@ #![allow(unused_variables)] -use std::fmt::{Debug}; +use std::fmt::Debug; pub use eval_res::EvalRes; +use sexp::{Sexp, SourcePosition}; use crate::asm::data::literal::Value; use crate::asm::data::Mask; @@ -13,8 +14,6 @@ use crate::asm::parse::arg_parser::TokenParser; use crate::runtime::fault::Fault; use crate::runtime::run_thread::state::RunState; use crate::runtime::run_thread::ThreadInfo; -use sexp::{Sexp, SourcePosition}; - mod eval_res; @@ -53,12 +52,12 @@ pub trait CrsnExtension: Debug + Send + Sync + 'static { /// If the instruction keyword is not recognized, return Unknown with the unchanged argument list. /// /// pcx is available on the arg_tokens parser - fn parse_op<'a>(&self, pos: SourcePosition, keyword: &str, arg_tokens: TokenParser<'a>) -> Result, CrsnError>; + fn parse_op<'a>(&self, pos: &SourcePosition, keyword: &str, arg_tokens: TokenParser<'a>) -> Result, CrsnError>; /// Parse a generic S-expression (non-op) /// /// pcx is available on the arg_tokens parser - fn parse_syntax<'a>(&self, pos: SourcePosition, keyword: &str, tokens: TokenParser<'a>) + fn parse_syntax<'a>(&self, pos: &SourcePosition, keyword: &str, tokens: TokenParser<'a>) -> Result>, CrsnError> { Ok(ParseRes::Unknown(tokens)) diff --git a/crsn/src/runtime/program.rs b/crsn/src/runtime/program.rs index 279e43e..1ddebdc 100644 --- a/crsn/src/runtime/program.rs +++ b/crsn/src/runtime/program.rs @@ -1,14 +1,15 @@ use std::collections::HashMap; use std::sync::Arc; +use sexp::SourcePosition; + use crate::asm::data::literal::{Addr, Label, RoutineName}; +use crate::asm::error::CrsnError; use crate::asm::instr::Op; use crate::asm::instr::op::OpKind; use crate::builtin::defs::{Barrier, BuiltinOp}; use crate::module::CrsnExtension; use crate::runtime::fault::Fault; -use sexp::SourcePosition; -use crate::asm::error::CrsnError; #[derive(Debug)] pub struct Program { @@ -96,9 +97,9 @@ impl Program { pos: SourcePosition { line: 0, column: 0, - index: 0 + index: 0, }, - cond: None + cond: None, } } else { &self.ops[addr.0 as usize] diff --git a/crsn/src/utils/mod.rs b/crsn/src/utils/mod.rs index 7a365ce..052b9a0 100644 --- a/crsn/src/utils/mod.rs +++ b/crsn/src/utils/mod.rs @@ -1,8 +1,9 @@ -pub use option_ext::UncheckedOptionExt; use std::fmt::Display; -use sexp::{Sexp, Atom}; use std::str::FromStr; +pub use option_ext::UncheckedOptionExt; +use sexp::{Atom, Sexp}; + mod option_ext; /// Convert a string token to Sexp diff --git a/crsn_arith/src/exec.rs b/crsn_arith/src/exec.rs index 7118d91..412473a 100644 --- a/crsn_arith/src/exec.rs +++ b/crsn_arith/src/exec.rs @@ -2,16 +2,16 @@ use std::ops::Rem; use num_traits::PrimInt; +use crsn::asm::data::{Rd, Wr}; use crsn::asm::instr::Cond; use crsn::module::{EvalRes, OpTrait}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; - -use crate::defs::ArithOp; -use crsn::sexp::Sexp; use crsn::sexp; +use crsn::sexp::Sexp; use crsn::utils::A; -use crsn::asm::data::{Rd, Wr}; + +use crate::defs::ArithOp; impl OpTrait for ArithOp { fn execute(&self, _ti: &ThreadInfo, state: &mut RunState) -> Result { @@ -238,7 +238,7 @@ impl OpTrait for ArithOp { } } -fn to_sexp_2_or_3(name: &str, dst : &Wr, a: &Rd, b: &Rd) -> Sexp { +fn to_sexp_2_or_3(name: &str, dst: &Wr, a: &Rd, b: &Rd) -> Sexp { if &dst.as_rd() == a { sexp::list(&[A(name), A(dst), A(b)]) } else { diff --git a/crsn_arith/src/lib.rs b/crsn_arith/src/lib.rs index ba06694..146fb98 100644 --- a/crsn_arith/src/lib.rs +++ b/crsn_arith/src/lib.rs @@ -22,7 +22,7 @@ impl CrsnExtension for ArithOps { "arith" } - fn parse_op<'a>(&self, pos: SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { + fn parse_op<'a>(&self, pos: &SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { parse::parse(pos, keyword, args) } } diff --git a/crsn_arith/src/parse.rs b/crsn_arith/src/parse.rs index 4248a11..e4a7fb0 100644 --- a/crsn_arith/src/parse.rs +++ b/crsn_arith/src/parse.rs @@ -3,11 +3,11 @@ use crsn::asm::error::CrsnError; use crsn::asm::instr::op::OpKind; use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::ParseRes; +use crsn::sexp::SourcePosition; use crate::defs::ArithOp; -use crsn::sexp::SourcePosition; -pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { +pub(crate) fn parse<'a>(pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { "cmp" => { ArithOp::Compare { @@ -57,7 +57,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Add requires 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Add requires 2 or 3 arguments".into(), pos.clone())); } } } @@ -80,7 +80,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Sub requires 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Sub requires 2 or 3 arguments".into(), pos.clone())); } } } @@ -103,7 +103,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Mul requires 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Mul requires 2 or 3 arguments".into(), pos.clone())); } } } @@ -130,7 +130,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("DivR requires 3 or 4 arguments".into(), pos)); + return Err(CrsnError::Parse("DivR requires 3 or 4 arguments".into(), pos.clone())); } } } @@ -156,7 +156,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Div requires 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Div requires 2 or 3 arguments".into(), pos.clone())); } } } @@ -180,7 +180,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Mod requires 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Mod requires 2 or 3 arguments".into(), pos.clone())); } } } @@ -203,7 +203,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("And requires 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("And requires 2 or 3 arguments".into(), pos.clone())); } } } @@ -226,7 +226,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Or requires 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Or requires 2 or 3 arguments".into(), pos.clone())); } } } @@ -249,7 +249,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Xor requires 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Xor requires 2 or 3 arguments".into(), pos.clone())); } } } @@ -270,7 +270,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Cpl requires 1 or 2 arguments".into(), pos)); + return Err(CrsnError::Parse("Cpl requires 1 or 2 arguments".into(), pos.clone())); } } } @@ -301,7 +301,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Rol requires 1, 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Rol requires 1, 2 or 3 arguments".into(), pos.clone())); } } } @@ -332,7 +332,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Ror requires 1, 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Ror requires 1, 2 or 3 arguments".into(), pos.clone())); } } } @@ -363,7 +363,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Lsl requires 1, 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Lsl requires 1, 2 or 3 arguments".into(), pos.clone())); } } } @@ -394,7 +394,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Lsr requires 1, 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Lsr requires 1, 2 or 3 arguments".into(), pos.clone())); } } } @@ -425,7 +425,7 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse } } _ => { - return Err(CrsnError::Parse("Asr requires 1, 2 or 3 arguments".into(), pos)); + return Err(CrsnError::Parse("Asr requires 1, 2 or 3 arguments".into(), pos.clone())); } } } diff --git a/crsn_screen/src/exec.rs b/crsn_screen/src/exec.rs index 1afd177..63bac12 100644 --- a/crsn_screen/src/exec.rs +++ b/crsn_screen/src/exec.rs @@ -1,19 +1,19 @@ use std::ops::Sub; use std::time::{Duration, Instant}; -use minifb::{ScaleMode, Window, WindowOptions, MouseMode, Key, MouseButton}; +use minifb::{Key, MouseButton, MouseMode, ScaleMode, Window, WindowOptions}; use crsn::asm::data::literal::Value; use crsn::asm::instr::Cond; use crsn::module::{EvalRes, OpTrait}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; - -use crate::defs::ScreenOp; -use crsn::sexp::Sexp; use crsn::sexp; +use crsn::sexp::Sexp; use crsn::utils::A; +use crate::defs::ScreenOp; + #[derive(Debug)] struct Opts { auto_blit: bool, @@ -299,7 +299,7 @@ fn blit(backend: &mut Backend) { backend.last_render = Instant::now(); } -fn num2key(num : Value) -> Option { +fn num2key(num: Value) -> Option { let remap = [ Key::Key0, Key::Key1, @@ -311,7 +311,6 @@ fn num2key(num : Value) -> Option { Key::Key7, Key::Key8, Key::Key9, - Key::A, // 10 Key::B, Key::C, @@ -361,7 +360,6 @@ fn num2key(num : Value) -> Option { Key::Up, Key::Apostrophe, Key::Backquote, - Key::Backslash, // 57 Key::Comma, Key::Equal, @@ -370,20 +368,17 @@ fn num2key(num : Value) -> Option { Key::Period, Key::RightBracket, Key::Semicolon, - Key::Slash, // 65 Key::Backspace, Key::Delete, Key::End, Key::Enter, - Key::Escape, // 70 Key::Home, Key::Insert, Key::Menu, Key::PageDown, Key::PageUp, - Key::Pause, // 76 Key::Space, Key::Tab, @@ -394,7 +389,6 @@ fn num2key(num : Value) -> Option { Key::RightShift, Key::LeftCtrl, Key::RightCtrl, - Key::NumPad0, // 86 Key::NumPad1, Key::NumPad2, diff --git a/crsn_screen/src/lib.rs b/crsn_screen/src/lib.rs index 9b569f5..f4f8c3c 100644 --- a/crsn_screen/src/lib.rs +++ b/crsn_screen/src/lib.rs @@ -25,7 +25,7 @@ impl CrsnExtension for ScreenOps { "screen" } - fn parse_op<'a>(&self, pos: SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { + fn parse_op<'a>(&self, pos: &SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { parse::parse(pos, keyword, args) } } diff --git a/crsn_screen/src/parse.rs b/crsn_screen/src/parse.rs index f73c20b..8ccbb7e 100644 --- a/crsn_screen/src/parse.rs +++ b/crsn_screen/src/parse.rs @@ -1,13 +1,13 @@ -use crsn::asm::data::{Rd}; +use crsn::asm::data::Rd; use crsn::asm::error::CrsnError; use crsn::asm::instr::op::OpKind; use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::ParseRes; +use crsn::sexp::SourcePosition; use crate::defs::ScreenOp; -use crsn::sexp::SourcePosition; -pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { +pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { "sc-init" => { ScreenOp::ScreenInit { @@ -65,14 +65,14 @@ pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParse "sc-key" => { ScreenOp::TestKey { pressed: args.next_wr()?, - code: args.next_rd()? + code: args.next_rd()?, } } "sc-mbtn" => { ScreenOp::TestMouse { pressed: args.next_wr()?, - button: args.next_rd()? + button: args.next_rd()?, } } diff --git a/crsn_stacks/src/exec.rs b/crsn_stacks/src/exec.rs index 2a6cdc9..f11554f 100644 --- a/crsn_stacks/src/exec.rs +++ b/crsn_stacks/src/exec.rs @@ -1,16 +1,16 @@ use std::collections::{HashMap, VecDeque}; +use crsn::asm::data::{Rd, RdObj, Wr}; use crsn::asm::data::literal::Value; use crsn::asm::instr::Cond; use crsn::module::{EvalRes, OpTrait}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; - -use crate::defs::StackOp; -use crsn::sexp::Sexp; use crsn::sexp; +use crsn::sexp::Sexp; use crsn::utils::A; -use crsn::asm::data::{RdObj, Rd, Wr}; + +use crate::defs::StackOp; #[derive(Debug, Default)] struct Stacks { @@ -66,7 +66,7 @@ pub(crate) fn drop_obj(state: &mut RunState, handle: Value) -> Result Ok(stacks.store.remove(&handle).map(|_| ())) } -fn prepare_push(state: &mut RunState, obj: &RdObj, src: &Rd, pushfn : impl FnOnce(&mut VecDeque, Value) -> ()) -> Result<(), Fault> { +fn prepare_push(state: &mut RunState, obj: &RdObj, src: &Rd, pushfn: impl FnOnce(&mut VecDeque, Value) -> ()) -> Result<(), Fault> { state.clear_status(); let handle = state.read_obj(*obj)?; let val = state.read(*src)?; @@ -79,7 +79,7 @@ fn prepare_push(state: &mut RunState, obj: &RdObj, src: &Rd, pushfn : impl FnOnc Ok(()) } -fn prepare_pop(state: &mut RunState, dst: &Wr, obj: &RdObj, popfn : impl FnOnce(&mut VecDeque) -> Option) -> Result<(), Fault> { +fn prepare_pop(state: &mut RunState, dst: &Wr, obj: &RdObj, popfn: impl FnOnce(&mut VecDeque) -> Option) -> Result<(), Fault> { state.clear_status(); let handle = state.read_obj(*obj)?; diff --git a/crsn_stacks/src/lib.rs b/crsn_stacks/src/lib.rs index 6059f4e..424a813 100644 --- a/crsn_stacks/src/lib.rs +++ b/crsn_stacks/src/lib.rs @@ -25,7 +25,7 @@ impl CrsnExtension for StackOps { "stacks" } - fn parse_op<'a>(&self, pos: SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { + fn parse_op<'a>(&self, pos: &SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { parse::parse(pos, keyword, args) } diff --git a/crsn_stacks/src/parse.rs b/crsn_stacks/src/parse.rs index 0458180..5fb3199 100644 --- a/crsn_stacks/src/parse.rs +++ b/crsn_stacks/src/parse.rs @@ -2,11 +2,11 @@ use crsn::asm::error::CrsnError; use crsn::asm::instr::op::OpKind; use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::ParseRes; +use crsn::sexp::SourcePosition; use crate::defs::StackOp; -use crsn::sexp::SourcePosition; -pub(crate) fn parse<'a>(pos: SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { +pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { "stack" => { StackOp::NewStack { diff --git a/launcher/src/main.rs b/launcher/src/main.rs index 95e0965..748c0f5 100644 --- a/launcher/src/main.rs +++ b/launcher/src/main.rs @@ -10,11 +10,11 @@ use clappconfig::clap::ArgMatches; use serde::{Deserialize, Serialize}; use crsn::asm::data::literal::Addr; +use crsn::module::OpTrait; use crsn::runtime::run_thread::{RunThread, ThreadToken}; use crsn_arith::ArithOps; use crsn_screen::ScreenOps; use crsn_stacks::StackOps; -use crsn::module::OpTrait; mod read_file; mod serde_duration_millis; diff --git a/lib/spanned_sexp/.travis.yml b/lib/spanned_sexp/.travis.yml index a4430b7..9a98210 100644 --- a/lib/spanned_sexp/.travis.yml +++ b/lib/spanned_sexp/.travis.yml @@ -8,9 +8,9 @@ addons: - libelf-dev - libdw-dev rust: -- nightly + - nightly os: -- linux + - linux env: global: @@ -21,16 +21,16 @@ before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: -- travis-cargo build -- travis-cargo test -- travis-cargo bench -- travis-cargo doc + - travis-cargo build + - travis-cargo test + - travis-cargo bench + - travis-cargo doc after_success: -- | - [ $TRAVIS_BRANCH = master ] && - [ $TRAVIS_PULL_REQUEST = false ] && - echo '' > target/doc/index.html && - git clone --depth 1 https://github.com/davisp/ghp-import && - ./ghp-import/ghp-import -n target/doc && - git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages -- travis-cargo coveralls --no-sudo + - | + [ $TRAVIS_BRANCH = master ] && + [ $TRAVIS_PULL_REQUEST = false ] && + echo '' > target/doc/index.html && + git clone --depth 1 https://github.com/davisp/ghp-import && + ./ghp-import/ghp-import -n target/doc && + git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages + - travis-cargo coveralls --no-sudo diff --git a/lib/spanned_sexp/Cargo.toml b/lib/spanned_sexp/Cargo.toml index 4742a78..7ba146a 100644 --- a/lib/spanned_sexp/Cargo.toml +++ b/lib/spanned_sexp/Cargo.toml @@ -4,12 +4,12 @@ version = "1.1.4" authors = ["Clark Gaebel "] documentation = "https://cgaebel.github.io/sexp" -homepage = "https://github.com/cgaebel/sexp" -repository = "https://github.com/cgaebel/sexp" +homepage = "https://github.com/cgaebel/sexp" +repository = "https://github.com/cgaebel/sexp" readme = "README.md" -keywords = [ "sexp", "parsing", "s-expression", "file-format" ] +keywords = ["sexp", "parsing", "s-expression", "file-format"] description = "A small, simple, self-contained, s-expression parser and pretty-printer." diff --git a/lib/spanned_sexp/src/error.rs b/lib/spanned_sexp/src/error.rs index 34ab7aa..1807365 100644 --- a/lib/spanned_sexp/src/error.rs +++ b/lib/spanned_sexp/src/error.rs @@ -12,11 +12,11 @@ pub struct Error { #[derive(Debug, PartialEq, Clone, Default)] pub struct SourcePosition { /// The line number on which the error occurred. - pub line: usize, + pub line: u32, /// The column number on which the error occurred. - pub column: usize, + pub column: u32, /// The index in the given string which caused the error. - pub index: usize, + pub index: u32, } /// Since errors are the uncommon case, they're boxed. This keeps the size of @@ -56,9 +56,9 @@ pub(crate) fn get_line_and_column(s: &str, pos: usize) -> SourcePosition { } } SourcePosition { - line, - column: cmp::max(col, 0) as usize, - index: pos, + line: line as u32, + column: cmp::max(col, 0) as u32, + index: pos as u32, } } @@ -66,7 +66,7 @@ pub(crate) fn get_line_and_column(s: &str, pos: usize) -> SourcePosition { fn err_impl(message: &'static str, s: &str, pos: &usize) -> Err { Box::new(Error { message, - pos: get_line_and_column(s, *pos) + pos: get_line_and_column(s, *pos), }) } diff --git a/lib/spanned_sexp/src/lib.rs b/lib/spanned_sexp/src/lib.rs index ee0ba18..bbbaa9c 100644 --- a/lib/spanned_sexp/src/lib.rs +++ b/lib/spanned_sexp/src/lib.rs @@ -11,9 +11,9 @@ use std::borrow::Cow; use std::fmt; use std::str::{self, FromStr}; +use error::{ERes, err, spos}; pub use error::Error; pub use error::SourcePosition; -use error::{ERes, err, spos}; #[cfg(test)] mod test; diff --git a/lib/spanned_sexp/src/test.rs b/lib/spanned_sexp/src/test.rs index f96beca..f32f2ce 100644 --- a/lib/spanned_sexp/src/test.rs +++ b/lib/spanned_sexp/src/test.rs @@ -27,8 +27,8 @@ fn test_pp() { fn test_tight_parens() { let s = "(hello(world))"; let sexp = parse(s).unwrap(); - assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into()), None), - Sexp::List(vec![Sexp::Atom(Atom::S("world".into()), None)], None)], None)); + assert_eq!(sexp, Sexp::List(vec![Sexp::Atom(Atom::S("hello".into()), Default::default()), + Sexp::List(vec![Sexp::Atom(Atom::S("world".into()), Default::default())], Default::default())], Default::default())); let s = "(this (has)tight(parens))"; let s2 = "( this ( has ) tight ( parens ) )"; assert_eq!(parse(s).unwrap(), parse(s2).unwrap()); @@ -66,5 +66,5 @@ fn line_and_col_test() { fn sexp_size() { // I just want to see when this changes, in the diff. use std::mem; - assert_eq!(mem::size_of::(), mem::size_of::() * 5); + assert_eq!(mem::size_of::(), mem::size_of::() * 6); } From 214d67364342721527add92c38a1888858441ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 23:17:47 +0200 Subject: [PATCH 11/12] remove junk, better explain whats going on with spanned_sexp --- lib/spanned_sexp/.cargo-ok | 1 - lib/spanned_sexp/.editorconfig | 10 ---------- lib/spanned_sexp/.travis.yml | 36 ---------------------------------- lib/spanned_sexp/Cargo.toml | 5 ----- lib/spanned_sexp/README.md | 16 +++++---------- 5 files changed, 5 insertions(+), 63 deletions(-) delete mode 100644 lib/spanned_sexp/.cargo-ok delete mode 100644 lib/spanned_sexp/.editorconfig delete mode 100644 lib/spanned_sexp/.travis.yml diff --git a/lib/spanned_sexp/.cargo-ok b/lib/spanned_sexp/.cargo-ok deleted file mode 100644 index b5754e2..0000000 --- a/lib/spanned_sexp/.cargo-ok +++ /dev/null @@ -1 +0,0 @@ -ok \ No newline at end of file diff --git a/lib/spanned_sexp/.editorconfig b/lib/spanned_sexp/.editorconfig deleted file mode 100644 index fa9becf..0000000 --- a/lib/spanned_sexp/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -root = true - -[*] -end_of_line = lf -insert_final_newline = true -charset = utf-8 -indent_style = space -indent_size = 2 -tab_width = 4 -trim_trailing_whitespace = true diff --git a/lib/spanned_sexp/.travis.yml b/lib/spanned_sexp/.travis.yml deleted file mode 100644 index 9a98210..0000000 --- a/lib/spanned_sexp/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: rust -sudo: false -# necessary for `travis-cargo coveralls --no-sudo` -addons: - apt: - packages: - - libcurl4-openssl-dev - - libelf-dev - - libdw-dev -rust: - - nightly -os: - - linux - -env: - global: - - TRAVIS_CARGO_NIGHTLY_FEATURE="" - - secure: C2P1wLHzBxccS3jrimsG2TaDy4sAhYiKSq1g+cwYHhAKZKkiIpL7Ez5iEHH6BbEvvg4HiUJy4j0w83luZ/FXUuxkD2GZsXWoG+20DFBTLQvCJE/LPahVNbb5i+NdmyIsZPHLloXNvT63hXwu8KNV4U0hrYAgViIXkumoLnOiQD/jim81i7gxUOSe65AzMHcfPRaAwKHn+NGIvUfwMzU2hKZbnH/BPIi2PNtQ6e0VZEvAqA5Ad3hRV0YaBKZ3HZn8tr8UnHKmLbPffb/01EVWAFBU+rFMVYrdzDsiVp7UHMPtVV9aNXUVszB+a/ASWHsAZEdX8XsbmH9RSEBCzsUq2j2HFM2R7yYZnkL3FPcpf/ZKgy4ZVw6gKO42DCvBRGwhI1JMjeKBmrzCGZHE70FxD0zAZRwX9n9M7mUKhakzMvs/LSKMQKlOJslSR+OLEUpr3MCBthpKIiajNYDrJL5P/3KrFOF2R4H/2Z91/3osEIRqzYiEKdeJU01Yef5FCI+H6SLvbhIlVAQTM0IJKGAP0B2N6J4Ot7XrYuGDQag48oPzWzJ2dOGwYjwkda1rgW7pdjtWuullOi2ob1zdI6y/i/CdAS8AE0yRz7VCK4grwonUICzdVaaIAaTTd0yq9PRWAjSjZqNG5EOLADzABIihPnkBw4WygoDq18rSkk0pRbE= - -before_script: - - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH - -script: - - travis-cargo build - - travis-cargo test - - travis-cargo bench - - travis-cargo doc -after_success: - - | - [ $TRAVIS_BRANCH = master ] && - [ $TRAVIS_PULL_REQUEST = false ] && - echo '' > target/doc/index.html && - git clone --depth 1 https://github.com/davisp/ghp-import && - ./ghp-import/ghp-import -n target/doc && - git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages - - travis-cargo coveralls --no-sudo diff --git a/lib/spanned_sexp/Cargo.toml b/lib/spanned_sexp/Cargo.toml index 7ba146a..6c03b9f 100644 --- a/lib/spanned_sexp/Cargo.toml +++ b/lib/spanned_sexp/Cargo.toml @@ -2,17 +2,12 @@ name = "sexp" version = "1.1.4" authors = ["Clark Gaebel "] - documentation = "https://cgaebel.github.io/sexp" homepage = "https://github.com/cgaebel/sexp" repository = "https://github.com/cgaebel/sexp" - readme = "README.md" - keywords = ["sexp", "parsing", "s-expression", "file-format"] - description = "A small, simple, self-contained, s-expression parser and pretty-printer." - license = "MIT" [dependencies] diff --git a/lib/spanned_sexp/README.md b/lib/spanned_sexp/README.md index 6da62fb..e49f312 100644 --- a/lib/spanned_sexp/README.md +++ b/lib/spanned_sexp/README.md @@ -1,15 +1,9 @@ -Sexp +Source-location tracking Sexp ===== -A small, simple, self-contained, s-expression parser and pretty-printer. +**This is a fork of "sexp", updated to the 2018 edition, where each parsed node tracks its +source file position. This enables better error reporting in subsequent parsing and processing.** -[![crates.io](https://img.shields.io/crates/v/sexp.svg)](https://crates.io/crates/sexp/) +--- -[![Build Status](https://travis-ci.org/cgaebel/sexp.svg?branch=master)](https://travis-ci.org/cgaebel/sexp) - -[![Coverage Status](https://coveralls.io/repos/cgaebel/sexp/badge.svg?branch=master&service=github)](https://coveralls.io/github/cgaebel/sexp?branch=master) - -Documentation -------------- - -See the [API Docs](https://cgaebel.github.io/sexp/). +Original version by Clark Gaebel: [https://github.com/cgaebel/sexp](https://github.com/cgaebel/sexp). From dbc445424446caf021f2d8a72433bcdc87ee9158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 6 Oct 2020 23:26:52 +0200 Subject: [PATCH 12/12] cleaning; optimize screen_bounce.csn --- crsn/src/asm/parse/arg_parser.rs | 1 - crsn/src/builtin/parse.rs | 1 - examples/screen_bounce.csn | 25 ++++++++++++------------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/crsn/src/asm/parse/arg_parser.rs b/crsn/src/asm/parse/arg_parser.rs index a5d911c..8f4e3dc 100644 --- a/crsn/src/asm/parse/arg_parser.rs +++ b/crsn/src/asm/parse/arg_parser.rs @@ -5,7 +5,6 @@ use crate::asm::error::CrsnError; use crate::asm::parse::parse_data::{parse_rd, parse_wr}; use crate::asm::parse::ParserContext; use crate::asm::parse::sexp_expect::expect_string_atom; -use crate::asm::patches::NextOrErr; /// Utility for argument parsing pub struct TokenParser<'a> { diff --git a/crsn/src/builtin/parse.rs b/crsn/src/builtin/parse.rs index ec5a57f..c8bb9d3 100644 --- a/crsn/src/builtin/parse.rs +++ b/crsn/src/builtin/parse.rs @@ -290,7 +290,6 @@ mod test { use sexp::SourcePosition; - use crate::asm::instr::Flatten; use crate::asm::parse::{parse_instructions, ParserContext}; use crate::asm::parse::sexp_expect::expect_list; use crate::builtin::BuiltinOps; diff --git a/examples/screen_bounce.csn b/examples/screen_bounce.csn index a1c24a0..c367f33 100644 --- a/examples/screen_bounce.csn +++ b/examples/screen_bounce.csn @@ -4,21 +4,20 @@ (sc-opt 1 1) ; auto blit (sc-opt 2 25) ; frame rate - (ld r0 5) ; x - (ld r1 0) ; y + (sym x r0) (sym y r1) (sym dx r2) (sym dy r3) (sym color r5) - (ld r2 1) ; dx - (ld r3 1) ; dy - - (ld r5 0x3300ff) + (ld x 5) (ld y 0) + (ld dx 1) (ld dy 1) + (ld color 0x3300ff) (:loop) - (add r5 0x000001) - (sc-px r0 r1 r5) - (add r0 r2) - (add r1 r3) - (cmp r0 799 (eq? (ld r2 -1)) (ne? (cmp r0 0 (eq? (ld r2 1))))) - (cmp r1 599 (eq? (ld r3 -1)) (ne? (cmp r1 0 (eq? (ld r3 1))))) + (add color 0x000001) + (sc-px x y color) + + (add x dx (z? (ld dx 1))) + (cmp x 799 (eq? (ld dx -1))) + + (add y dy (z? (ld dy 1))) + (cmp y 599 (eq? (ld dy -1))) (j :loop) - (fault "unreachable") )