Compare commits
6 Commits
7091c81201
...
9857863220
Author | SHA1 | Date | |
---|---|---|---|
9857863220 | |||
15030635fb | |||
1a7dd72783 | |||
b59d014383 | |||
fef5ee8041 | |||
eed0bb8c76 |
@ -6,18 +6,17 @@
|
||||
- Implement functions as first class citizens
|
||||
- Parse __more__ binary operators
|
||||
- Parse more complex bindings
|
||||
- Rework error messages
|
||||
- Parse other language constructions
|
||||
- Namespace identifiers in the symbol table
|
||||
- Stdlib
|
||||
- Document code
|
||||
- Watch mode
|
||||
- Simple language server
|
||||
- Decide how to handle comments in the syntax (?)(should comments mean something like in rust?)
|
||||
- Fix comment handling in the AST
|
||||
- Abstract the parsing of datatypes, such that in the future generics can be implemented in a single place
|
||||
- Begin work on the code formatter
|
||||
- Remove all panic! and todo!
|
||||
- Change REPL to execute code only after `;;` is found
|
||||
- Forward the code generated by the REPL to the PHP repl
|
||||
|
||||
## v0.1.2
|
||||
|
||||
@ -29,6 +28,10 @@
|
||||
- [x] Typecheck if/else if/else
|
||||
- [x] Typecheck for loops
|
||||
- [x] Typecheck while loops
|
||||
- [x] Include Ariadne for error reporting
|
||||
- [x] Migrate lexic errors to new error interface
|
||||
- [x] Migrate syntax errors to new error interface
|
||||
- [x] Migrate semantic errors to new error interface
|
||||
|
||||
|
||||
## v0.1.1
|
||||
|
232
Cargo.lock
generated
232
Cargo.lock
generated
@ -2,6 +2,23 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ariadne"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44055e597c674aef7cb903b2b9f6e4cba1277ed0d2d61dae7cd52d7ffa81f8e2"
|
||||
dependencies = [
|
||||
"concolor",
|
||||
"unicode-width",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.1.0"
|
||||
@ -9,7 +26,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concolor"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b946244a988c390a94667ae0e3958411fa40cc46ea496a929b263d883f5f9c3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"concolor-query",
|
||||
"is-terminal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concolor-query"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
|
||||
dependencies = [
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -24,6 +78,12 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.158"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
@ -92,8 +152,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thp"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"colored",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -105,13 +166,52 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -120,53 +220,165 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
@ -1,12 +1,13 @@
|
||||
[package]
|
||||
name = "thp"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ariadne = { version = "0.4.1", features = ["auto-color"] }
|
||||
colored = "2.1.0"
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
serde_json = "1.0.120"
|
||||
|
26
error_codes.yaml
Normal file
26
error_codes.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
-- A map of error codes to error messages
|
||||
0x000000: Incomplete string
|
||||
0x000001: Invalid hex number
|
||||
0x000002: Invalid octal number
|
||||
0x000003: Invalid binary number
|
||||
0x000004: Invalid floating point number
|
||||
0x000005: Invalid scientific number
|
||||
0x000006: Incomplete multiline comment
|
||||
0x000007: Unfinished statement
|
||||
0x000008: Unexpected tokens
|
||||
0x000009: Incomplete arguments list
|
||||
0x000010: Invalid variable declaration
|
||||
0x000011: Incomplete parameter list
|
||||
0x000012: Invalid parameter declaration
|
||||
0x000013: Invalid while loop
|
||||
0x000014: Invalid function declaration
|
||||
0x000015: Invalid for loop
|
||||
0x000016: Invalid if condition
|
||||
0x000017: Incomplete block
|
||||
0x000018: Missing reference
|
||||
0x000019: Invalid reference
|
||||
0x000020: Unknown error (compiler todo)
|
||||
0x000021: Mismatched types
|
||||
0x000022: Duplicated reference
|
||||
0x000023: Mismatched argument count
|
||||
|
@ -7,7 +7,7 @@ impl Transpilable for PPrimary<'_> {
|
||||
PPrimary::FloatingLiteral(value) => value.to_string(),
|
||||
PPrimary::StringLiteral(value) => format!("\"{}\"", value),
|
||||
PPrimary::Variable(name) => format!("${}", name),
|
||||
PPrimary::Symbol(name) => format!("{}", name),
|
||||
// PPrimary::Symbol(name) => format!("{}", name),
|
||||
PPrimary::BoolLiteral(bool) => {
|
||||
if *bool {
|
||||
String::from("true")
|
||||
|
31
src/error_handling/error_messages.rs
Normal file
31
src/error_handling/error_messages.rs
Normal file
@ -0,0 +1,31 @@
|
||||
//! Contains constants that point to error messages
|
||||
|
||||
pub const LEX_INCOMPLETE_STRING: u32 = 0;
|
||||
pub const LEX_INVALID_HEX_NUMBER: u32 = 1;
|
||||
pub const LEX_INVALID_OCTAL_NUMBER: u32 = 2;
|
||||
pub const LEX_INVALID_BINARY_NUMBER: u32 = 3;
|
||||
pub const LEX_INVALID_FLOATING_NUMBER: u32 = 4;
|
||||
pub const LEX_INVALID_SCIENTIFIC_NUMBER: u32 = 5;
|
||||
pub const LEX_INCOMPLETE_MULTILINE_COMMENT: u32 = 6;
|
||||
pub const SYNTAX_INCOMPLETE_STATEMENT: u32 = 7;
|
||||
pub const SYNTAX_UNEXPECTED_TOKENS: u32 = 8;
|
||||
pub const SYNTAX_INCOMPLETE_ARGUMENT_LIST: u32 = 9;
|
||||
pub const SYNTAX_INVALID_VARIABLE_DECLARATION: u32 = 10;
|
||||
pub const SYNTAX_INCOMPLETE_PARAMETER_LIST: u32 = 11;
|
||||
pub const SYNTAX_INVALID_PARAMETER_DECLARATION: u32 = 12;
|
||||
pub const SYNTAX_INVALID_WHILE_LOOP: u32 = 13;
|
||||
pub const SYNTAX_INVALID_FUNCTION_DECLARATION: u32 = 14;
|
||||
pub const SYNTAX_INVALID_FOR_LOOP: u32 = 15;
|
||||
pub const SYNTAX_INVALID_IF_CONDITION: u32 = 16;
|
||||
pub const SYNTAX_INCOMPLETE_BLOCK: u32 = 17;
|
||||
pub const SEMANTIC_MISSING_REFERENCE: u32 = 18;
|
||||
pub const SEMANTIC_INVALID_REFERENCE: u32 = 19;
|
||||
pub const COMPILER_TODO: u32 = 20;
|
||||
pub const SEMANTIC_MISMATCHED_TYPES: u32 = 21;
|
||||
pub const SEMANTIC_DUPLICATED_REFERENCE: u32 = 22;
|
||||
pub const SEMANTIC_MISMATCHED_ARGUMENT_COUNT: u32 = 23;
|
||||
|
||||
/// Reads the error codes from the error code list
|
||||
pub fn error_code_to_string() -> String {
|
||||
todo!()
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
use super::{LexError, PrintableError};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
impl PrintableError for LexError {
|
||||
fn get_error_str(&self, chars: &Vec<char>) -> String {
|
||||
let line_number = get_line_number(chars, self.position);
|
||||
let (erroneous_code, column_number_zero) = get_line(chars, self.position);
|
||||
let column_number = column_number_zero + 1;
|
||||
|
||||
let line_number_whitespace = " ".repeat(line_number.to_string().len());
|
||||
let whitespace = " ".repeat(column_number_zero);
|
||||
let reason = &self.reason;
|
||||
|
||||
format!(
|
||||
r#"
|
||||
{line_number_whitespace} |
|
||||
{line_number } | {erroneous_code}
|
||||
{line_number_whitespace} | {whitespace}^
|
||||
|
||||
{reason} at line {line_number}:{column_number}"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts a line of code from `chars` and the number of characters in the back.
|
||||
/// `pos` indicates a position, from where to extract the line.
|
||||
///
|
||||
/// Ex. Given:
|
||||
/// - `input = "first line\nsecond line\nthird line"`
|
||||
/// - `pos = 15`
|
||||
///
|
||||
/// this function should return `("second line", 4)`
|
||||
fn get_line(chars: &Vec<char>, pos: usize) -> (String, usize) {
|
||||
let mut result_chars = VecDeque::<char>::new();
|
||||
|
||||
// Push chars to the front until a new line is found
|
||||
// TODO: refactor
|
||||
let mut before_pos = pos;
|
||||
loop {
|
||||
let current_char = chars[before_pos];
|
||||
|
||||
if current_char == '\n' {
|
||||
// This is important because before_pos will be used to calculate
|
||||
// the number of chars before pos
|
||||
before_pos += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
result_chars.push_front(current_char);
|
||||
|
||||
if before_pos == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
before_pos -= 1;
|
||||
}
|
||||
|
||||
// Push chars to the end until a new line is found
|
||||
let mut after_pos = pos + 1;
|
||||
let char_count = chars.len();
|
||||
while after_pos < char_count {
|
||||
let current_char = chars[after_pos];
|
||||
|
||||
if current_char == '\n' {
|
||||
break;
|
||||
}
|
||||
|
||||
result_chars.push_back(current_char);
|
||||
after_pos += 1;
|
||||
}
|
||||
|
||||
(result_chars.iter().collect::<String>(), pos - before_pos)
|
||||
}
|
||||
|
||||
fn get_line_number(chars: &Vec<char>, target_pos: usize) -> usize {
|
||||
let mut count = 1;
|
||||
|
||||
for (pos, char) in chars.iter().enumerate() {
|
||||
if pos >= target_pos {
|
||||
break;
|
||||
}
|
||||
|
||||
if *char == '\n' {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::lexic;
|
||||
|
||||
#[test]
|
||||
fn test_error_msg() {
|
||||
let input = String::from("val name' = 20");
|
||||
let result = lexic::get_tokens(&input);
|
||||
|
||||
match result {
|
||||
Ok(_) => assert!(false),
|
||||
Err(err_data) => {
|
||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||
let err_str = err_data.get_error_str(&chars);
|
||||
|
||||
let expected_str = format!(
|
||||
r#"
|
||||
|
|
||||
1 | val name' = 20
|
||||
| ^
|
||||
|
||||
Illegal character `'` (escaped: \') at line 1:9"#,
|
||||
);
|
||||
|
||||
assert_eq!(expected_str, err_str,);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_line() {
|
||||
let input = String::from("first line\nsecond line\nthird line");
|
||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||
|
||||
let (result, back_count) = get_line(&chars, 15);
|
||||
|
||||
assert_eq!("second line", result);
|
||||
assert_eq!(4, back_count);
|
||||
|
||||
let input = String::from("val binding = 322");
|
||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||
|
||||
let (result, back_count) = get_line(&chars, 6);
|
||||
|
||||
assert_eq!("val binding = 322", result);
|
||||
assert_eq!(6, back_count);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_get_line_number() {
|
||||
let input = String::from("one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten");
|
||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||
|
||||
let line_number = get_line_number(&chars, 11);
|
||||
assert_eq!(3, line_number);
|
||||
|
||||
let line_number = get_line_number(&chars, 0);
|
||||
assert_eq!(1, line_number);
|
||||
|
||||
let line_number = get_line_number(&chars, 3);
|
||||
assert_eq!(1, line_number);
|
||||
|
||||
let line_number = get_line_number(&chars, 15);
|
||||
assert_eq!(4, line_number);
|
||||
}
|
||||
}
|
@ -1,44 +1,73 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use ariadne::{Label, Report, ReportKind, Source};
|
||||
use serde::Serialize;
|
||||
|
||||
use self::semantic_error::SemanticError;
|
||||
|
||||
mod lex_error;
|
||||
pub mod semantic_error;
|
||||
mod syntax_error;
|
||||
mod utils;
|
||||
|
||||
pub mod error_messages;
|
||||
|
||||
pub trait PrintableError {
|
||||
fn get_error_str(&self, chars: &Vec<char>) -> String;
|
||||
fn print_ariadne(&self, source: &String);
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct ErrorContainer {
|
||||
pub error_code: u32,
|
||||
pub error_offset: usize,
|
||||
pub labels: Vec<ErrorLabel>,
|
||||
pub note: Option<String>,
|
||||
pub help: Option<String>,
|
||||
}
|
||||
|
||||
/// Mirrors ariadne's Label
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct ErrorLabel {
|
||||
pub message: String,
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
}
|
||||
|
||||
pub type MistiError = ErrorContainer;
|
||||
|
||||
/*
|
||||
#[derive(Serialize, Debug)]
|
||||
pub enum MistiError {
|
||||
Lex(LexError),
|
||||
Syntax(SyntaxError),
|
||||
Semantic(SemanticError),
|
||||
Lex(ErrorContainer),
|
||||
Syntax(ErrorContainer),
|
||||
Semantic(ErrorContainer),
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct LexError {
|
||||
pub position: usize,
|
||||
// TODO: Add and end position
|
||||
pub end_position: usize,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct SyntaxError {
|
||||
pub error_start: usize,
|
||||
pub error_end: usize,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
impl PrintableError for MistiError {
|
||||
fn get_error_str(&self, chars: &Vec<char>) -> String {
|
||||
match self {
|
||||
Self::Lex(err) => err.get_error_str(chars),
|
||||
Self::Syntax(err) => err.get_error_str(chars),
|
||||
Self::Semantic(err) => err.get_error_str(chars),
|
||||
impl PrintableError for ErrorContainer {
|
||||
fn get_error_str(&self, _: &Vec<char>) -> String {
|
||||
panic!("REMOVED: manually generating an error message")
|
||||
}
|
||||
|
||||
fn print_ariadne(&self, source: &String) {
|
||||
let mut report: ariadne::ReportBuilder<'_, (&str, Range<usize>)> =
|
||||
Report::build(ReportKind::Error, "sample.thp", self.error_offset);
|
||||
|
||||
for label in self.labels.iter() {
|
||||
let l = Label::new(("sample.thp", label.start..label.end))
|
||||
.with_message(label.message.clone());
|
||||
report = report.with_label(l)
|
||||
}
|
||||
|
||||
if let Some(help) = &self.help {
|
||||
report = report.with_help(help);
|
||||
}
|
||||
|
||||
if let Some(note) = &self.note {
|
||||
report = report.with_help(note);
|
||||
}
|
||||
|
||||
report
|
||||
.with_code(self.error_code)
|
||||
.finish()
|
||||
.eprint(("sample.thp", Source::from(source)))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use ariadne::{Color, Label, Report, ReportKind, Source};
|
||||
use serde::Serialize;
|
||||
|
||||
use super::utils::{get_line, get_line_number};
|
||||
@ -30,4 +31,16 @@ impl PrintableError for SemanticError {
|
||||
{reason} at line {line_number}:{before}"#,
|
||||
)
|
||||
}
|
||||
|
||||
fn print_ariadne(&self, source: &String) {
|
||||
let report = Report::build(ReportKind::Error, "sample.thp", self.error_start)
|
||||
.with_label(
|
||||
Label::new(("sample.thp", self.error_start..self.error_end))
|
||||
.with_message(self.reason.clone())
|
||||
.with_color(Color::Red),
|
||||
)
|
||||
.finish();
|
||||
|
||||
report.eprint(("sample.thp", Source::from(source))).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -1,64 +0,0 @@
|
||||
use super::utils::{get_line, get_line_number};
|
||||
use super::{PrintableError, SyntaxError};
|
||||
|
||||
impl PrintableError for SyntaxError {
|
||||
fn get_error_str(&self, chars: &Vec<char>) -> String {
|
||||
let (line, before, length) = get_line(chars, self.error_start, self.error_end);
|
||||
|
||||
let line_number = get_line_number(chars, self.error_start);
|
||||
let line_number_whitespace = " ".repeat(line_number.to_string().len());
|
||||
|
||||
let whitespace = vec![' '; before].iter().collect::<String>();
|
||||
let indicator = vec!['^'; length].iter().collect::<String>();
|
||||
let reason = &self.reason;
|
||||
|
||||
format!(
|
||||
r#"
|
||||
{line_number_whitespace} |
|
||||
{line_number } | {line}
|
||||
{line_number_whitespace} | {whitespace}{indicator}
|
||||
|
||||
{reason} at line {line_number}:{before}"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{error_handling::MistiError, lexic::get_tokens, syntax::build_ast};
|
||||
|
||||
fn _get_error_data(input: String) -> (Vec<char>, MistiError) {
|
||||
let tokens = get_tokens(&input).unwrap();
|
||||
let error_holder = build_ast(&tokens);
|
||||
|
||||
match error_holder {
|
||||
Ok(_) => panic!(
|
||||
"syntax_error test: Input expected to throw error didn't:\n\n{}",
|
||||
input
|
||||
),
|
||||
Err(error) => {
|
||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||
|
||||
(chars, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_get_line() {
|
||||
let input: Vec<char> = String::from("\n\nval number == 50\n\n")
|
||||
.chars()
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let start_position = 13;
|
||||
let end_position = 15;
|
||||
|
||||
let (line, before, length) = get_line(&input, start_position, end_position);
|
||||
|
||||
assert_eq!("val number == 50", line);
|
||||
assert_eq!(11, before);
|
||||
assert_eq!(2, length);
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ mod utils;
|
||||
|
||||
pub mod token;
|
||||
|
||||
use crate::error_handling::{LexError, MistiError};
|
||||
use crate::error_handling::{ErrorContainer, ErrorLabel, MistiError};
|
||||
use token::Token;
|
||||
|
||||
use self::token::TokenType;
|
||||
@ -36,7 +36,7 @@ pub enum LexResult {
|
||||
/// Contains the last position, which should be the input lenght - 1
|
||||
None(usize),
|
||||
/// An error was found while scanning.
|
||||
Err(LexError),
|
||||
Err(ErrorContainer),
|
||||
}
|
||||
|
||||
/// Scans and returns all the tokens in the input String
|
||||
@ -88,7 +88,7 @@ pub fn get_tokens(input: &String) -> Result<Vec<Token>, MistiError> {
|
||||
current_pos = next_pos;
|
||||
}
|
||||
LexResult::Err(error_info) => {
|
||||
return Err(MistiError::Lex(error_info));
|
||||
return Err(error_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,16 +151,20 @@ fn next_token(
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
let error = LexError {
|
||||
position: current_pos,
|
||||
end_position: current_pos + 1,
|
||||
reason: format!(
|
||||
"Illegal character `{}` (escaped: {})",
|
||||
next_char,
|
||||
next_char.escape_default().to_string(),
|
||||
),
|
||||
let label = ErrorLabel {
|
||||
message: String::from("This character is not allowed"),
|
||||
start: current_pos,
|
||||
end: current_pos + 1,
|
||||
};
|
||||
LexResult::Err(error)
|
||||
let error_container = ErrorContainer {
|
||||
error_offset: current_pos,
|
||||
error_code: 0x010001,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: Some(String::from("Remove this character")),
|
||||
};
|
||||
|
||||
LexResult::Err(error_container)
|
||||
})
|
||||
}
|
||||
|
||||
@ -196,15 +200,15 @@ fn handle_indentation(
|
||||
break;
|
||||
} else {
|
||||
// Illegal state: Indentation error
|
||||
let error = LexError {
|
||||
position: current_pos,
|
||||
end_position: current_pos + 1,
|
||||
reason: format!(
|
||||
"Indentation error: expected {} spaces, found {}",
|
||||
new_top, spaces
|
||||
),
|
||||
let econtaner = ErrorContainer {
|
||||
error_code: 0,
|
||||
error_offset: current_pos,
|
||||
labels: vec![],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return LexResult::Err(error);
|
||||
|
||||
return LexResult::Err(econtaner);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
use super::token::Token;
|
||||
use crate::{
|
||||
error_handling::LexError,
|
||||
error_handling::{
|
||||
error_messages::LEX_INCOMPLETE_MULTILINE_COMMENT, ErrorContainer, ErrorLabel,
|
||||
},
|
||||
lexic::{utils, LexResult},
|
||||
};
|
||||
|
||||
@ -40,12 +42,21 @@ pub fn scan_multiline(chars: &Vec<char>, start_pos: usize) -> LexResult {
|
||||
),
|
||||
Err(last_position) => {
|
||||
// Throw an error: Incomplete multiline comment
|
||||
LexResult::Err(LexError {
|
||||
position: start_pos,
|
||||
// TODO: add an end_position
|
||||
end_position: last_position,
|
||||
reason: "Unfinished multiline commend".into(),
|
||||
})
|
||||
let label = ErrorLabel {
|
||||
message: String::from("The code ends here without closing the multiline comment"),
|
||||
// This is minus 1 so we are pointing at something, and not at EOF
|
||||
start: last_position - 1,
|
||||
end: last_position,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: LEX_INCOMPLETE_MULTILINE_COMMENT,
|
||||
error_offset: last_position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: Some(String::from("End the multiline comment with `*/`")),
|
||||
};
|
||||
|
||||
LexResult::Err(econtainer)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -228,7 +239,7 @@ mod tests {
|
||||
let result = scan_multiline(&input, 0);
|
||||
match result {
|
||||
LexResult::Err(error) => {
|
||||
assert_eq!(0, error.position)
|
||||
assert_eq!(error.error_code, LEX_INCOMPLETE_MULTILINE_COMMENT);
|
||||
}
|
||||
_ => {
|
||||
panic!("Expected an error scannning an incomplete multiline comment")
|
||||
|
@ -1,4 +1,8 @@
|
||||
use crate::error_handling::LexError;
|
||||
use crate::error_handling::error_messages::{
|
||||
LEX_INVALID_BINARY_NUMBER, LEX_INVALID_FLOATING_NUMBER, LEX_INVALID_HEX_NUMBER,
|
||||
LEX_INVALID_OCTAL_NUMBER, LEX_INVALID_SCIENTIFIC_NUMBER,
|
||||
};
|
||||
use crate::error_handling::{ErrorContainer, ErrorLabel};
|
||||
use crate::lexic::{token::Token, utils, LexResult};
|
||||
|
||||
/// Function to scan an int/float
|
||||
@ -57,11 +61,22 @@ fn scan_hex(chars: &[char], start_pos: usize, current: String) -> LexResult {
|
||||
let (t, next) = scan_hex_digits(chars, start_pos + 1, utils::str_append(current, *c));
|
||||
LexResult::Some(t, next)
|
||||
}
|
||||
_ => LexResult::Err(LexError {
|
||||
position: start_pos,
|
||||
end_position: start_pos + 1,
|
||||
reason: String::from("Tried to scan an incomplete hex value"),
|
||||
}),
|
||||
_ => {
|
||||
let label = ErrorLabel {
|
||||
message: String::from("The hex number ends here, without any digit"),
|
||||
start: start_pos,
|
||||
end: start_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: LEX_INVALID_HEX_NUMBER,
|
||||
error_offset: start_pos,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
|
||||
LexResult::Err(econtainer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,12 +97,21 @@ fn scan_octal(chars: &[char], start_pos: usize) -> LexResult {
|
||||
}
|
||||
|
||||
if token_vec.is_empty() {
|
||||
LexResult::Err(LexError {
|
||||
let label = ErrorLabel {
|
||||
message: String::from("The octal number ends here, without any digit"),
|
||||
start: current_pos,
|
||||
end: current_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: LEX_INVALID_OCTAL_NUMBER,
|
||||
// minus 2 to account for the opening '0o'
|
||||
position: start_pos - 2,
|
||||
end_position: current_pos,
|
||||
reason: String::from("Found an incomplete octal number"),
|
||||
})
|
||||
error_offset: current_pos - 2,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
|
||||
LexResult::Err(econtainer)
|
||||
} else {
|
||||
let octal_numbers = format!("0o{}", token_vec.iter().collect::<String>());
|
||||
let new_token = Token::new_int(octal_numbers, start_pos - 2);
|
||||
@ -113,12 +137,21 @@ fn scan_binary(chars: &[char], start_pos: usize) -> LexResult {
|
||||
}
|
||||
|
||||
if token_vec.is_empty() {
|
||||
LexResult::Err(LexError {
|
||||
let label = ErrorLabel {
|
||||
message: String::from("The binary number ends here, without any digit"),
|
||||
start: current_pos,
|
||||
end: current_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: LEX_INVALID_BINARY_NUMBER,
|
||||
// minus 2 to account for the opening '0b'
|
||||
position: start_pos - 2,
|
||||
end_position: current_pos,
|
||||
reason: String::from("Found an incomplete binary number"),
|
||||
})
|
||||
error_offset: current_pos - 2,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
|
||||
LexResult::Err(econtainer)
|
||||
} else {
|
||||
let octal_numbers = format!("0b{}", token_vec.iter().collect::<String>());
|
||||
let new_token = Token::new_int(octal_numbers, start_pos - 2);
|
||||
@ -135,18 +168,45 @@ fn scan_binary(chars: &[char], start_pos: usize) -> LexResult {
|
||||
fn scan_double(chars: &Vec<char>, start_pos: usize, current: String) -> LexResult {
|
||||
match chars.get(start_pos) {
|
||||
Some(c) if utils::is_digit(*c) => scan_double_impl(chars, start_pos, current),
|
||||
Some(_) => LexResult::Err(LexError {
|
||||
position: start_pos,
|
||||
end_position: start_pos + 1,
|
||||
reason: String::from(
|
||||
"The character after the dot when scanning a double is not a number.",
|
||||
Some(_) => {
|
||||
let label = ErrorLabel {
|
||||
message: String::from("The floating number ends here, without any digit"),
|
||||
start: start_pos,
|
||||
end: start_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: LEX_INVALID_FLOATING_NUMBER,
|
||||
// minus 2 to account for the opening '0b'
|
||||
error_offset: start_pos,
|
||||
labels: vec![label],
|
||||
note: Some(String::from(
|
||||
"Floating point numbers must always have at least 1 digit after the period",
|
||||
)),
|
||||
help: None,
|
||||
};
|
||||
|
||||
LexResult::Err(econtainer)
|
||||
}
|
||||
_ => {
|
||||
let label = ErrorLabel {
|
||||
message: String::from(
|
||||
"The code ends here, without completing the floating point number",
|
||||
),
|
||||
}),
|
||||
_ => LexResult::Err(LexError {
|
||||
position: start_pos,
|
||||
end_position: start_pos + 1,
|
||||
reason: String::from("EOF when scanning a double number."),
|
||||
}),
|
||||
start: start_pos,
|
||||
end: start_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: LEX_INVALID_FLOATING_NUMBER,
|
||||
error_offset: start_pos,
|
||||
labels: vec![label],
|
||||
note: Some(String::from(
|
||||
"Floating point numbers must always have at least 1 digit after the period",
|
||||
)),
|
||||
help: None,
|
||||
};
|
||||
|
||||
LexResult::Err(econtainer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,13 +250,24 @@ fn scan_scientific(chars: &Vec<char>, start_pos: usize, current: String) -> LexR
|
||||
let (t, next) = scan_digits(chars, start_pos + 2, new_value);
|
||||
LexResult::Some(t, next)
|
||||
}
|
||||
_ => LexResult::Err(LexError {
|
||||
position: start_pos,
|
||||
end_position: start_pos + 1,
|
||||
reason: String::from(
|
||||
"The characters after 'e' are not + or -, or are not followed by a number",
|
||||
),
|
||||
}),
|
||||
_ => {
|
||||
let label = ErrorLabel {
|
||||
message: String::from("The scientific number ends here, incorrectly"),
|
||||
start: start_pos,
|
||||
end: start_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: LEX_INVALID_SCIENTIFIC_NUMBER,
|
||||
error_offset: start_pos,
|
||||
labels: vec![label],
|
||||
note: Some(String::from(
|
||||
"Scientific numbers must always have a sign (+ or -) and a digit afterwards: `3.22e+2`"
|
||||
)),
|
||||
help: None,
|
||||
};
|
||||
|
||||
LexResult::Err(econtainer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,7 +405,7 @@ mod tests {
|
||||
|
||||
match scan(&input, start_pos) {
|
||||
LexResult::Err(reason) => {
|
||||
assert_eq!("Tried to scan an incomplete hex value", reason.reason)
|
||||
assert_eq!(reason.error_code, LEX_INVALID_HEX_NUMBER)
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
@ -383,9 +454,8 @@ mod tests {
|
||||
let result = scan(&input, 0);
|
||||
match result {
|
||||
LexResult::Err(error) => {
|
||||
assert_eq!(error.position, 0);
|
||||
assert_eq!(error.end_position, 2);
|
||||
assert_eq!(error.reason, "Found an incomplete octal number");
|
||||
assert_eq!(error.error_offset, 0);
|
||||
assert_eq!(error.error_code, LEX_INVALID_OCTAL_NUMBER)
|
||||
}
|
||||
_ => panic!("Expected an error, got {:?}", result),
|
||||
}
|
||||
@ -412,9 +482,8 @@ mod tests {
|
||||
let result = scan(&input, 0);
|
||||
match result {
|
||||
LexResult::Err(error) => {
|
||||
assert_eq!(error.position, 0);
|
||||
assert_eq!(error.end_position, 2);
|
||||
assert_eq!(error.reason, "Found an incomplete binary number");
|
||||
assert_eq!(error.error_offset, 0);
|
||||
assert_eq!(error.error_code, LEX_INVALID_BINARY_NUMBER)
|
||||
}
|
||||
_ => panic!("Expected an error, got {:?}", result),
|
||||
}
|
||||
@ -453,10 +522,7 @@ mod tests {
|
||||
let start_pos = 0;
|
||||
|
||||
match scan(&input, start_pos) {
|
||||
LexResult::Err(reason) => assert_eq!(
|
||||
"The character after the dot when scanning a double is not a number.",
|
||||
reason.reason
|
||||
),
|
||||
LexResult::Err(reason) => assert_eq!(reason.error_code, LEX_INVALID_FLOATING_NUMBER),
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
@ -464,9 +530,7 @@ mod tests {
|
||||
let start_pos = 0;
|
||||
|
||||
match scan(&input, start_pos) {
|
||||
LexResult::Err(reason) => {
|
||||
assert_eq!("EOF when scanning a double number.", reason.reason)
|
||||
}
|
||||
LexResult::Err(reason) => assert_eq!(reason.error_code, LEX_INVALID_FLOATING_NUMBER),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
@ -567,10 +631,7 @@ mod tests {
|
||||
|
||||
match scan(&input, start_pos) {
|
||||
LexResult::Err(reason) => {
|
||||
assert_eq!(
|
||||
"The characters after 'e' are not + or -, or are not followed by a number",
|
||||
reason.reason
|
||||
)
|
||||
assert_eq!(reason.error_code, LEX_INVALID_SCIENTIFIC_NUMBER)
|
||||
}
|
||||
_ => panic!("Expected an error"),
|
||||
}
|
||||
@ -583,10 +644,7 @@ mod tests {
|
||||
|
||||
match scan(&input, start_pos) {
|
||||
LexResult::Err(reason) => {
|
||||
assert_eq!(
|
||||
"The characters after 'e' are not + or -, or are not followed by a number",
|
||||
reason.reason
|
||||
)
|
||||
assert_eq!(reason.error_code, LEX_INVALID_SCIENTIFIC_NUMBER)
|
||||
}
|
||||
_ => panic!("Expected an error"),
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::error_handling::LexError;
|
||||
use crate::error_handling::error_messages::LEX_INCOMPLETE_STRING;
|
||||
use crate::error_handling::{ErrorContainer, ErrorLabel};
|
||||
use crate::lexic::token::Token;
|
||||
use crate::lexic::{utils, LexResult};
|
||||
|
||||
@ -26,11 +27,27 @@ pub fn scan_impl(chars: &Vec<char>, start_pos: usize, current: String) -> LexRes
|
||||
start_pos + 1,
|
||||
)
|
||||
}
|
||||
Some(c) if *c == '\n' => LexResult::Err(LexError {
|
||||
position: start_pos,
|
||||
end_position: start_pos + 1,
|
||||
reason: String::from("Unexpected new line inside a string."),
|
||||
}),
|
||||
Some(c) if *c == '\n' => {
|
||||
let string_start_pos = start_pos - (current.len() + 1);
|
||||
let label_2 = ErrorLabel {
|
||||
message: String::from("The line ends here"),
|
||||
start: start_pos,
|
||||
end: start_pos + 1,
|
||||
};
|
||||
let label_1 = ErrorLabel {
|
||||
message: String::from("The string starts here"),
|
||||
start: string_start_pos,
|
||||
end: string_start_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: LEX_INCOMPLETE_STRING,
|
||||
error_offset: start_pos,
|
||||
labels: vec![label_1, label_2],
|
||||
note: Some(String::from("Strings cannot have newlines")),
|
||||
help: None,
|
||||
};
|
||||
LexResult::Err(econtainer)
|
||||
}
|
||||
Some(c) if *c == '\\' => {
|
||||
if let Some(escape) = test_escape_char(chars, start_pos + 1) {
|
||||
// This should only detect an escaped `"`
|
||||
@ -40,11 +57,28 @@ pub fn scan_impl(chars: &Vec<char>, start_pos: usize, current: String) -> LexRes
|
||||
}
|
||||
}
|
||||
Some(c) => scan_impl(chars, start_pos + 1, utils::str_append(current, *c)),
|
||||
None => LexResult::Err(LexError {
|
||||
position: start_pos,
|
||||
end_position: start_pos + 1,
|
||||
reason: String::from("Incomplete string found"),
|
||||
}),
|
||||
None => {
|
||||
let string_start_pos = start_pos - (current.len() + 1);
|
||||
let label_1 = ErrorLabel {
|
||||
message: String::from("The string starts here"),
|
||||
start: string_start_pos,
|
||||
end: string_start_pos + 1,
|
||||
};
|
||||
let label_2 = ErrorLabel {
|
||||
message: String::from("The code ends here"),
|
||||
start: start_pos,
|
||||
end: start_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: LEX_INCOMPLETE_STRING,
|
||||
error_offset: start_pos,
|
||||
labels: vec![label_1, label_2],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
|
||||
LexResult::Err(econtainer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,8 +141,8 @@ mod tests {
|
||||
fn should_not_scan_a_new_line() {
|
||||
let input = str_to_vec("\"Hello,\nworld!\"");
|
||||
let start_pos = 1;
|
||||
if let LexResult::Err(reason) = scan(&input, start_pos) {
|
||||
assert_eq!("Unexpected new line inside a string.", reason.reason)
|
||||
if let LexResult::Err(err) = scan(&input, start_pos) {
|
||||
assert_eq!(LEX_INCOMPLETE_STRING, err.error_code)
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
@ -204,8 +238,8 @@ mod tests {
|
||||
let result = scan(&input, start_pos);
|
||||
|
||||
match result {
|
||||
LexResult::Err(reason) => {
|
||||
assert_eq!("Incomplete string found", reason.reason)
|
||||
LexResult::Err(err) => {
|
||||
assert_eq!(LEX_INCOMPLETE_STRING, err.error_code)
|
||||
}
|
||||
_ => panic!("expected an error"),
|
||||
}
|
||||
@ -217,8 +251,8 @@ mod tests {
|
||||
let result = scan(&input, 1);
|
||||
|
||||
match result {
|
||||
LexResult::Err(reason) => {
|
||||
assert_eq!("Incomplete string found", reason.reason)
|
||||
LexResult::Err(err) => {
|
||||
assert_eq!(LEX_INCOMPLETE_STRING, err.error_code)
|
||||
}
|
||||
_ => panic!("expected an error"),
|
||||
}
|
||||
|
@ -59,6 +59,6 @@ pub enum PPrimary<'a> {
|
||||
///
|
||||
/// This is a $variable
|
||||
Variable(&'a String),
|
||||
/// This is a symbol, e.g. a function name
|
||||
Symbol(&'a String),
|
||||
// This is a symbol, e.g. a function name
|
||||
// Symbol(&'a String),
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
use colored::Colorize;
|
||||
|
||||
use crate::codegen::Transpilable;
|
||||
use crate::error_handling::PrintableError;
|
||||
|
||||
@ -46,8 +44,7 @@ fn compile(input: &String) {
|
||||
let tokens = match lexic::get_tokens(input) {
|
||||
Ok(t) => t,
|
||||
Err(error) => {
|
||||
let chars: Vec<char> = input.chars().collect();
|
||||
eprintln!("{}", error.get_error_str(&chars));
|
||||
error.print_ariadne(input);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -57,9 +54,8 @@ fn compile(input: &String) {
|
||||
//
|
||||
let ast = match syntax::build_ast(&tokens) {
|
||||
Ok(ast) => ast,
|
||||
Err(reason) => {
|
||||
let chars: Vec<char> = input.chars().collect();
|
||||
eprintln!("{}", reason.get_error_str(&chars));
|
||||
Err(error) => {
|
||||
error.print_ariadne(input);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -70,10 +66,8 @@ fn compile(input: &String) {
|
||||
let res1 = crate::semantic::check_semantics(&ast);
|
||||
match res1 {
|
||||
Ok(_) => {}
|
||||
Err(reason) => {
|
||||
let chars: Vec<char> = input.chars().collect();
|
||||
let error = format!("{}: {}", "error".on_red(), reason.get_error_str(&chars));
|
||||
eprintln!("{}", error);
|
||||
Err(error) => {
|
||||
error.print_ariadne(input);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{error_messages::SEMANTIC_DUPLICATED_REFERENCE, ErrorContainer, ErrorLabel},
|
||||
semantic::{
|
||||
impls::SemanticCheck,
|
||||
types::{Type, Typed},
|
||||
@ -17,16 +17,19 @@ impl SemanticCheck for VariableBinding<'_> {
|
||||
// TODO: Define if variables can be redeclared.
|
||||
// If so, it is irrelevant to check if the variable is already defined
|
||||
if scope.test(binding_name) {
|
||||
let error = SemanticError {
|
||||
error_start: self.identifier.position,
|
||||
error_end: self.identifier.get_end_position(),
|
||||
reason: format!(
|
||||
"Duplicated: A symbol with name {} was already defined",
|
||||
binding_name
|
||||
),
|
||||
let label = ErrorLabel {
|
||||
message: String::from("A reference with this name was already defined"),
|
||||
start: self.identifier.position,
|
||||
end: self.identifier.get_end_position(),
|
||||
};
|
||||
|
||||
return Err(MistiError::Semantic(error));
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_DUPLICATED_REFERENCE,
|
||||
error_offset: self.identifier.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
// This gets the datatype of the assigned expression,
|
||||
@ -41,16 +44,22 @@ impl SemanticCheck for VariableBinding<'_> {
|
||||
|
||||
// Both the declared & actual datatypes must be the same
|
||||
if datatype != expression_datatype {
|
||||
let error = SemanticError {
|
||||
error_start: self.identifier.position,
|
||||
error_end: self.identifier.get_end_position(),
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"The variable `{}` was declared as `{:?}` but its expression has type `{:?}`",
|
||||
binding_name, datatype, expression_datatype
|
||||
),
|
||||
start: self.identifier.position,
|
||||
end: self.identifier.get_end_position(),
|
||||
};
|
||||
|
||||
return Err(MistiError::Semantic(error));
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_DUPLICATED_REFERENCE,
|
||||
error_offset: self.identifier.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
scope.insert(binding_name.clone(), datatype);
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::SEMANTIC_MISMATCHED_TYPES, ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::{
|
||||
impls::SemanticCheck,
|
||||
symbol_table::SymbolTable,
|
||||
@ -17,14 +19,22 @@ impl SemanticCheck for Conditional<'_> {
|
||||
let if_condition_type = if_condition.get_type(scope)?;
|
||||
if !if_condition_type.equals(&bool_type) {
|
||||
let (error_start, error_end) = if_condition.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a condition of type Bool, found {:?}",
|
||||
if_condition_type
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
// Check if block
|
||||
@ -37,11 +47,22 @@ impl SemanticCheck for Conditional<'_> {
|
||||
let condition_type = condition.get_type(scope)?;
|
||||
if !condition_type.equals(&bool_type) {
|
||||
let (error_start, error_end) = condition.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Expected a condition of type Bool, found {:?}", condition_type),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a condition of type Bool, found {:?}",
|
||||
condition_type
|
||||
),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
else_if_member.body.check_semantics(scope)?;
|
||||
|
@ -1,5 +1,11 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::{
|
||||
COMPILER_TODO, SEMANTIC_INVALID_REFERENCE, SEMANTIC_MISMATCHED_ARGUMENT_COUNT,
|
||||
SEMANTIC_MISMATCHED_TYPES,
|
||||
},
|
||||
ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::{
|
||||
impls::SemanticCheck,
|
||||
symbol_table::SymbolTable,
|
||||
@ -22,15 +28,23 @@ impl SemanticCheck for Expression<'_> {
|
||||
if parameters.len() != arguments.len() {
|
||||
let (error_start, error_end) = f.arguments.get_position();
|
||||
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected {} arguments, got {}",
|
||||
parameters.len(),
|
||||
arguments.len(),
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_ARGUMENT_COUNT,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
// Check that each argument matches the required datatype
|
||||
@ -42,14 +56,22 @@ impl SemanticCheck for Expression<'_> {
|
||||
if !argument_datatype.is_value(parameter) {
|
||||
// The argument and the parameter have diferent types
|
||||
let (error_start, error_end) = argument.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a {}, got {:?}",
|
||||
parameter, argument_datatype
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,14 +79,22 @@ impl SemanticCheck for Expression<'_> {
|
||||
}
|
||||
_ => {
|
||||
let (error_start, error_end) = fun.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a function type, got {:?}",
|
||||
function_datatype
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,21 +117,37 @@ impl SemanticCheck for Expression<'_> {
|
||||
} else {
|
||||
// Error: unary negation can only be applied to a Bool
|
||||
let (error_start, error_end) = expression.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Expected a Bool, got a {}", t),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected a Bool, got {}", t),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
}
|
||||
("!", Type::Function(_, _)) => {
|
||||
// Error: unary negation can only be applied to a Bool
|
||||
let (error_start, error_end) = expression.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Expected a Bool, got a function",),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected a Bool, got a function"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
("-", Type::Value(t)) => {
|
||||
if t == "Int" || t == "Float" {
|
||||
@ -110,21 +156,37 @@ impl SemanticCheck for Expression<'_> {
|
||||
} else {
|
||||
// Error: unary negation can only be applied to a Number
|
||||
let (error_start, error_end) = expression.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Expected a Float or Int, got a {}", t),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected a Float or Int, got a {}", t),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
}
|
||||
("-", Type::Function(_, _)) => {
|
||||
// Error: unary negation can only be applied to a Bool
|
||||
let (error_start, error_end) = expression.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Expected a Float or Int, got a function",),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected a Float or Int, got a function"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
(op, _) => {
|
||||
// Compiler error: something that shouldn't be
|
||||
@ -146,11 +208,19 @@ impl SemanticCheck for Expression<'_> {
|
||||
// If the operator is not found its a user error,
|
||||
// because we allow arbitrary operators
|
||||
let (error_start, error_end) = (op.position, op.get_end_position());
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("The binary operator {} does not exist", op.value),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("The binary operator {} does not exist", op.value),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_INVALID_REFERENCE,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
};
|
||||
|
||||
@ -168,26 +238,42 @@ impl SemanticCheck for Expression<'_> {
|
||||
|
||||
if !left_expr_type.is_value(&op_params[0]) {
|
||||
let (error_start, error_end) = left_expr.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a {}, got a {:?} on the left side of the {} operator",
|
||||
op_params[0], left_expr_type, op.value
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
if !right_expr_type.is_value(&op_params[1]) {
|
||||
let (error_start, error_end) = right_expr.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a {}, got a {:?} on the right side of the {} operator",
|
||||
op_params[1], left_expr_type, op.value
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
// After all these checks, we are ok
|
||||
@ -206,13 +292,21 @@ impl SemanticCheck for Expression<'_> {
|
||||
// TODO: if the array is empty then its
|
||||
// datatype should be determined by its usage.
|
||||
if arr.exps.is_empty() {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: arr.start,
|
||||
error_end: arr.end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"An array must have at least 1 element to determine its type. This will be fixed later."
|
||||
),
|
||||
}));
|
||||
start: arr.start,
|
||||
end: arr.end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: COMPILER_TODO,
|
||||
error_offset: arr.start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
let mut expressions = arr.exps.iter();
|
||||
@ -227,20 +321,28 @@ impl SemanticCheck for Expression<'_> {
|
||||
|
||||
// error, found an item with a diferent datatype
|
||||
let (error_start, error_end) = exp.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"All elements of an array must have the same datatype. Expected {:?}, got {:?}",
|
||||
first_type,
|
||||
exp_type,
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: COMPILER_TODO,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::SEMANTIC_MISMATCHED_TYPES, ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::{
|
||||
impls::SemanticCheck,
|
||||
symbol_table::SymbolTable,
|
||||
@ -30,11 +32,19 @@ impl SemanticCheck for ForLoop<'_> {
|
||||
_ => {
|
||||
// error, types other than an Array are not supported
|
||||
let (error_start, error_end) = self.collection.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Only Array[T] are allowed as a for-loop collection."),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Only Arrays are allowed here"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
};
|
||||
let item_type = &item_type[0];
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{error_messages::SEMANTIC_DUPLICATED_REFERENCE, ErrorContainer, ErrorLabel},
|
||||
semantic::{impls::SemanticCheck, symbol_table::SymbolTable, types::Type},
|
||||
syntax::ast::FunctionDeclaration,
|
||||
};
|
||||
@ -13,16 +13,24 @@ impl SemanticCheck for FunctionDeclaration<'_> {
|
||||
|
||||
// Check that the function is not already defined
|
||||
if scope.test(&function_name) {
|
||||
let error = SemanticError {
|
||||
error_start: self.identifier.position,
|
||||
error_end: self.identifier.get_end_position(),
|
||||
reason: format!(
|
||||
"Duplicated: A symbol with name {} was already defined",
|
||||
function_name
|
||||
let (error_start, error_end) =
|
||||
(self.identifier.position, self.identifier.get_end_position());
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"A symbol with name {} was already defined at this scope",
|
||||
function_name,
|
||||
),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
|
||||
return Err(MistiError::Semantic(error));
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_DUPLICATED_REFERENCE,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
// Create a new scope and use it in the function block
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::SEMANTIC_MISMATCHED_TYPES, ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::{
|
||||
impls::SemanticCheck,
|
||||
symbol_table::SymbolTable,
|
||||
@ -16,14 +18,23 @@ impl SemanticCheck for WhileLoop<'_> {
|
||||
|
||||
if !condition_type.equals(&Type::Value("Bool".into())) {
|
||||
let (error_start, error_end) = condition.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a condition of type Bool, found {:?}",
|
||||
condition_type
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: Some(String::from("THP does not have truthy/falsey values.")),
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
// TODO: Define scoping rules for while loops
|
||||
|
@ -1,7 +1,13 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::{
|
||||
COMPILER_TODO, SEMANTIC_INVALID_REFERENCE, SEMANTIC_MISMATCHED_TYPES,
|
||||
SEMANTIC_MISSING_REFERENCE,
|
||||
},
|
||||
ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::symbol_table::SymbolTable,
|
||||
syntax::ast::{Expression, Positionable},
|
||||
syntax::ast::Expression,
|
||||
};
|
||||
|
||||
use super::{Type, Typed};
|
||||
@ -19,11 +25,19 @@ impl Typed for Expression<'_> {
|
||||
let datatype = match scope.get_type(&identifier.value) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: identifier.position,
|
||||
error_end: identifier.get_end_position(),
|
||||
reason: format!("Cannot find `{}` in this scope.", identifier.value),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Cannot find this identifier in this scope"),
|
||||
start: identifier.position,
|
||||
end: identifier.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISSING_REFERENCE,
|
||||
error_offset: identifier.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
};
|
||||
|
||||
@ -43,20 +57,44 @@ impl Typed for Expression<'_> {
|
||||
// not the function itself
|
||||
Ok(Type::Value(return_type))
|
||||
}
|
||||
Some(_) => Err(MistiError::Semantic(SemanticError {
|
||||
error_start: id.position,
|
||||
error_end: id.get_end_position(),
|
||||
reason: format!("Expected `{}` to be a function", &id.value),
|
||||
})),
|
||||
None => Err(MistiError::Semantic(SemanticError {
|
||||
error_start: id.position,
|
||||
error_end: id.get_end_position(),
|
||||
reason: format!("Cannot find `{}` in this scope.", id.value),
|
||||
})),
|
||||
Some(_) => {
|
||||
let label = ErrorLabel {
|
||||
message: String::from(
|
||||
"Expected this identifier to be a function",
|
||||
),
|
||||
start: id.position,
|
||||
end: id.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_INVALID_REFERENCE,
|
||||
error_offset: id.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
None => {
|
||||
let label = ErrorLabel {
|
||||
message: String::from(
|
||||
"Cannot find this identifier in this scope",
|
||||
),
|
||||
start: id.position,
|
||||
end: id.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_INVALID_REFERENCE,
|
||||
error_offset: id.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(
|
||||
"Get datatype of an expression that resolves into a function call"
|
||||
"Get datatype of an expression that may resolve into a function call"
|
||||
),
|
||||
}
|
||||
}
|
||||
@ -64,35 +102,59 @@ impl Typed for Expression<'_> {
|
||||
let expr_type = match exp.get_type(scope) {
|
||||
Ok(t) => t,
|
||||
Err(_reason) => {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: 0,
|
||||
error_end: 1,
|
||||
reason: format!("Error getting type of expression"),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Error getting type of this expression"),
|
||||
// TODO: Fix these positions
|
||||
start: 0,
|
||||
end: 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: COMPILER_TODO,
|
||||
error_offset: 0,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
};
|
||||
|
||||
// Only supported unary operator: - & !
|
||||
if op.value == "-" {
|
||||
if !expr_type.is_value("Int") && !expr_type.is_value("Float") {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: 0,
|
||||
error_end: 1,
|
||||
reason: format!(
|
||||
"Expected a Int or Float after unary `-`, got {:?}",
|
||||
expr_type
|
||||
),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected an `Int` or `Float`, got {:?}", expr_type),
|
||||
// TODO: Fix positioning
|
||||
start: 0,
|
||||
end: 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: 0,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
} else {
|
||||
return Ok(Type::Value("Int".into()));
|
||||
}
|
||||
} else if op.value == "!" {
|
||||
if !expr_type.is_value("Bool") {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: 0,
|
||||
error_end: 1,
|
||||
reason: format!("Expected a Bool after unary `!`, got {:?}", expr_type),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected a `Bool`, got {:?}", expr_type),
|
||||
// TODO: Fix positioning
|
||||
start: 0,
|
||||
end: 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: 0,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
} else {
|
||||
return Ok(Type::Value("Bool".into()));
|
||||
}
|
||||
@ -105,20 +167,27 @@ impl Typed for Expression<'_> {
|
||||
let t2 = exp2.get_type(scope)?;
|
||||
|
||||
// TODO: There's definitely a better way to do this
|
||||
// maybe store operators as functions?
|
||||
if operator.value == "+" && t1.is_value("Int") && t2.is_value("Int") {
|
||||
return Ok(Type::Value("Int".into()));
|
||||
} else if operator.value == "-" && t1.is_value("Int") && t2.is_value("Int") {
|
||||
return Ok(Type::Value("Int".into()));
|
||||
}
|
||||
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
// TODO: fix positions
|
||||
error_start: 0,
|
||||
error_end: 1,
|
||||
reason: format!(
|
||||
"Unsupported binary operator or invalid arguments to the operator."
|
||||
),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Unsupported binary operator"),
|
||||
// TODO: Fix positioning
|
||||
start: 0,
|
||||
end: 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: 0,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
Expression::Array(arr) => {
|
||||
// The first expression found determines the
|
||||
@ -129,13 +198,20 @@ impl Typed for Expression<'_> {
|
||||
// TODO: if the array is empty then its
|
||||
// datatype should be determined by its usage.
|
||||
if arr.exps.is_empty() {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: arr.start,
|
||||
error_end: arr.end,
|
||||
reason: format!(
|
||||
"An array must have at least 1 element to determine its type. This will be fixed later."
|
||||
),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Compiler limit: Arrays must have at least 1 element to determine their type"),
|
||||
// TODO: Fix positioning
|
||||
start: arr.start,
|
||||
end: arr.end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: 0,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(econtainer);
|
||||
}
|
||||
|
||||
// Just get the first type and use it
|
||||
|
@ -83,6 +83,14 @@ pub enum BlockMember<'a> {
|
||||
#[derive(Debug)]
|
||||
pub struct ParamsList<'a> {
|
||||
pub parameters: Vec<Parameter<'a>>,
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
}
|
||||
|
||||
impl Positionable for ParamsList<'_> {
|
||||
fn get_position(&self) -> (usize, usize) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
error_handling::{error_messages::SYNTAX_INCOMPLETE_ARGUMENT_LIST, ErrorContainer, ErrorLabel},
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{
|
||||
ast::{functions::ArgumentsList, Expression},
|
||||
@ -58,18 +58,44 @@ pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<ArgumentsList
|
||||
Ok((t, next)) => (t, next),
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a closing paren after the function identifier."),
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
}));
|
||||
let label_1 = ErrorLabel {
|
||||
message: String::from("The argument list starts here"),
|
||||
start: opening_paren.position,
|
||||
end: opening_paren.get_end_position(),
|
||||
};
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a closing paren `)` here"),
|
||||
start: t.position,
|
||||
end: t.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INCOMPLETE_ARGUMENT_LIST,
|
||||
error_offset: t.position,
|
||||
labels: vec![label_1, label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a closing paren after the function identifier."),
|
||||
error_start: opening_paren.position,
|
||||
error_end: opening_paren.get_end_position(),
|
||||
}));
|
||||
let label_1 = ErrorLabel {
|
||||
message: String::from("The argument list starts here"),
|
||||
start: opening_paren.position,
|
||||
end: opening_paren.get_end_position(),
|
||||
};
|
||||
let label_2 = ErrorLabel {
|
||||
message: String::from("The code ends here without closing the argument list"),
|
||||
start: current_pos,
|
||||
end: current_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INCOMPLETE_ARGUMENT_LIST,
|
||||
error_offset: current_pos,
|
||||
labels: vec![label_1, label_2],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
current_pos = next_pos;
|
||||
|
@ -1,5 +1,8 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
error_handling::{
|
||||
error_messages::{SYNTAX_INCOMPLETE_PARAMETER_LIST, SYNTAX_INVALID_PARAMETER_DECLARATION},
|
||||
ErrorContainer, ErrorLabel,
|
||||
},
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{utils::parse_token_type, ParsingError, ParsingResult},
|
||||
};
|
||||
@ -60,28 +63,61 @@ pub fn parse_params_list(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Param
|
||||
}
|
||||
|
||||
// Parse closing paren
|
||||
let (_closing_paren, next_pos) =
|
||||
let (closing_paren, next_pos) =
|
||||
match parse_token_type(tokens, current_pos, TokenType::RightParen) {
|
||||
Ok((t, next)) => (t, next),
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a closing paren after the function identifier."),
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
}));
|
||||
let label_1 = ErrorLabel {
|
||||
message: String::from("The parameter list starts here"),
|
||||
start: opening_paren.position,
|
||||
end: opening_paren.get_end_position(),
|
||||
};
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a closing paren `)` here"),
|
||||
start: t.position,
|
||||
end: t.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INCOMPLETE_PARAMETER_LIST,
|
||||
error_offset: t.position,
|
||||
labels: vec![label_1, label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a closing paren after the function identifier."),
|
||||
error_start: opening_paren.position,
|
||||
error_end: opening_paren.get_end_position(),
|
||||
}));
|
||||
let label_1 = ErrorLabel {
|
||||
message: String::from("The parameter list starts here"),
|
||||
start: opening_paren.position,
|
||||
end: opening_paren.get_end_position(),
|
||||
};
|
||||
let label_2 = ErrorLabel {
|
||||
message: String::from("The code ends here without closing the parameter list"),
|
||||
start: current_pos,
|
||||
end: current_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INCOMPLETE_PARAMETER_LIST,
|
||||
error_offset: current_pos,
|
||||
labels: vec![label_1, label_2],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
current_pos = next_pos;
|
||||
|
||||
Ok((ParamsList { parameters }, current_pos))
|
||||
Ok((
|
||||
ParamsList {
|
||||
parameters,
|
||||
start: opening_paren.position,
|
||||
end: closing_paren.get_end_position(),
|
||||
},
|
||||
current_pos,
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse a single parameter definition of the form:
|
||||
@ -112,19 +148,36 @@ fn parse_param_definition(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Para
|
||||
return Err(ParsingError::Err(err));
|
||||
}
|
||||
// However, if we fail to parse an identifier, it's an error
|
||||
Err(ParsingError::Mismatch(_)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected an identifier for the parameter."),
|
||||
error_start: tokens[pos].position,
|
||||
error_end: tokens[pos].get_end_position(),
|
||||
}));
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an identifier here, found this instead"),
|
||||
start: t.position,
|
||||
end: t.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_PARAMETER_DECLARATION,
|
||||
error_offset: t.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected an identifier for the parameter."),
|
||||
error_start: tokens[pos].position,
|
||||
error_end: tokens[pos].get_end_position(),
|
||||
}))
|
||||
let datatype_token = &tokens[pos];
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an identifier after this datatype"),
|
||||
start: datatype_token.position,
|
||||
end: datatype_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_PARAMETER_DECLARATION,
|
||||
error_offset: datatype_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -16,7 +16,7 @@ use self::parseable::{Parseable, ParsingError, ParsingResult};
|
||||
pub fn build_ast<'a>(tokens: &'a Vec<Token>) -> Result<ModuleAST, MistiError> {
|
||||
match ModuleAST::try_parse(tokens, 0) {
|
||||
Ok((module, _)) => Ok(module),
|
||||
Err(ParsingError::Err(error)) => Err(MistiError::Syntax(error)),
|
||||
Err(ParsingError::Err(error)) => Err(error),
|
||||
_ => {
|
||||
// This shouldn't happen. The module parser returns an error if it finds nothing to parse.
|
||||
unreachable!("Illegal state during parsing: The Module parse should always return a result or error")
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{error_handling::SyntaxError, lexic::token::Token};
|
||||
use crate::{error_handling::ErrorContainer, lexic::token::Token};
|
||||
|
||||
/// The result of a parsing operation.
|
||||
/// On success, it contains the item and the position of the next token
|
||||
@ -15,7 +15,7 @@ pub enum ParsingError<'a> {
|
||||
///
|
||||
/// For example, when parsing a function declaration
|
||||
/// the `fun` token is found, but then no identifier
|
||||
Err(SyntaxError),
|
||||
Err(ErrorContainer),
|
||||
}
|
||||
|
||||
/// Represents a type that can be parsed using Recursive Descent
|
||||
|
@ -1,5 +1,8 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
error_handling::{
|
||||
error_messages::{SYNTAX_INCOMPLETE_STATEMENT, SYNTAX_INVALID_VARIABLE_DECLARATION},
|
||||
ErrorContainer, ErrorLabel,
|
||||
},
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{
|
||||
ast::{var_binding::VariableBinding, Expression},
|
||||
@ -52,31 +55,57 @@ impl<'a> Parseable<'a> for VariableBinding<'a> {
|
||||
Ok((t, n)) => (t, n),
|
||||
Err(ParsingError::Mismatch(token)) => {
|
||||
// The parser found a token, but it's not an identifier
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: token.position,
|
||||
error_end: token.get_end_position(),
|
||||
reason: "There should be an identifier after a binding".into(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an identifier here"),
|
||||
start: token.position,
|
||||
end: token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_VARIABLE_DECLARATION,
|
||||
error_offset: token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
_ => {
|
||||
// The parser didn't find an Identifier after VAL/VAR or the Datatype
|
||||
match (binding_token, datatype) {
|
||||
(Some(binding_token), None) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: format!(
|
||||
"There should be an identifier after a `{}` token",
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"There should be an identifier after this `{}` token",
|
||||
if is_var { "var" } else { "val" }
|
||||
),
|
||||
error_start: binding_token.position,
|
||||
error_end: binding_token.get_end_position(),
|
||||
}));
|
||||
start: binding_token.position,
|
||||
end: binding_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_VARIABLE_DECLARATION,
|
||||
error_offset: binding_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
(_, Some(datatype_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: "There should be an identifier after the datatype".into(),
|
||||
error_start: datatype_token.position,
|
||||
error_end: datatype_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from(
|
||||
"There should be an identifier after this datatype",
|
||||
),
|
||||
start: datatype_token.position,
|
||||
end: datatype_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_VARIABLE_DECLARATION,
|
||||
error_offset: datatype_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
_ => {
|
||||
unreachable!(
|
||||
@ -94,19 +123,37 @@ impl<'a> Parseable<'a> for VariableBinding<'a> {
|
||||
Ok((t, _)) => t,
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
// The parser found a token, but it's not the `=` operator
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: format!("There should be an equal sign `=` after the identifier"),
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from(
|
||||
"Expected an equal sign `=` here, after the variable identifier",
|
||||
),
|
||||
start: t.position,
|
||||
end: t.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_VARIABLE_DECLARATION,
|
||||
error_offset: t.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
_ => {
|
||||
// The parser didn't find the `=` operator after the identifier
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: format!("There should be an equal sign `=` after the identifier",),
|
||||
error_start: identifier.position,
|
||||
error_end: identifier.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an equal sign `=` after this identifier"),
|
||||
start: identifier.position,
|
||||
end: identifier.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_VARIABLE_DECLARATION,
|
||||
error_offset: identifier.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
let next_pos = next_pos + 1;
|
||||
@ -117,11 +164,19 @@ impl<'a> Parseable<'a> for VariableBinding<'a> {
|
||||
let (expression, next_pos) = match Expression::try_parse(tokens, next_pos) {
|
||||
Ok((exp, next)) => (exp, next),
|
||||
_ => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected an expression after the equal `=` operator"),
|
||||
error_start: equal_operator.position,
|
||||
error_end: equal_operator.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an expression after this equal `=` operator"),
|
||||
start: equal_operator.position,
|
||||
end: equal_operator.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_VARIABLE_DECLARATION,
|
||||
error_offset: equal_operator.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -130,11 +185,19 @@ impl<'a> Parseable<'a> for VariableBinding<'a> {
|
||||
let next_pos = match parse_terminator(tokens, next_pos) {
|
||||
Ok((_, next)) => next,
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
reason: format!("Unexpected token `{}`, expected a new line", t.value),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a new line here, found another token"),
|
||||
start: t.position,
|
||||
end: t.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INCOMPLETE_STATEMENT,
|
||||
error_offset: t.position,
|
||||
labels: vec![label],
|
||||
note: Some(String::from("There may only be one statement per line")),
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@ -229,8 +292,7 @@ mod tests {
|
||||
|
||||
match binding {
|
||||
Err(ParsingError::Err(error)) => {
|
||||
assert_eq!(4, error.error_start);
|
||||
assert_eq!(6, error.error_end);
|
||||
assert_eq!(4, error.error_offset);
|
||||
}
|
||||
_ => panic!("Error expected"),
|
||||
}
|
||||
@ -245,8 +307,7 @@ mod tests {
|
||||
|
||||
match binding {
|
||||
Err(ParsingError::Err(error)) => {
|
||||
assert_eq!(0, error.error_start);
|
||||
assert_eq!(3, error.error_end);
|
||||
assert_eq!(0, error.error_offset);
|
||||
}
|
||||
_ => panic!("Error expected"),
|
||||
}
|
||||
@ -261,8 +322,7 @@ mod tests {
|
||||
|
||||
match binding {
|
||||
Err(ParsingError::Err(error)) => {
|
||||
assert_eq!(4, error.error_start);
|
||||
assert_eq!(7, error.error_end);
|
||||
assert_eq!(4, error.error_offset);
|
||||
}
|
||||
_ => panic!("Error expected"),
|
||||
}
|
||||
@ -272,12 +332,8 @@ mod tests {
|
||||
|
||||
match binding {
|
||||
Err(ParsingError::Err(error)) => {
|
||||
assert_eq!(4, error.error_start);
|
||||
assert_eq!(11, error.error_end);
|
||||
assert_eq!(
|
||||
"There should be an identifier after a binding",
|
||||
error.reason
|
||||
);
|
||||
assert_eq!(4, error.error_offset);
|
||||
assert_eq!(error.error_code, SYNTAX_INVALID_VARIABLE_DECLARATION)
|
||||
}
|
||||
_ => panic!("Error expected"),
|
||||
}
|
||||
@ -290,8 +346,7 @@ mod tests {
|
||||
|
||||
match binding {
|
||||
Err(ParsingError::Err(error)) => {
|
||||
assert_eq!(7, error.error_start);
|
||||
assert_eq!(14, error.error_end);
|
||||
assert_eq!(7, error.error_offset);
|
||||
}
|
||||
_ => panic!("Error expected"),
|
||||
}
|
||||
@ -304,12 +359,8 @@ mod tests {
|
||||
|
||||
match binding {
|
||||
Err(ParsingError::Err(error)) => {
|
||||
assert_eq!(4, error.error_start);
|
||||
assert_eq!(10, error.error_end);
|
||||
assert_eq!(
|
||||
"There should be an identifier after the datatype",
|
||||
error.reason
|
||||
);
|
||||
assert_eq!(4, error.error_offset);
|
||||
assert_eq!(error.error_code, SYNTAX_INVALID_VARIABLE_DECLARATION)
|
||||
}
|
||||
_ => panic!("Error expected"),
|
||||
}
|
||||
@ -322,12 +373,8 @@ mod tests {
|
||||
|
||||
match binding {
|
||||
Err(ParsingError::Err(error)) => {
|
||||
assert_eq!(0, error.error_start);
|
||||
assert_eq!(3, error.error_end);
|
||||
assert_eq!(
|
||||
"There should be an identifier after a `val` token",
|
||||
error.reason
|
||||
);
|
||||
assert_eq!(0, error.error_offset);
|
||||
assert_eq!(error.error_code, SYNTAX_INVALID_VARIABLE_DECLARATION)
|
||||
}
|
||||
_ => panic!("Error expected"),
|
||||
}
|
||||
@ -340,12 +387,8 @@ mod tests {
|
||||
|
||||
match binding {
|
||||
Err(ParsingError::Err(error)) => {
|
||||
assert_eq!(4, error.error_start);
|
||||
assert_eq!(14, error.error_end);
|
||||
assert_eq!(
|
||||
"There should be an equal sign `=` after the identifier",
|
||||
error.reason
|
||||
);
|
||||
assert_eq!(4, error.error_offset);
|
||||
assert_eq!(error.error_code, SYNTAX_INVALID_VARIABLE_DECLARATION)
|
||||
}
|
||||
_ => panic!("Error expected"),
|
||||
}
|
||||
@ -358,12 +401,8 @@ mod tests {
|
||||
|
||||
match binding {
|
||||
Err(ParsingError::Err(error)) => {
|
||||
assert_eq!(15, error.error_start);
|
||||
assert_eq!(16, error.error_end);
|
||||
assert_eq!(
|
||||
"Expected an expression after the equal `=` operator",
|
||||
error.reason
|
||||
);
|
||||
assert_eq!(15, error.error_offset);
|
||||
assert_eq!(error.error_code, SYNTAX_INVALID_VARIABLE_DECLARATION)
|
||||
}
|
||||
_ => panic!("Error expected"),
|
||||
}
|
||||
@ -376,12 +415,8 @@ mod tests {
|
||||
|
||||
match binding {
|
||||
Err(ParsingError::Err(error)) => {
|
||||
assert_eq!(21, error.error_start);
|
||||
assert_eq!(26, error.error_end);
|
||||
assert_eq!(
|
||||
"Unexpected token `print`, expected a new line",
|
||||
error.reason
|
||||
);
|
||||
assert_eq!(21, error.error_offset);
|
||||
assert_eq!(error.error_code, SYNTAX_INCOMPLETE_STATEMENT)
|
||||
}
|
||||
_ => panic!("Error expected"),
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
error_handling::{error_messages::SYNTAX_INCOMPLETE_BLOCK, ErrorContainer, ErrorLabel},
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{
|
||||
ast::{Block, BlockMember, Expression, Statement},
|
||||
@ -65,20 +65,34 @@ impl<'a> Parseable<'a> for Block<'a> {
|
||||
Ok((t, next)) => (t, next),
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a closing brace after the block body."),
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a closing brace `}` here"),
|
||||
start: t.position,
|
||||
end: t.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INCOMPLETE_BLOCK,
|
||||
error_offset: t.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a closing brace after the block body."),
|
||||
// TODO: use the last token (at pos current_pos) as guide for the error
|
||||
// msg position
|
||||
error_start: opening_brace.position,
|
||||
error_end: opening_brace.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a closing brace `}` here"),
|
||||
start: current_pos,
|
||||
end: current_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INCOMPLETE_BLOCK,
|
||||
error_offset: current_pos,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
current_pos = next_pos;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
error_handling::{error_messages::SYNTAX_INVALID_IF_CONDITION, ErrorContainer, ErrorLabel},
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{
|
||||
ast::{Block, Condition, Conditional, Expression, Positionable},
|
||||
@ -23,18 +23,34 @@ impl<'a> Parseable<'a> for Conditional<'a> {
|
||||
Ok(tuple) => tuple,
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(wrong_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected an expression after the if token"),
|
||||
error_start: wrong_token.position,
|
||||
error_end: wrong_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a Bool expression here"),
|
||||
start: wrong_token.position,
|
||||
end: wrong_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_IF_CONDITION,
|
||||
error_offset: wrong_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected an expression after the if token"),
|
||||
error_start: if_token.position,
|
||||
error_end: if_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a Bool expression after this `if` keyword"),
|
||||
start: if_token.position,
|
||||
end: if_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_IF_CONDITION,
|
||||
error_offset: if_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -43,19 +59,35 @@ impl<'a> Parseable<'a> for Conditional<'a> {
|
||||
Ok(t) => t,
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(wrong_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a block after the condition"),
|
||||
error_start: wrong_token.position,
|
||||
error_end: wrong_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block here, after the condition"),
|
||||
start: wrong_token.position,
|
||||
end: wrong_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_IF_CONDITION,
|
||||
error_offset: wrong_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
let (error_start, error_end) = if_expression.get_position();
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a block after the condition"),
|
||||
error_start,
|
||||
error_end,
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block after this condition"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_IF_CONDITION,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -87,18 +119,34 @@ impl<'a> Parseable<'a> for Conditional<'a> {
|
||||
Ok(tuple) => tuple,
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(wrong_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected an expression after the if token"),
|
||||
error_start: wrong_token.position,
|
||||
error_end: wrong_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a Bool expression here"),
|
||||
start: wrong_token.position,
|
||||
end: wrong_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_IF_CONDITION,
|
||||
error_offset: wrong_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected an expression after the if token"),
|
||||
error_start: if_token.position,
|
||||
error_end: if_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a Bool expression after this `if` keyword"),
|
||||
start: if_token.position,
|
||||
end: if_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_IF_CONDITION,
|
||||
error_offset: if_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -107,19 +155,35 @@ impl<'a> Parseable<'a> for Conditional<'a> {
|
||||
Ok(t) => t,
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(wrong_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a block after the condition"),
|
||||
error_start: wrong_token.position,
|
||||
error_end: wrong_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block here, after the condition"),
|
||||
start: wrong_token.position,
|
||||
end: wrong_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_IF_CONDITION,
|
||||
error_offset: wrong_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
let (error_start, error_end) = condition.get_position();
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a block after the condition"),
|
||||
error_start,
|
||||
error_end,
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block after this condition"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_IF_CONDITION,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -140,18 +204,36 @@ impl<'a> Parseable<'a> for Conditional<'a> {
|
||||
Ok(t) => t,
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(wrong_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a block after the else keyword"),
|
||||
error_start: wrong_token.position,
|
||||
error_end: wrong_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block here, after the condition"),
|
||||
start: wrong_token.position,
|
||||
end: wrong_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_IF_CONDITION,
|
||||
error_offset: wrong_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a block after the else keyword"),
|
||||
error_start: else_token.position,
|
||||
error_end: else_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from(
|
||||
"Expected a block here, after this `else` keyword",
|
||||
),
|
||||
start: else_token.position,
|
||||
end: else_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_IF_CONDITION,
|
||||
error_offset: else_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
error_handling::{error_messages::SYNTAX_INVALID_FOR_LOOP, ErrorContainer, ErrorLabel},
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{
|
||||
ast::{loops::ForLoop, Block, Expression, Positionable},
|
||||
@ -23,21 +23,34 @@ impl<'a> Parseable<'a> for ForLoop<'a> {
|
||||
Ok(t) => t,
|
||||
Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)),
|
||||
Err(ParsingError::Mismatch(e)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: e.position,
|
||||
error_end: e.get_end_position(),
|
||||
reason: format!(
|
||||
"Expected an identifier after the `for` keyword, found {}",
|
||||
e.value
|
||||
),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an identifier here, after the `for` keyword"),
|
||||
start: e.position,
|
||||
end: e.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FOR_LOOP,
|
||||
error_offset: e.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: for_keyword.position,
|
||||
error_end: for_keyword.get_end_position(),
|
||||
reason: format!("Expected an identifier after the `for` keyword"),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an identifier after this `for` keyword"),
|
||||
start: for_keyword.position,
|
||||
end: for_keyword.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FOR_LOOP,
|
||||
error_offset: for_keyword.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -55,21 +68,38 @@ impl<'a> Parseable<'a> for ForLoop<'a> {
|
||||
Ok((second_id, next)) => (Some(second_id), next),
|
||||
Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)),
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
reason: format!(
|
||||
"Expected an identifier after the comma, found `{}`",
|
||||
t.value
|
||||
),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an identifier here, after the comma"),
|
||||
start: t.position,
|
||||
end: t.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FOR_LOOP,
|
||||
error_offset: t.position,
|
||||
labels: vec![label],
|
||||
note: Some(String::from(
|
||||
"To iterate only over values, use `for identifier in ...`",
|
||||
)),
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: comma.position,
|
||||
error_end: comma.get_end_position(),
|
||||
reason: format!("Expected an identifier after the comma"),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an identifier after this comma"),
|
||||
start: comma.position,
|
||||
end: comma.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FOR_LOOP,
|
||||
error_offset: comma.position,
|
||||
labels: vec![label],
|
||||
note: Some(String::from(
|
||||
"To iterate only over values, use `for identifier in ...`",
|
||||
)),
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -79,11 +109,19 @@ impl<'a> Parseable<'a> for ForLoop<'a> {
|
||||
Ok(tuple) => tuple,
|
||||
Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)),
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
reason: format!("Expected the `in` keyword, found `{}`", t.value),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected the `in` keyword here"),
|
||||
start: t.position,
|
||||
end: t.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FOR_LOOP,
|
||||
error_offset: t.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
let previous_token = if second_id.is_none() {
|
||||
@ -91,11 +129,19 @@ impl<'a> Parseable<'a> for ForLoop<'a> {
|
||||
} else {
|
||||
second_id.unwrap()
|
||||
};
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: previous_token.position,
|
||||
error_end: previous_token.get_end_position(),
|
||||
reason: format!("Expected the `in` keyword"),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected the `in` keyword after this identifier"),
|
||||
start: previous_token.position,
|
||||
end: previous_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FOR_LOOP,
|
||||
error_offset: previous_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -104,11 +150,19 @@ impl<'a> Parseable<'a> for ForLoop<'a> {
|
||||
Ok(t) => t,
|
||||
Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)),
|
||||
Err(_) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: in_keyword.position,
|
||||
error_end: in_keyword.get_end_position(),
|
||||
reason: format!("Expected an expression after the `in` keyword"),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an expression after this `in` keyword"),
|
||||
start: in_keyword.position,
|
||||
end: in_keyword.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FOR_LOOP,
|
||||
error_offset: in_keyword.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -117,19 +171,35 @@ impl<'a> Parseable<'a> for ForLoop<'a> {
|
||||
Ok(t) => t,
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(wrong_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a block after the collection"),
|
||||
error_start: wrong_token.position,
|
||||
error_end: wrong_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block here"),
|
||||
start: wrong_token.position,
|
||||
end: wrong_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FOR_LOOP,
|
||||
error_offset: wrong_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
let (error_start, error_end) = expr.get_position();
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a block after the collection"),
|
||||
error_start,
|
||||
error_end,
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block here, after the collection"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FOR_LOOP,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
error_handling::{
|
||||
error_messages::SYNTAX_INVALID_FUNCTION_DECLARATION, ErrorContainer, ErrorLabel,
|
||||
},
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{
|
||||
ast::{Block, FunctionDeclaration},
|
||||
ast::{Block, FunctionDeclaration, Positionable},
|
||||
functions::params_list::parse_params_list,
|
||||
parseable::{Parseable, ParsingError, ParsingResult},
|
||||
utils::{parse_token_type, try_operator},
|
||||
@ -28,18 +30,34 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> {
|
||||
Ok((id, next)) => (id, next),
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(wrong_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected an identifier after the `fun` keyword."),
|
||||
error_start: wrong_token.position,
|
||||
error_end: wrong_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an identifier here"),
|
||||
start: wrong_token.position,
|
||||
end: wrong_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FUNCTION_DECLARATION,
|
||||
error_offset: wrong_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected an identifier after the `fun` keyword."),
|
||||
error_start: fun_keyword.position,
|
||||
error_end: fun_keyword.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected an identifier after this `fun` keyword"),
|
||||
start: fun_keyword.position,
|
||||
end: fun_keyword.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FUNCTION_DECLARATION,
|
||||
error_offset: fun_keyword.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
current_pos = next_pos;
|
||||
@ -50,22 +68,38 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> {
|
||||
Ok((params, next_pos)) => (params, next_pos),
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(wrong_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from(
|
||||
"Expected an opening paren after the function identifier.",
|
||||
),
|
||||
error_start: wrong_token.position,
|
||||
error_end: wrong_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a parameter list here"),
|
||||
start: wrong_token.position,
|
||||
end: wrong_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FUNCTION_DECLARATION,
|
||||
error_offset: wrong_token.get_end_position(),
|
||||
labels: vec![label],
|
||||
note: Some(String::from(
|
||||
"If this function doesn't take any parameter, use an empty list `()`",
|
||||
)),
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from(
|
||||
"Expected an opening paren after the function identifier.",
|
||||
),
|
||||
error_start: identifier.position,
|
||||
error_end: identifier.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a parameter list after this identifier"),
|
||||
start: identifier.position,
|
||||
end: identifier.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FUNCTION_DECLARATION,
|
||||
error_offset: identifier.get_end_position(),
|
||||
labels: vec![label],
|
||||
note: Some(String::from(
|
||||
"If this function doesn't take any parameter, use an empty list `()`",
|
||||
)),
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
current_pos = next_pos;
|
||||
@ -83,18 +117,38 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> {
|
||||
Ok((t, next)) => (Some(t), next),
|
||||
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
|
||||
Err(ParsingError::Mismatch(wrong_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a datatype after the arrow operator."),
|
||||
error_start: wrong_token.position,
|
||||
error_end: wrong_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a Datatype here"),
|
||||
start: wrong_token.position,
|
||||
end: wrong_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FUNCTION_DECLARATION,
|
||||
error_offset: wrong_token.position,
|
||||
labels: vec![label],
|
||||
note: Some(String::from(
|
||||
"If you want a function without a return type, omit the arrow as well",
|
||||
)),
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a datatype after the arrow operator."),
|
||||
error_start: arrow_op.position,
|
||||
error_end: arrow_op.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a Datatype after this arrow `->` operator"),
|
||||
start: arrow_op.position,
|
||||
end: arrow_op.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FUNCTION_DECLARATION,
|
||||
error_offset: arrow_op.position,
|
||||
labels: vec![label],
|
||||
note: Some(String::from(
|
||||
"If you want a function without a return type, omit the arrow as well",
|
||||
)),
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -107,18 +161,41 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> {
|
||||
return Err(ParsingError::Err(error));
|
||||
}
|
||||
Err(ParsingError::Mismatch(wrong_token)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a block after the function declaration."),
|
||||
error_start: wrong_token.position,
|
||||
error_end: wrong_token.get_end_position(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block here, after the function declaration"),
|
||||
start: wrong_token.position,
|
||||
end: wrong_token.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FUNCTION_DECLARATION,
|
||||
error_offset: wrong_token.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
reason: String::from("Expected a block after the function declaration."),
|
||||
error_start: identifier.position,
|
||||
error_end: identifier.get_end_position(),
|
||||
}));
|
||||
let (error_start, error_end) = {
|
||||
if let Some(return_type) = return_type {
|
||||
(return_type.position, return_type.get_end_position())
|
||||
} else {
|
||||
params_list.get_position()
|
||||
}
|
||||
};
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block here, after the function declaration"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_FUNCTION_DECLARATION,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
current_pos = next_pos;
|
||||
@ -159,12 +236,8 @@ mod tests {
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(
|
||||
err.reason,
|
||||
"Expected an identifier after the `fun` keyword."
|
||||
);
|
||||
assert_eq!(err.error_start, 4);
|
||||
assert_eq!(err.error_end, 5);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 4);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -173,12 +246,8 @@ mod tests {
|
||||
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(
|
||||
err.reason,
|
||||
"Expected an identifier after the `fun` keyword."
|
||||
);
|
||||
assert_eq!(err.error_start, 0);
|
||||
assert_eq!(err.error_end, 3);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 0);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -191,12 +260,8 @@ mod tests {
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(
|
||||
err.reason,
|
||||
"Expected an opening paren after the function identifier."
|
||||
);
|
||||
assert_eq!(err.error_start, 7);
|
||||
assert_eq!(err.error_end, 8);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 7);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -205,12 +270,8 @@ mod tests {
|
||||
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(
|
||||
err.reason,
|
||||
"Expected an opening paren after the function identifier."
|
||||
);
|
||||
assert_eq!(err.error_start, 4);
|
||||
assert_eq!(err.error_end, 6);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 4);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -223,12 +284,8 @@ mod tests {
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(
|
||||
err.reason,
|
||||
"Expected a closing paren after the function identifier."
|
||||
);
|
||||
assert_eq!(err.error_start, 7);
|
||||
assert_eq!(err.error_end, 8);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 7);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -237,12 +294,8 @@ mod tests {
|
||||
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(
|
||||
err.reason,
|
||||
"Expected a closing paren after the function identifier."
|
||||
);
|
||||
assert_eq!(err.error_start, 6);
|
||||
assert_eq!(err.error_end, 7);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 6);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -255,12 +308,8 @@ mod tests {
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(
|
||||
err.reason,
|
||||
"Expected an identifier after the `fun` keyword."
|
||||
);
|
||||
assert_eq!(err.error_start, 0);
|
||||
assert_eq!(err.error_end, 3);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 0);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -271,12 +320,8 @@ mod tests {
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(
|
||||
err.reason,
|
||||
"Expected an identifier after the `fun` keyword."
|
||||
);
|
||||
assert_eq!(err.error_start, 0);
|
||||
assert_eq!(err.error_end, 3);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 0);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -289,12 +334,8 @@ mod tests {
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(
|
||||
err.reason,
|
||||
"Expected a block after the function declaration."
|
||||
);
|
||||
assert_eq!(err.error_start, 9);
|
||||
assert_eq!(err.error_end, 10);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 9);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -303,12 +344,8 @@ mod tests {
|
||||
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(
|
||||
err.reason,
|
||||
"Expected a block after the function declaration."
|
||||
);
|
||||
assert_eq!(err.error_start, 4);
|
||||
assert_eq!(err.error_end, 6);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 4);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -321,9 +358,8 @@ mod tests {
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(err.reason, "Expected a closing brace after the block body.");
|
||||
assert_eq!(err.error_start, 9);
|
||||
assert_eq!(err.error_end, 10);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 9);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -333,9 +369,8 @@ mod tests {
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(err.reason, "Expected a closing brace after the block body.");
|
||||
assert_eq!(err.error_start, 9);
|
||||
assert_eq!(err.error_end, 10);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 9);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -369,9 +404,8 @@ mod tests {
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(err.reason, "Expected a datatype after the arrow operator.");
|
||||
assert_eq!(err.error_start, 12);
|
||||
assert_eq!(err.error_end, 13);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 9);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
@ -384,9 +418,8 @@ mod tests {
|
||||
|
||||
match fun_decl {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(err.reason, "Expected a datatype after the arrow operator.");
|
||||
assert_eq!(err.error_start, 9);
|
||||
assert_eq!(err.error_end, 11);
|
||||
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
|
||||
assert_eq!(err.error_offset, 9);
|
||||
}
|
||||
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
error_handling::{
|
||||
error_messages::{SYNTAX_INCOMPLETE_STATEMENT, SYNTAX_UNEXPECTED_TOKENS},
|
||||
ErrorContainer, ErrorLabel,
|
||||
},
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{
|
||||
ast::{Expression, ModuleAST, ModuleMembers, Statement},
|
||||
@ -46,14 +49,23 @@ impl<'a> Parseable<'a> for ModuleAST<'a> {
|
||||
let next_pos = match parse_terminator(tokens, next_pos) {
|
||||
Ok((_, next)) => next,
|
||||
Err(ParsingError::Mismatch(t)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
reason: format!(
|
||||
"Unexpected token `{}`, expected a new line",
|
||||
t.value
|
||||
let label = ErrorLabel {
|
||||
message: String::from(
|
||||
"Expected a new line here, found another token",
|
||||
),
|
||||
}))
|
||||
start: t.position,
|
||||
end: t.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INCOMPLETE_STATEMENT,
|
||||
error_offset: t.position,
|
||||
labels: vec![label],
|
||||
note: Some(String::from(
|
||||
"There may only be one statement per line",
|
||||
)),
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@ -78,14 +90,22 @@ impl<'a> Parseable<'a> for ModuleAST<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// If we reached this point we didn't match any productions and fail
|
||||
// If we reached this point we didn't match any productions and should fail
|
||||
let t = &tokens[current_pos];
|
||||
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: t.position,
|
||||
error_end: t.get_end_position(),
|
||||
reason: "Expected an statement or an expresion at the top level.".into(),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("This sequence of tokens couldn't be parsed"),
|
||||
start: t.position,
|
||||
end: t.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_UNEXPECTED_TOKENS,
|
||||
error_offset: t.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
|
||||
Ok((ModuleAST { productions }, current_pos))
|
||||
@ -150,7 +170,7 @@ mod test {
|
||||
match result {
|
||||
Ok(_) => panic!("Expected an error"),
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!("Unexpected token `print`, expected a new line", err.reason);
|
||||
assert_eq!(err.error_code, SYNTAX_INCOMPLETE_STATEMENT)
|
||||
}
|
||||
_ => panic!("Expected a parsing error"),
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
error_handling::SyntaxError,
|
||||
error_handling::{error_messages::SYNTAX_INVALID_WHILE_LOOP, ErrorContainer, ErrorLabel},
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{
|
||||
ast::{loops::WhileLoop, Block, Expression, Positionable},
|
||||
@ -23,21 +23,34 @@ impl<'a> Parseable<'a> for WhileLoop<'a> {
|
||||
Ok(t) => t,
|
||||
Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)),
|
||||
Err(ParsingError::Mismatch(e)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: e.position,
|
||||
error_end: e.get_end_position(),
|
||||
reason: format!(
|
||||
"Expected an expression after the `while` keyword, found {}",
|
||||
e.value
|
||||
),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a Bool expression here"),
|
||||
start: e.position,
|
||||
end: e.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_WHILE_LOOP,
|
||||
error_offset: e.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: while_keyword.position,
|
||||
error_end: while_keyword.get_end_position(),
|
||||
reason: format!("Expected an identifier after the `while` keyword"),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a Bool expression after this `while` keyword"),
|
||||
start: while_keyword.position,
|
||||
end: while_keyword.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_WHILE_LOOP,
|
||||
error_offset: while_keyword.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -46,18 +59,36 @@ impl<'a> Parseable<'a> for WhileLoop<'a> {
|
||||
Ok(t) => t,
|
||||
Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)),
|
||||
Err(ParsingError::Mismatch(e)) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: e.position,
|
||||
error_end: e.get_end_position(),
|
||||
reason: format!("Expected a block after the condition, found {}", e.value),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block here, after the condition"),
|
||||
start: e.position,
|
||||
end: e.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_WHILE_LOOP,
|
||||
error_offset: e.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
Err(ParsingError::Unmatched) => {
|
||||
return Err(ParsingError::Err(SyntaxError {
|
||||
error_start: while_keyword.position,
|
||||
error_end: while_keyword.get_end_position(),
|
||||
reason: format!("Expected a block after the condition"),
|
||||
}))
|
||||
let (error_start, error_end) = condition.get_position();
|
||||
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Expected a block after this condition"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_WHILE_LOOP,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(ParsingError::Err(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -72,6 +72,8 @@ pub fn parse_token_type(
|
||||
|
||||
match tokens.get(current_pos) {
|
||||
Some(t) if t.token_type == token_type => Ok((t, current_pos + 1)),
|
||||
// TODO: Why are we checking if the token is NewLine here? Arent all newlines filtered
|
||||
// above?
|
||||
Some(t) if t.token_type == TokenType::EOF || t.token_type == TokenType::NewLine => {
|
||||
Err(ParsingError::Unmatched)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user