Improve error output; Add syntax error check
This commit is contained in:
parent
2fee80843d
commit
0fa0a026f8
@ -4,8 +4,8 @@ new_data = []
|
||||
|
||||
for commit_data in data {
|
||||
new_data += {
|
||||
message = commit_data.commit.message
|
||||
name = commit_data.commit.committer.name
|
||||
message = commit_data:commit:message
|
||||
name = commit_data:commit:committer:name
|
||||
}
|
||||
}
|
||||
|
||||
|
26
src/error.rs
26
src/error.rs
@ -397,10 +397,14 @@ impl fmt::Display for Error {
|
||||
actual,
|
||||
location,
|
||||
relevant_source,
|
||||
} => write!(
|
||||
f,
|
||||
"Expected {expected}, but got {actual} at {location}. Code: {relevant_source} ",
|
||||
),
|
||||
} => {
|
||||
let location = get_position(location);
|
||||
|
||||
write!(
|
||||
f,
|
||||
"Expected {expected}, but got {actual} at {location}. Code: {relevant_source} ",
|
||||
)
|
||||
}
|
||||
WrongColumnAmount { expected, actual } => write!(
|
||||
f,
|
||||
"Wrong column amount. Expected {expected} but got {actual}."
|
||||
@ -408,7 +412,9 @@ impl fmt::Display for Error {
|
||||
External(message) => write!(f, "External error: {message}"),
|
||||
CustomMessage(message) => write!(f, "{message}"),
|
||||
Syntax { source, location } => {
|
||||
write!(f, "Syntax error at {location}, this is not valid: {source}")
|
||||
let location = get_position(location);
|
||||
|
||||
write!(f, "Syntax error at {location}: {source}")
|
||||
}
|
||||
TypeCheck { expected, actual } => write!(
|
||||
f,
|
||||
@ -418,7 +424,11 @@ impl fmt::Display for Error {
|
||||
error,
|
||||
location,
|
||||
source,
|
||||
} => write!(f, "{error} Occured at {location}: \"{source}\""),
|
||||
} => {
|
||||
let location = get_position(location);
|
||||
|
||||
write!(f, "{error} Occured at {location}: \"{source}\"")
|
||||
}
|
||||
SerdeJson(message) => write!(f, "JSON processing error: {message}"),
|
||||
ParserCancelled => write!(
|
||||
f,
|
||||
@ -427,3 +437,7 @@ impl fmt::Display for Error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_position(position: &Point) -> String {
|
||||
format!("column {}, row {}", position.row + 1, position.column)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! You can use this library externally by calling either of the "eval"
|
||||
//! functions or by constructing your own Evaluator.
|
||||
use tree_sitter::{Parser, Tree as TSTree};
|
||||
use tree_sitter::{Node, Parser, Tree as TSTree, TreeCursor};
|
||||
|
||||
use crate::{language, AbstractTree, Error, Map, Result, Root, Value};
|
||||
|
||||
@ -69,12 +69,39 @@ impl Interpreter {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_only(&mut self, source: &str) {
|
||||
self.syntax_tree = self.parser.parse(source, self.syntax_tree.as_ref());
|
||||
pub fn parse(&mut self, source: &str) -> Result<()> {
|
||||
fn check_for_error(source: &str, node: Node, cursor: &mut TreeCursor) -> Result<()> {
|
||||
if node.is_error() {
|
||||
Err(Error::Syntax {
|
||||
source: source[node.byte_range()].to_string(),
|
||||
location: node.start_position(),
|
||||
})
|
||||
} else {
|
||||
for child in node.children(&mut cursor.clone()) {
|
||||
check_for_error(source, child, cursor)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let syntax_tree = self.parser.parse(source, None);
|
||||
|
||||
if let Some(tree) = &syntax_tree {
|
||||
let root = tree.root_node();
|
||||
let mut cursor = root.walk();
|
||||
|
||||
check_for_error(source, root, &mut cursor)?;
|
||||
}
|
||||
|
||||
self.syntax_tree = syntax_tree;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(&mut self, source: &str) -> Result<Value> {
|
||||
self.syntax_tree = self.parser.parse(source, self.syntax_tree.as_ref());
|
||||
self.parse(source)?;
|
||||
|
||||
self.abstract_tree = if let Some(syntax_tree) = &self.syntax_tree {
|
||||
Some(Root::from_syntax_node(
|
||||
source,
|
||||
|
@ -75,7 +75,7 @@ fn main() {
|
||||
let mut interpreter = Interpreter::new(context).unwrap();
|
||||
|
||||
if args.show_syntax_tree {
|
||||
interpreter.parse_only(&source);
|
||||
interpreter.parse(&source).unwrap();
|
||||
|
||||
println!("{}", interpreter.syntax_tree().unwrap());
|
||||
}
|
||||
|
@ -94,27 +94,27 @@ mod value {
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn interpret_empty() {
|
||||
fn empty() {
|
||||
assert_eq!(interpret("x = 9"), Ok(Value::Option(None)));
|
||||
assert_eq!(interpret("x = 1 + 1"), Ok(Value::Option(None)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_integer() {
|
||||
fn integer() {
|
||||
assert_eq!(interpret("1"), Ok(Value::Integer(1)));
|
||||
assert_eq!(interpret("123"), Ok(Value::Integer(123)));
|
||||
assert_eq!(interpret("-666"), Ok(Value::Integer(-666)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_float() {
|
||||
fn float() {
|
||||
assert_eq!(interpret("0.1"), Ok(Value::Float(0.1)));
|
||||
assert_eq!(interpret("12.3"), Ok(Value::Float(12.3)));
|
||||
assert_eq!(interpret("-6.66"), Ok(Value::Float(-6.66)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_string() {
|
||||
fn string() {
|
||||
assert_eq!(interpret("\"one\""), Ok(Value::String("one".to_string())));
|
||||
assert_eq!(interpret("'one'"), Ok(Value::String("one".to_string())));
|
||||
assert_eq!(interpret("`one`"), Ok(Value::String("one".to_string())));
|
||||
@ -127,7 +127,7 @@ mod value {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_list() {
|
||||
fn list() {
|
||||
assert_eq!(
|
||||
interpret("[1, 2, 'foobar']"),
|
||||
Ok(Value::List(List::with_items(vec![
|
||||
@ -139,7 +139,7 @@ mod value {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_map() {
|
||||
fn map() {
|
||||
let map = Map::new();
|
||||
|
||||
map.set("x".to_string(), Value::Integer(1), None).unwrap();
|
||||
@ -150,7 +150,7 @@ mod value {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_map_types() {
|
||||
fn map_types() {
|
||||
let map = Map::new();
|
||||
|
||||
map.set("x".to_string(), Value::Integer(1), Some(Type::Integer))
|
||||
@ -169,7 +169,7 @@ mod value {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_map_type_errors() {
|
||||
fn map_type_errors() {
|
||||
assert!(interpret("{ foo <bool> = 'bar' }")
|
||||
.unwrap_err()
|
||||
.is_type_check_error(&Error::TypeCheck {
|
||||
@ -179,15 +179,15 @@ mod value {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_function() {
|
||||
let result = interpret("() -> <int> { 1 }");
|
||||
fn function() {
|
||||
let result = interpret("() <int> { 1 }");
|
||||
let value = result.unwrap();
|
||||
let function = value.as_function().unwrap();
|
||||
|
||||
assert_eq!(&Vec::<Identifier>::with_capacity(0), function.parameters());
|
||||
assert_eq!(&Type::Integer, function.return_type());
|
||||
|
||||
let result = interpret("(x <bool>) -> <bool> {true}");
|
||||
let result = interpret("(x <bool>) <bool> { true }");
|
||||
let value = result.unwrap();
|
||||
let function = value.as_function().unwrap();
|
||||
|
||||
@ -199,7 +199,7 @@ mod value {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_option() {
|
||||
fn option() {
|
||||
let result = interpret("x <option(int)> = some(1); x").unwrap();
|
||||
|
||||
assert_eq!(Value::Option(Some(Box::new(Value::Integer(1)))), result);
|
||||
@ -210,11 +210,11 @@ mod function_call {
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn interpret_function_call() {
|
||||
fn function_call() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
"
|
||||
foobar = (message <str>) -> <str> { message }
|
||||
foobar = (message <str>) <str> { message }
|
||||
foobar('Hiya')
|
||||
",
|
||||
),
|
||||
@ -223,7 +223,20 @@ mod function_call {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_callback() {
|
||||
fn call_empty_function() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
"
|
||||
foobar = (message <str>) <none> {}
|
||||
foobar('Hiya')
|
||||
",
|
||||
),
|
||||
Ok(Value::none())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callback() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
"
|
||||
@ -238,7 +251,7 @@ mod function_call {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_built_in_function_call() {
|
||||
fn built_in_function_call() {
|
||||
assert_eq!(interpret("output('Hiya')"), Ok(Value::Option(None)));
|
||||
}
|
||||
}
|
||||
@ -247,7 +260,7 @@ mod if_else {
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn interpret_if() {
|
||||
fn r#if() {
|
||||
assert_eq!(
|
||||
interpret("if true { 'true' }"),
|
||||
Ok(Value::String("true".to_string()))
|
||||
@ -255,7 +268,7 @@ mod if_else {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_if_else() {
|
||||
fn if_else() {
|
||||
assert_eq!(
|
||||
interpret("if false { 1 } else { 2 }"),
|
||||
Ok(Value::Integer(2))
|
||||
@ -267,7 +280,7 @@ mod if_else {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_if_else_else_if_else() {
|
||||
fn if_else_else_if_else() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
"
|
||||
@ -285,7 +298,7 @@ mod if_else {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_if_else_if_else_if_else_if_else() {
|
||||
fn if_else_if_else_if_else_if_else() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
"
|
||||
@ -329,13 +342,13 @@ mod index {
|
||||
let test = interpret(
|
||||
"
|
||||
x = [1 2 3]
|
||||
y = () -> <int> { 0 }
|
||||
y = () <int> { 2 }
|
||||
x:y()
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Value::Integer(1), test);
|
||||
assert_eq!(Value::Integer(3), test);
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,7 +356,7 @@ mod r#match {
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn interpret_match() {
|
||||
fn r#match() {
|
||||
let test = interpret(
|
||||
"
|
||||
match 1 {
|
||||
@ -359,7 +372,7 @@ mod r#match {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_match_assignment() {
|
||||
fn match_assignment() {
|
||||
let test = interpret(
|
||||
"
|
||||
x = match 1 {
|
||||
@ -380,7 +393,7 @@ mod r#while {
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn interpret_while_loop() {
|
||||
fn while_loop() {
|
||||
assert_eq!(interpret("while false { 'foo' }"), Ok(Value::Option(None)))
|
||||
}
|
||||
|
||||
@ -428,19 +441,4 @@ mod type_definition {
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modify_value_async() {
|
||||
let result = interpret(
|
||||
"
|
||||
fn = (x <int>) <none> {
|
||||
|
||||
}
|
||||
|
||||
fn(1)
|
||||
",
|
||||
);
|
||||
|
||||
assert_eq!(Ok(Value::none()), result);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
Anonymous Function
|
||||
================================================================================
|
||||
|
||||
() <str> { "Hiya" }
|
||||
() -> <str> { "Hiya" }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -11,14 +11,6 @@ module.exports = grammar({
|
||||
|
||||
_comment: $ => /[#][^#\n]*[#|\n]/,
|
||||
|
||||
block: $ =>
|
||||
seq(
|
||||
optional('async'),
|
||||
'{',
|
||||
repeat1($.statement),
|
||||
'}',
|
||||
),
|
||||
|
||||
statement: $ =>
|
||||
prec.left(
|
||||
seq(
|
||||
@ -70,6 +62,14 @@ module.exports = grammar({
|
||||
),
|
||||
),
|
||||
|
||||
block: $ =>
|
||||
seq(
|
||||
optional('async'),
|
||||
'{',
|
||||
repeat($.statement),
|
||||
'}',
|
||||
),
|
||||
|
||||
identifier: $ =>
|
||||
choice(
|
||||
$._identifier_pattern,
|
||||
@ -175,7 +175,7 @@ module.exports = grammar({
|
||||
map: $ =>
|
||||
seq(
|
||||
'{',
|
||||
repeat(
|
||||
repeat1(
|
||||
seq(
|
||||
$.identifier,
|
||||
optional($.type_definition),
|
||||
|
@ -17,38 +17,6 @@
|
||||
"type": "PATTERN",
|
||||
"value": "[#][^#\\n]*[#|\\n]"
|
||||
},
|
||||
"block": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "async"
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "{"
|
||||
},
|
||||
{
|
||||
"type": "REPEAT1",
|
||||
"content": {
|
||||
"type": "SYMBOL",
|
||||
"name": "statement"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"statement": {
|
||||
"type": "PREC_LEFT",
|
||||
"value": 0,
|
||||
@ -202,6 +170,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"block": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "async"
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "{"
|
||||
},
|
||||
{
|
||||
"type": "REPEAT",
|
||||
"content": {
|
||||
"type": "SYMBOL",
|
||||
"name": "statement"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"identifier": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
@ -518,7 +518,7 @@
|
||||
"value": "{"
|
||||
},
|
||||
{
|
||||
"type": "REPEAT",
|
||||
"type": "REPEAT1",
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
|
@ -37,7 +37,7 @@
|
||||
"fields": {},
|
||||
"children": {
|
||||
"multiple": true,
|
||||
"required": true,
|
||||
"required": false,
|
||||
"types": [
|
||||
{
|
||||
"type": "statement",
|
||||
@ -365,7 +365,7 @@
|
||||
"fields": {},
|
||||
"children": {
|
||||
"multiple": true,
|
||||
"required": false,
|
||||
"required": true,
|
||||
"types": [
|
||||
{
|
||||
"type": "identifier",
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user