[sc-609] Update for fixed version of TSPL

This commit is contained in:
Nicolas Abril 2024-04-11 15:26:44 +02:00
parent a525046690
commit 1c124e9a83
11 changed files with 65 additions and 151 deletions

16
Cargo.lock generated
View File

@ -11,6 +11,14 @@ dependencies = [
"highlight_error",
]
[[package]]
name = "TSPL"
version = "0.0.9"
source = "git+https://github.com/developedby/TSPL.git?branch=fix-hvml-bugs#f72a02d79c399c9fcb7ae0a2d4985c0451124afc"
dependencies = [
"highlight_error",
]
[[package]]
name = "anstream"
version = "0.6.13"
@ -167,9 +175,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "hashbrown"
version = "0.14.3"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "heck"
@ -188,7 +196,7 @@ name = "hvm-core"
version = "0.2.24"
source = "git+https://github.com/HigherOrderCO/hvm-core.git?branch=hvm32-compat#8bc55acaa14bb18d889e2c80a300a2fadb400d03"
dependencies = [
"TSPL",
"TSPL 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec",
"clap",
"nohash-hasher",
@ -201,7 +209,7 @@ dependencies = [
name = "hvm-lang"
version = "0.1.0"
dependencies = [
"TSPL",
"TSPL 0.0.9 (git+https://github.com/developedby/TSPL.git?branch=fix-hvml-bugs)",
"clap",
"highlight_error",
"hvm-core",

View File

@ -20,7 +20,7 @@ default = ["cli"]
cli = ["dep:clap"]
[dependencies]
TSPL = "0.0.9"
TSPL = { git = "https://github.com/developedby/TSPL.git", branch = "fix-hvml-bugs" }
clap = { version = "4.4.1", features = ["derive"], optional = true }
highlight_error = "0.1.1"
hvm-core = { git = "https://github.com/HigherOrderCO/hvm-core.git", branch = "hvm32-compat" }

View File

@ -89,7 +89,7 @@ enum Mode {
#[arg(help = "Path to the input file")]
path: PathBuf,
#[arg(value_parser = |arg: &str| hvml::term::parser::TermParser::new_term(arg))]
#[arg(value_parser = |arg: &str| hvml::term::parser::TermParser::new(arg).parse_term())]
arguments: Option<Vec<hvml::term::Term>>,
},
/// Runs the lambda-term level desugaring passes.

View File

@ -24,7 +24,8 @@ pub const NAT_ZERO: &str = "Nat.zero";
impl Book {
pub fn builtins() -> Book {
TermParser::new_book(BUILTINS, Book::default(), true)
TermParser::new(BUILTINS)
.parse_book(Book::default(), true)
.expect("Error parsing builtin file, this should not happen")
}

View File

@ -8,5 +8,7 @@ pub fn load_file_to_book(path: &Path) -> Result<Book, String> {
}
pub fn do_parse_book(code: &str, path: &Path) -> Result<Book, String> {
TermParser::new_book(code, Book::builtins(), false).map_err(|e| format!("In {} :\n{}", path.display(), e))
TermParser::new(code)
.parse_book(Book::builtins(), false)
.map_err(|e| format!("In {} :\n{}", path.display(), e))
}

View File

@ -46,21 +46,19 @@ use super::NumType;
// <Number> ::= ([0-9]+ | "0x"[0-9a-fA-F]+ | "0b"[01]+)
// <Operator> ::= ( "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<<" | ">>" | "<=" | ">=" | "<" | ">" | "^" )
TSPL::new_parser!(TermParser);
pub struct TermParser<'i> {
input: &'i str,
index: usize,
}
impl<'a> TermParser<'a> {
// TODO: Since TSPL doesn't expose `new` we need something that creates the parser.
pub fn new_book(input: &'a str, default_book: Book, builtin: bool) -> Result<Book, String> {
Self::new(input).parse_book(default_book, builtin)
}
pub fn new_term(input: &'a str) -> Result<Term, String> {
Self::new(input).parse_term()
pub fn new(input: &'a str) -> Self {
Self { input, index: 0 }
}
/* AST parsing functions */
fn parse_book(&mut self, default_book: Book, builtin: bool) -> Result<Book, String> {
pub fn parse_book(&mut self, default_book: Book, builtin: bool) -> Result<Book, String> {
let mut book = default_book;
self.skip_trivia();
while !self.is_eof() {
@ -69,7 +67,7 @@ impl<'a> TermParser<'a> {
// adt declaration
let (nam, adt) = self.parse_datatype(builtin)?;
let end_idx = *self.index();
book.add_adt(nam, adt).map_err(|e| add_ctx(&e, ini_idx, end_idx, self.input()))?;
book.add_adt(nam, adt).map_err(|e| add_ctx_to_msg(&e, ini_idx, end_idx, self.input()))?;
} else {
// function declaration rule
let (name, rule) = self.parse_rule()?;
@ -206,7 +204,7 @@ impl<'a> TermParser<'a> {
})
}
fn parse_term(&mut self) -> Result<Term, String> {
pub fn parse_term(&mut self) -> Result<Term, String> {
maybe_grow(|| {
let (tag, unexpected_tag) = self.parse_tag()?;
let Some(head) = self.skip_peek_one() else { return self.expected("term") };
@ -582,17 +580,6 @@ impl<'a> TermParser<'a> {
}
}
/* Overrides */
/// Generates an error message for parsing failures, including the highlighted context.
///
/// Override to have our own error message.
fn expected<T>(&mut self, exp: &str) -> Result<T, String> {
let ini_idx = *self.index();
let end_idx = *self.index() + 1;
self.expected_spanned(exp, ini_idx, end_idx)
}
fn expected_spanned<T>(&mut self, exp: &str, ini_idx: usize, end_idx: usize) -> Result<T, String> {
let ctx = highlight_error(ini_idx, end_idx, self.input());
let is_eof = self.is_eof();
@ -600,19 +587,6 @@ impl<'a> TermParser<'a> {
Err(format!("\x1b[1m- expected:\x1b[0m {}\n\x1b[1m- detected:\x1b[0m{}", exp, detected))
}
/// Consumes an instance of the given string, erroring if it is not found.
///
/// Override to have our own error message.
fn consume(&mut self, text: &str) -> Result<(), String> {
self.skip_trivia();
if self.input().get(*self.index() ..).unwrap_or_default().starts_with(text) {
*self.index() += text.len();
Ok(())
} else {
self.expected(format!("'{text}'").as_str())
}
}
/// Consumes text if the input starts with it. Otherwise, do nothing.
fn try_consume(&mut self, text: &str) -> bool {
self.skip_trivia();
@ -623,116 +597,36 @@ impl<'a> TermParser<'a> {
false
}
}
}
/// Parses a name from the input, supporting alphanumeric characters, underscores, periods, and hyphens.
impl<'a> Parser<'a> for TermParser<'a> {
fn input(&mut self) -> &'a str {
self.input
}
fn index(&mut self) -> &mut usize {
&mut self.index
}
/// Generates an error message for parsing failures, including the highlighted context.
///
/// Override to call our own `expected`.
fn parse_name(&mut self) -> Result<String, String> {
self.skip_trivia();
let name = self.take_while(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '/');
if name.is_empty() { self.expected("name") } else { Ok(name.to_owned()) }
/// Override to have our own error message.
fn expected<T>(&mut self, exp: &str) -> Result<T, String> {
let ini_idx = *self.index();
let end_idx = *self.index() + 1;
self.expected_spanned(exp, ini_idx, end_idx)
}
// TODO: Override because the lib has a bug where it will error on '_' .
/// Parses a u64 from the input, supporting dec, hex (0xNUM), and bin (0bNUM).
fn parse_u64(&mut self) -> Result<u64, String> {
/// Consumes an instance of the given string, erroring if it is not found.
///
/// Override to have our own error message.
fn consume(&mut self, text: &str) -> Result<(), String> {
self.skip_trivia();
let radix = match self.peek_many(2) {
Some("0x") => {
self.advance_many(2);
16
}
Some("0b") => {
self.advance_many(2);
2
}
_ => 10,
};
let num_str = self.take_while(move |c| c.is_digit(radix) || c == '_');
let num_str = num_str.chars().filter(|c| *c != '_').collect::<String>();
if num_str.is_empty() {
self.expected("numeric digit")
if self.input().get(*self.index() ..).unwrap_or_default().starts_with(text) {
*self.index() += text.len();
Ok(())
} else {
u64::from_str_radix(&num_str, radix).map_err(|e| e.to_string())
}
}
// TODO: Override to accept more escape sequences
/// Parses a single unicode character, supporting scape sequences.
fn parse_char(&mut self) -> Result<char, String> {
match self.advance_one() {
Some('\\') => match self.advance_one() {
Some('u' | 'U') => {
self.consume("{")?;
let codepoint_str = self.take_while(|c| c.is_ascii_hexdigit());
self.consume("}")?;
u32::from_str_radix(codepoint_str, 16)
.ok()
.and_then(std::char::from_u32)
.ok_or_else(|| self.expected::<char>("unicode-codepoint").unwrap_err())
}
Some('n') => Ok('\n'),
Some('r') => Ok('\r'),
Some('t') => Ok('\t'),
Some('\'') => Ok('\''),
Some('\"') => Ok('\"'),
Some('\\') => Ok('\\'),
Some('0') => Ok('\0'),
Some(chr) => self.expected(&format!("\\{}", chr)),
None => self.expected("escaped-char"),
},
Some(other) => Ok(other),
None => self.expected("char"),
}
}
// TODO: Override to accept more escape sequences
/// Parses a quoted character, like 'x'.
fn parse_quoted_char(&mut self) -> Result<char, String> {
self.skip_trivia();
self.consume("'")?;
let chr = self.parse_char()?;
self.consume("'")?;
Ok(chr)
}
// TODO: Override to accept more escape sequences
/// Parses a quoted string, like "foobar".
fn parse_quoted_string(&mut self) -> Result<String, String> {
self.skip_trivia();
self.consume("\"")?;
let mut result = String::new();
while let Some(chr) = self.peek_one() {
if chr == '"' {
break;
} else {
result.push(self.parse_char()?);
}
}
self.consume("\"")?;
Ok(result)
}
// TODO: override to avoid looping on ending in comment without \n
/// Consumes the next character in the text.
fn skip_trivia(&mut self) {
while let Some(c) = self.peek_one() {
if c.is_ascii_whitespace() {
self.advance_one();
continue;
}
if c == '/' && self.input().get(*self.index() ..).unwrap_or_default().starts_with("//") {
while let Some(c) = self.peek_one() {
if c != '\n' {
self.advance_one();
} else {
break;
}
}
self.advance_one(); // Skip the newline character as well
continue;
}
break;
self.expected(format!("'{text}'").as_str())
}
}
}
@ -772,7 +666,7 @@ impl Book {
}
}
fn add_ctx(msg: &str, ini_idx: usize, end_idx: usize, file: &str) -> String {
fn add_ctx_to_msg(msg: &str, ini_idx: usize, end_idx: usize, file: &str) -> String {
let ctx = highlight_error(ini_idx, end_idx, file);
format!("{msg}\n{ctx}")
}

View File

@ -97,7 +97,7 @@ fn run_golden_test_dir_multiple(test_name: &str, run: &[&RunFn]) {
#[test]
fn compile_term() {
run_golden_test_dir(function_name!(), &|code, _| {
let mut term = TermParser::new_term(code)?;
let mut term = TermParser::new(code).parse_term()?;
let mut vec = Vec::new();
term.check_unbound_vars(&mut HashMap::new(), &mut vec);

View File

@ -0,0 +1 @@
(String.cons '\u{1' "\u2}\u{zxcx}")

View File

@ -1 +1 @@
main = (String.cons '\U{1F30E}' String.nil)
main = (String.cons '\u{1F30E}' String.nil)

View File

@ -1 +1 @@
main = (String.cons '\U{1F30E}' String.nil)
main = (String.cons '\u{1F30E}' String.nil)

View File

@ -0,0 +1,8 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_term/wrong_unicode_escape.hvm
---
Errors:
- expected: '}'
- detected:
 1 | (String.cons '\u{1' "\u2}\u{zxcx}")