Compare commits
8 Commits
06d8c1b0f3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| dd1b6af259 | |||
| 96f8aa3bbb | |||
| 660b9245fb | |||
| 4b61adea5d | |||
| 081ff9a27a | |||
| 834b5b1213 | |||
| 1f4645ed24 | |||
| 158b76fe39 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,6 @@
|
|||||||
/target
|
/target
|
||||||
/*.c
|
/*.c
|
||||||
/*.s
|
/*.s
|
||||||
|
/*.o
|
||||||
/*.ssa
|
/*.ssa
|
||||||
|
/test
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mclangc"
|
name = "mclangc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
default-run = "mclangc"
|
default-run = "mclangc"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
|||||||
174
docs/main.md
Normal file
174
docs/main.md
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
|
||||||
|
## Quick warning
|
||||||
|
|
||||||
|
This language is still in its pre alpha stages, while it tends to work it still has numerous bugs, breaks, and fails with a smell of unusual input, it is not suitable for anything beyond experimenting and having fun. That said this project has been my biggest one yet, and is one of my favorites, having went under multiple rewrites over the 4 or so years its changed a lot from simple forth style syntax, to now, a c/rust style, which has been a struggle, but now its at a point where it actually produces code and you can *kinda* make something with it.
|
||||||
|
|
||||||
|
Hope you have fun figuring out how to use this damn thing, and maybe even build something with it.
|
||||||
|
|
||||||
|
Also, the docs are also written with ChatGPT (gross, i know), but i needed some docs fast.
|
||||||
|
|
||||||
|
\- MCorange
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This language is a small, low level, C and Rust inspired systems language with a strong focus on explicit control, simple syntax, and predictable behavior. It mixes familiar C style statements and loops with Rust-like ideas such as structs, methods, references, and explicit types. The goal is clarity over magic: most things are spelled out, lifetimes are implied by references, and there is little hidden behavior.
|
||||||
|
|
||||||
|
The language is suitable for experiments, embedded style programs, or as a foundation for a custom runtime or OS environment. It favors straightforward semantics, manual control flow, and a minimal standard library model.
|
||||||
|
|
||||||
|
## High level description
|
||||||
|
|
||||||
|
As a procedural language, barely higher level than C, programs are built from functions, structs, and constants. Types are explicit, references are written by prepending `&`, and arrays or slices are expressed directly in the type system. For now code is organized with `include`, for simpler development, this will change into modules eventually.
|
||||||
|
|
||||||
|
Control flow is traditional and imperative. There are `if`, `else`, `for`, `while`, and `loop` constructs, along with `break` and `continue`. Functions return values explicitly using `return`, and side effects are visible and direct.
|
||||||
|
|
||||||
|
Methods are just functions namespaced under a type, and there is no hidden constructor logic. Everything that allocates or returns a reference does so explicitly.
|
||||||
|
|
||||||
|
## Small language reference
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
Type aliases:
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
type cstr = [u8];
|
||||||
|
```
|
||||||
|
|
||||||
|
Creates an alias. Here, `str` is defined as a byte array type.
|
||||||
|
|
||||||
|
Built-in simple types:
|
||||||
|
|
||||||
|
* usize/isize
|
||||||
|
* i8-i64
|
||||||
|
* u8-u64
|
||||||
|
* bool
|
||||||
|
* array or slice types like [u8]
|
||||||
|
|
||||||
|
References are written with `&T`.
|
||||||
|
|
||||||
|
## Includes
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
include "std.mcl";
|
||||||
|
```
|
||||||
|
|
||||||
|
Textual inclusion of another source file. This is resolved at compile time.
|
||||||
|
|
||||||
|
## Structs
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
struct Foo {
|
||||||
|
a: usize,
|
||||||
|
b: &str
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Structs group named fields. Fields have explicit types. There are no implicit constructors.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
Function definition syntax:
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
fn name(arg: Type, ...) -> ReturnType {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
fn mul(n: usize, n2: usize) -> usize {
|
||||||
|
return n * n2;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Functions may return values using `return`. Void functions omit the return type.
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
Methods are functions scoped to a type:
|
||||||
|
|
||||||
|
As of now methods are only able to be linked to [Structs](#Structs)
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
fn Foo.new(a1: usize, b: &str) -> &Foo {
|
||||||
|
return &Foo {
|
||||||
|
a: a1,
|
||||||
|
b: b
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This defines a constructor-like function for `Foo`. It explicitly returns a reference to a `Foo` value.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
Variables are declared with `let`:
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
let obj = Foo::new(1, "owo");
|
||||||
|
```
|
||||||
|
|
||||||
|
Type inference appears to exist, but types can likely be written explicitly if desired.
|
||||||
|
|
||||||
|
## Field access
|
||||||
|
|
||||||
|
Struct fields can be accessed through references:
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
obj->b;
|
||||||
|
```
|
||||||
|
|
||||||
|
As well as without dereferencing:
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
obj.c
|
||||||
|
```
|
||||||
|
|
||||||
|
## Control flow
|
||||||
|
|
||||||
|
Infinite loop:
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
loop {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
While loop:
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
while (true) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For loop:
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
for (let i = 0; i < 10; i += 1) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Conditionals:
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
if (i > 7) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Constants
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
const FOO: usize = main;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Statics
|
||||||
|
|
||||||
|
```no-test
|
||||||
|
static FOO: usize = main;
|
||||||
|
```
|
||||||
55
editor_support/mclang4.vim
Normal file
55
editor_support/mclang4.vim
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
" Vim syntax file
|
||||||
|
" Language: MClang4
|
||||||
|
" Maintainer: MCorange
|
||||||
|
" Maintainer: xomf
|
||||||
|
|
||||||
|
if exists("b:current_syntax")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
syn match MCLangSlash '/' containedin=ALLBUT,MCLangComment
|
||||||
|
syn match MCLangComment '//.*'
|
||||||
|
|
||||||
|
syn region MCLangComment start='/\*' end='\*/' contains=@Spell
|
||||||
|
|
||||||
|
syn keyword MCLangKeywords fn if else struct enum type while for break continue let const mut static include extern return loop as
|
||||||
|
" syn keyword MCLangOp + -
|
||||||
|
syn keyword MCLangTypes void u8 u16 u32 u64 i8 i16 i32 i64 usize isize str
|
||||||
|
syn keyword MCLangBool true false
|
||||||
|
syn keyword MCLangConst NULL
|
||||||
|
" i8 i16 i32 i64 f32 f64
|
||||||
|
syn region MCLangString start=+"+ skip=+\\.+ end=+"+ contains=@Spell
|
||||||
|
syn region MCLangChar start=+'+ skip=+\\.+ end=+'+
|
||||||
|
|
||||||
|
syn match MCLangFuncCall '\<[A-Za-z_][A-Za-z0-9_]*\>\ze\s*(' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||||
|
syn match MCLangFuncCall '\<[A-Za-z_][A-Za-z0-9_]*::[A-Za-z_][A-Za-z0-9_]*\>\ze\s*(' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||||
|
"syn match MCLangFuncDef '\<fn\s\+\zs[A-Za-z_][A-Za-z0-9_]*\.\?\>' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||||
|
"syn match MCLangConstName '\<const\s\+\zs[A-Za-z_][A-Za-z0-9_]*\>' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||||
|
syn match MCLangIdentifier '\<[A-Za-z_][A-Za-z0-9_]*\>'
|
||||||
|
syn match MCLangOp '[+\-*%&|^!~<>]=\?\|==\|!=\|<=\|>=\|&&\|\\|\|::\|:\|[;,\.]' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||||
|
syn match MCLangNumber '\<\(0b[01]\+\|0x[0-9A-Fa-f]\+\|\d\+\.\d*\|\.\d\+\|\d\+\)\>' containedin=ALLBUT,MCLangString,MCLangChar,MCLangComment
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
syn match MCLangDelim /[()]/
|
||||||
|
syn match MCLangDelim /[{}]/
|
||||||
|
syn match MCLangDelim /[\[\]]/
|
||||||
|
|
||||||
|
let b:current_syntax = "MCLang4"
|
||||||
|
|
||||||
|
hi def link MCLangComment Comment
|
||||||
|
hi def link MCLangTypes Type
|
||||||
|
hi def link MCLangString String
|
||||||
|
hi def link MCLangChar Character
|
||||||
|
hi def link MCLangNumber Constant
|
||||||
|
hi def link MCLangConst Constant
|
||||||
|
"hi def link MCLangConstName Constant
|
||||||
|
hi def link MCLangBool Boolean
|
||||||
|
hi def link MCLangDelim Delimiter
|
||||||
|
hi def link MCLangOp Operator
|
||||||
|
hi def link MCLangKeywords Keyword
|
||||||
|
"hi def link MCLangIdentifier Identifier
|
||||||
|
hi def link MCLangFuncCall Function
|
||||||
|
"hi def link MCLangFuncDef Function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
libbeaker.a
Normal file
BIN
libbeaker.a
Normal file
Binary file not shown.
BIN
libbeaker.so
Executable file
BIN
libbeaker.so
Executable file
Binary file not shown.
1
old_tests/tokeniser/comments.exp
Normal file
1
old_tests/tokeniser/comments.exp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@@ -133,7 +133,7 @@ fn test_parser(cf: &CollectedFiles, compile: bool) -> anyhow::Result<usize> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let ast = match mclangc::parser::parse_program(tokens) {
|
let ast = match mclangc::parser::parse_program(tokens, &mclangc::cli::CliArgs::default()) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
crate::error!("Test parser/{name} had an error: {e}");
|
crate::error!("Test parser/{name} had an error: {e}");
|
||||||
13
src/cli.rs
13
src/cli.rs
@@ -3,7 +3,7 @@ use clap::{error::ErrorKind, CommandFactory};
|
|||||||
use crate::logger::Level;
|
use crate::logger::Level;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, clap::Parser)]
|
#[derive(Debug, Default, clap::Parser)]
|
||||||
pub struct CliArgs {
|
pub struct CliArgs {
|
||||||
/// Output more info, will get overwritten if -q|--quiet is specified
|
/// Output more info, will get overwritten if -q|--quiet is specified
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
@@ -13,9 +13,20 @@ pub struct CliArgs {
|
|||||||
quiet: bool,
|
quiet: bool,
|
||||||
#[arg(long, short, value_parser=crate::targets::get_all_targets(), default_value_t=crate::targets::get_default_target())]
|
#[arg(long, short, value_parser=crate::targets::get_all_targets(), default_value_t=crate::targets::get_default_target())]
|
||||||
pub target: String,
|
pub target: String,
|
||||||
|
#[arg(long="include", short='I', default_values=[".", "include"])]
|
||||||
|
pub include_paths: Vec<String>,
|
||||||
/// Output file
|
/// Output file
|
||||||
#[arg(long, short, default_value="a.out")]
|
#[arg(long, short, default_value="a.out")]
|
||||||
pub output: String,
|
pub output: String,
|
||||||
|
|
||||||
|
/// Linker args
|
||||||
|
#[arg(long, short='L')]
|
||||||
|
pub linker_args: Vec<String>,
|
||||||
|
|
||||||
|
/// Link with C libs, required for any C libraries
|
||||||
|
#[arg(long, short='C')]
|
||||||
|
pub link_c: bool,
|
||||||
|
|
||||||
/// All input files
|
/// All input files
|
||||||
#[clap(num_args = 1..)]
|
#[clap(num_args = 1..)]
|
||||||
pub input: Vec<String>
|
pub input: Vec<String>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
#[derive(Debug, Clone, PartialOrd, Ord, Eq, Hash)]
|
||||||
pub struct Loc {
|
pub struct Loc {
|
||||||
file: String,
|
file: String,
|
||||||
line: usize,
|
line: usize,
|
||||||
@@ -27,7 +27,7 @@ impl Loc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct LocBox<T: Clone + Debug> {
|
pub struct LocBox<T: Clone + Debug> {
|
||||||
inner: T,
|
inner: T,
|
||||||
loc: Loc
|
loc: Loc
|
||||||
@@ -64,6 +64,12 @@ impl Default for Loc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Loc {
|
||||||
|
fn eq(&self, _: &Self) -> bool {
|
||||||
|
true // ignore eq cause comparing 2 locations is irrelevant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait LocIncr {
|
pub trait LocIncr {
|
||||||
fn inc_line(&mut self);
|
fn inc_line(&mut self);
|
||||||
fn inc_col(&mut self);
|
fn inc_col(&mut self);
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#![doc = include_str!("../docs/main.md")]
|
||||||
|
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod tokeniser;
|
pub mod tokeniser;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ pub mod log {
|
|||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! lerror {
|
macro_rules! lerror {
|
||||||
($loc:expr, $($arg:tt)*) => {
|
($loc:expr_2021, $($arg:tt)*) => {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Error, &format!($($arg)*))
|
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Error, &format!($($arg)*))
|
||||||
} else {
|
} else {
|
||||||
@@ -120,7 +120,7 @@ pub mod log {
|
|||||||
}
|
}
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! lwarn {
|
macro_rules! lwarn {
|
||||||
($loc:expr, $($arg:tt)*) => {
|
($loc:expr_2021, $($arg:tt)*) => {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Warn, &format!($($arg)*))
|
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Warn, &format!($($arg)*))
|
||||||
} else {
|
} else {
|
||||||
@@ -130,7 +130,7 @@ pub mod log {
|
|||||||
}
|
}
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! linfo {
|
macro_rules! linfo {
|
||||||
($loc:expr, $($arg:tt)*) => {
|
($loc:expr_2021, $($arg:tt)*) => {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Info, &format!($($arg)*))
|
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Info, &format!($($arg)*))
|
||||||
} else {
|
} else {
|
||||||
@@ -140,7 +140,7 @@ pub mod log {
|
|||||||
}
|
}
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! lhelp {
|
macro_rules! lhelp {
|
||||||
($loc:expr, $($arg:tt)*) => {
|
($loc:expr_2021, $($arg:tt)*) => {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Help, &format!($($arg)*))
|
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Help, &format!($($arg)*))
|
||||||
} else {
|
} else {
|
||||||
@@ -150,7 +150,7 @@ pub mod log {
|
|||||||
}
|
}
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! ldebug {
|
macro_rules! ldebug {
|
||||||
($loc:expr, $($arg:tt)*) => {
|
($loc:expr_2021, $($arg:tt)*) => {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Debug, &format!($($arg)*))
|
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Debug, &format!($($arg)*))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
14
src/main.rs
14
src/main.rs
@@ -1,4 +1,6 @@
|
|||||||
use std::{path::PathBuf, process::ExitCode};
|
#![doc = include_str!("../docs/main.md")]
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
// Importing logger here too cause the logger macros dont work outside the mclanc lib
|
// Importing logger here too cause the logger macros dont work outside the mclanc lib
|
||||||
@@ -6,7 +8,6 @@ mod logger;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let cli = mclangc::cli::CliArgs::parse();
|
let cli = mclangc::cli::CliArgs::parse();
|
||||||
cli.set_log_level();
|
cli.set_log_level();
|
||||||
@@ -22,11 +23,16 @@ fn main() -> anyhow::Result<()> {
|
|||||||
let data = std::fs::read_to_string(&fp).unwrap();
|
let data = std::fs::read_to_string(&fp).unwrap();
|
||||||
info!("Tokenising {file}");
|
info!("Tokenising {file}");
|
||||||
let tokens = mclangc::tokeniser::tokenise(&data, &file)?;
|
let tokens = mclangc::tokeniser::tokenise(&data, &file)?;
|
||||||
|
// dbg!(&tokens);
|
||||||
info!("Parsing {file}");
|
info!("Parsing {file}");
|
||||||
let mut prog = mclangc::parser::parse_program(tokens)?;
|
let mut prog = mclangc::parser::parse_program(tokens, &cli)?;
|
||||||
info!("Validating {file}");
|
info!("Validating {file}");
|
||||||
mclangc::validator::validate_code(&mut prog)?;
|
mclangc::validator::validate_code(&mut prog)?;
|
||||||
// dbg!(&prog);
|
dbg!(&prog.functions.len());
|
||||||
|
for f in &prog.functions {
|
||||||
|
error!("owo {}", f.1.inner());
|
||||||
|
}
|
||||||
|
// dbg!(&prog.literals);
|
||||||
progs.push((fp, prog));
|
progs.push((fp, prog));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::{collections::HashMap, fmt::Display};
|
use std::{collections::BTreeMap, fmt::Display};
|
||||||
|
|
||||||
use crate::{common::loc::LocBox, tokeniser::tokentype::*};
|
use crate::{common::loc::LocBox, parser::ast::{Program, literal::Literal}, tokeniser::tokentype::*};
|
||||||
|
|
||||||
use super::{typ::Type, Ast};
|
use super::{typ::Type, Ast};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
// Comment(Comment),
|
// Comment(Comment),
|
||||||
Group(Box<LocBox<Expr>>),
|
Group(Box<LocBox<Expr>>),
|
||||||
@@ -16,8 +16,9 @@ pub enum Expr {
|
|||||||
typ: Punctuation,
|
typ: Punctuation,
|
||||||
left: Box<LocBox<Expr>>,
|
left: Box<LocBox<Expr>>,
|
||||||
right: Box<LocBox<Expr>>,
|
right: Box<LocBox<Expr>>,
|
||||||
|
signed: bool,
|
||||||
},
|
},
|
||||||
Literal(super::literal::Literal),
|
Literal(String, super::literal::Literal), // string is id
|
||||||
ArrayIndex {
|
ArrayIndex {
|
||||||
name: Box<LocBox<Expr>>,
|
name: Box<LocBox<Expr>>,
|
||||||
index: Box<LocBox<Expr>>,
|
index: Box<LocBox<Expr>>,
|
||||||
@@ -27,20 +28,26 @@ pub enum Expr {
|
|||||||
path: Box<LocBox<Expr>>,
|
path: Box<LocBox<Expr>>,
|
||||||
params: CallParams, // LocBox<Expr> ~ (, Expr)*
|
params: CallParams, // LocBox<Expr> ~ (, Expr)*
|
||||||
},
|
},
|
||||||
//MethodCall {
|
MethodCall {
|
||||||
// var_name: Box<LocBox<Expr>>,
|
left: Box<LocBox<Expr>>,
|
||||||
// method_name: Ident,
|
strct: Option<Ident>,
|
||||||
// params: CallParams,
|
params: CallParams,
|
||||||
//},
|
},
|
||||||
|
|
||||||
/// the left side only exists on the /.|->/ chain
|
/// the left side only exists on the /.|->/ chain
|
||||||
FieldAccess {
|
FieldAccess {
|
||||||
left: Box<Option<LocBox<Expr>>>,
|
left: Box<Option<LocBox<Expr>>>,
|
||||||
right: Box<LocBox<Expr>>,
|
right: Box<LocBox<Expr>>,
|
||||||
|
offset: usize,
|
||||||
|
l_typ: Option<Box<Type>>,
|
||||||
|
r_typ: Option<Box<Type>>,
|
||||||
},
|
},
|
||||||
PtrFieldAccess {
|
PtrFieldAccess {
|
||||||
left: Box<Option<LocBox<Expr>>>,
|
left: Box<Option<LocBox<Expr>>>,
|
||||||
right: Box<LocBox<Expr>>,
|
right: Box<LocBox<Expr>>,
|
||||||
|
offset: usize,
|
||||||
|
l_typ: Option<Box<Type>>,
|
||||||
|
r_typ: Option<Box<Type>>
|
||||||
},
|
},
|
||||||
ForLoop {
|
ForLoop {
|
||||||
init: Box<Ast>,
|
init: Box<Ast>,
|
||||||
@@ -56,10 +63,7 @@ pub enum Expr {
|
|||||||
body: Block,
|
body: Block,
|
||||||
},
|
},
|
||||||
If(IfExpr),
|
If(IfExpr),
|
||||||
Struct {
|
Struct(String, StructLit),
|
||||||
path: Path,
|
|
||||||
fields: HashMap<Ident, LocBox<Expr>>,
|
|
||||||
},
|
|
||||||
Return(Box<Option<LocBox<Expr>>>),
|
Return(Box<Option<LocBox<Expr>>>),
|
||||||
Break,
|
Break,
|
||||||
Continue,
|
Continue,
|
||||||
@@ -67,6 +71,15 @@ pub enum Expr {
|
|||||||
left: Box<LocBox<Expr>>,
|
left: Box<LocBox<Expr>>,
|
||||||
right: Box<LocBox<Type>>
|
right: Box<LocBox<Type>>
|
||||||
},
|
},
|
||||||
|
Self_ {
|
||||||
|
strct: Option<Ident>
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
|
||||||
|
pub struct StructLit {
|
||||||
|
pub path: Path,
|
||||||
|
pub fields: BTreeMap<Ident, LocBox<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
@@ -74,17 +87,36 @@ impl Expr {
|
|||||||
let Expr::Path(p) = self else {panic!("Unwrapping")};
|
let Expr::Path(p) = self else {panic!("Unwrapping")};
|
||||||
p.clone()
|
p.clone()
|
||||||
}
|
}
|
||||||
|
pub fn as_number(&self, prog: &Program) -> Option<Number> {
|
||||||
|
match self {
|
||||||
|
Self::Literal(_, Literal::Number(num)) => Some(*num),
|
||||||
|
Self::Path(Path(path)) => {
|
||||||
|
if let Some(val) = prog.get_const_var(path.last().unwrap()) {
|
||||||
|
val.1.inner().val.inner().as_number(prog)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub struct CallParams(pub Vec<LocBox<Expr>>);
|
pub struct CallParams(pub Vec<LocBox<Expr>>);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub struct Block(pub Vec<Ast>);
|
pub struct Block(pub Vec<Ast>);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl Default for Block {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
|
||||||
pub struct Path(pub Vec<Ident>);
|
pub struct Path(pub Vec<Ident>);
|
||||||
|
|
||||||
impl Display for Path {
|
impl Display for Path {
|
||||||
@@ -100,14 +132,20 @@ impl Display for Path {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl Path {
|
||||||
|
pub fn display_asm_compat(&self) -> String {
|
||||||
|
self.to_string().replace("::", "$")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub struct IfExpr {
|
pub struct IfExpr {
|
||||||
pub test: Box<LocBox<Expr>>,
|
pub test: Box<LocBox<Expr>>,
|
||||||
pub body: Block,
|
pub body: Block,
|
||||||
pub else_if: Option<IfBranchExpr>
|
pub else_if: Option<IfBranchExpr>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub enum IfBranchExpr {
|
pub enum IfBranchExpr {
|
||||||
ElseIf(Box<IfExpr>),
|
ElseIf(Box<IfExpr>),
|
||||||
Else(Block)
|
Else(Block)
|
||||||
|
|||||||
@@ -1,18 +1,61 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::{common::loc::LocBox, tokeniser::tokentype::*};
|
use crate::{common::{Loc, loc::LocBox}, debug, parser::ast::{Program, typ::Type}, tokeniser::tokentype::*, validator::{predefined::SIZE, validate_expr}};
|
||||||
|
|
||||||
use super::{expr::Expr, typ::Type, Ast};
|
use super::expr::Expr;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
Number(Number),
|
Number(Number),
|
||||||
Ident(Ident),
|
Ident(Ident),
|
||||||
String(TString),
|
String(TString),
|
||||||
Char(Char),
|
Char(Char),
|
||||||
Array(Vec<LocBox<Expr>>),
|
Array{
|
||||||
|
items: Vec<LocBox<Expr>>,
|
||||||
|
item_size: Option<usize>,
|
||||||
|
},
|
||||||
|
Bool(bool),
|
||||||
ArrayRepeat {
|
ArrayRepeat {
|
||||||
val: Box<LocBox<Expr>>,
|
val: Box<LocBox<Expr>>,
|
||||||
count: Box<LocBox<Expr>>,
|
item_size: Option<usize>,
|
||||||
|
count: usize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Literal {
|
||||||
|
pub fn to_type(&self, prog: &mut Program) -> anyhow::Result<Type> {
|
||||||
|
let t = match self {
|
||||||
|
Self::Number(_) => Type::Builtin { name: String::from("usize"), size: SIZE as u8, signed: false },
|
||||||
|
Self::Ident(ident) => Type::Owned(ident.clone()),
|
||||||
|
Self::String(str) if str.cstr => {
|
||||||
|
debug!("is cstr");
|
||||||
|
Type::Ref { inner: Box::new(Type::Owned(Ident(String::from("cstr")))), mutable: false}
|
||||||
|
},
|
||||||
|
Self::String(str) if !str.cstr => Type::Ref { inner: Box::new(Type::Owned(Ident(String::from("str")))), mutable: false},
|
||||||
|
Self::String(_) => unreachable!("stupid rust"),
|
||||||
|
Self::Char(_) => Type::Owned(Ident(String::from("char"))),
|
||||||
|
Self::Bool(_) => Type::Owned(Ident(String::from("bool"))),
|
||||||
|
Self::Array { items: arr, ..} => {
|
||||||
|
if arr.is_empty() {
|
||||||
|
Type::SizedArray {
|
||||||
|
inner: Box::new(Type::Builtin { name: "void".to_string(), size: 0, signed: false }),
|
||||||
|
count: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 0, base: 10, signed: false })))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut item = arr.first().unwrap().clone();
|
||||||
|
let loc = item.loc().clone();
|
||||||
|
Type::SizedArray {
|
||||||
|
inner: Box::new(validate_expr(prog, &mut item)?.unwrap()),
|
||||||
|
count: LocBox::new(&loc, Expr::Literal(String::new(), Literal::Number(Number { val: arr.len(), base: 10, signed: false })))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::ArrayRepeat { val, count, ..} => {
|
||||||
|
let loc = val.loc().clone();
|
||||||
|
let mut val = val.clone();
|
||||||
|
Type::SizedArray { inner: Box::new(validate_expr(prog, &mut val)?.unwrap()), count: LocBox::new(&loc, Expr::Literal(String::new(), Literal::Number(Number { val: *count, base: 10, signed: false })))}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
Ok(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use statement::{ConstVar, Enum, Function, StaticVar, Struct};
|
use statement::{ConstVar, Enum, Function, StaticVar, Struct};
|
||||||
|
|
||||||
use crate::{common::loc::LocBox, validator::predefined::TypeType};
|
use crate::{common::loc::LocBox, parser::ast::{expr::StructLit, literal::Literal, statement::Let, typ::Type}};
|
||||||
pub use crate::tokeniser::tokentype::*;
|
pub use crate::tokeniser::tokentype::*;
|
||||||
|
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
@@ -10,19 +10,104 @@ pub mod literal;
|
|||||||
pub mod statement;
|
pub mod statement;
|
||||||
pub mod typ;
|
pub mod typ;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Scope {
|
||||||
|
pub vars: HashMap<Ident, LocBox<Let>>,
|
||||||
|
pub static_vars: HashMap<Ident, StaticVar>,
|
||||||
|
pub const_vars: HashMap<Ident, ConstVar>,
|
||||||
|
pub inner_scope: Option<Box<Scope>>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
pub ast: expr::Block,
|
pub ast: expr::Block,
|
||||||
pub structs: HashMap<Ident, LocBox<Struct>>,
|
pub structs: HashMap<Ident, LocBox<Struct>>,
|
||||||
pub enums: HashMap<Ident, LocBox<Enum>>,
|
pub enums: HashMap<Ident, LocBox<Enum>>,
|
||||||
pub types: HashMap<Ident, TypeType>,
|
pub types: HashMap<Ident, LocBox<Type>>,
|
||||||
pub functions: HashMap<Ident, LocBox<Function>>,
|
pub functions: HashMap<Ident, LocBox<Function>>,
|
||||||
pub member_functions: HashMap<Ident, HashMap<Ident, LocBox<Function>>>,
|
pub member_functions: HashMap<Ident, HashMap<Ident, LocBox<Function>>>,
|
||||||
pub static_vars: HashMap<Ident, StaticVar>,
|
pub static_vars: HashMap<Ident, StaticVar>,
|
||||||
pub const_vars: HashMap<Ident, ConstVar>
|
pub const_vars: HashMap<Ident, ConstVar>,
|
||||||
|
pub literals: HashMap<String, Literal>, // string is id of literal
|
||||||
|
pub struct_literals: HashMap<String, StructLit>, // string is id of literal
|
||||||
|
pub scope: Option<Scope>,
|
||||||
|
pub curr_fn_args: HashMap<Ident, LocBox<Type>>,
|
||||||
|
pub curr_struct: Option<Ident>,
|
||||||
|
pub included_files: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
|
impl Scope {
|
||||||
|
pub fn get_var(&self, name: &Ident) -> Option<(Ident, LocBox<Let>)> {
|
||||||
|
if let Some(inner) = &self.inner_scope {
|
||||||
|
if let Some((ident, var)) = inner.get_var(name) {
|
||||||
|
return Some((ident, var));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(var) = self.vars.get(name) {
|
||||||
|
return Some((name.clone(), var.clone()));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
pub fn get_static_var(&self, name: &Ident) -> Option<(Ident, LocBox<StaticVar>)> {
|
||||||
|
if let Some(inner) = &self.inner_scope {
|
||||||
|
if let Some((ident, var)) = inner.get_static_var(name) {
|
||||||
|
return Some((ident, var));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(var) = self.static_vars.get(name) {
|
||||||
|
return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone())));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
pub fn get_const_var(&self, name: &Ident) -> Option<(Ident, LocBox<ConstVar>)> {
|
||||||
|
if let Some(inner) = &self.inner_scope {
|
||||||
|
if let Some((ident, var)) = inner.get_const_var(name) {
|
||||||
|
return Some((ident, var));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(var) = self.const_vars.get(name) {
|
||||||
|
return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone())));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn get_var(&self, name: &Ident) -> Option<(Ident, LocBox<Let>)> {
|
||||||
|
if let Some(scope) = &self.scope {
|
||||||
|
scope.get_var(name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_static_var(&self, name: &Ident) -> Option<(Ident, LocBox<StaticVar>)> {
|
||||||
|
if let Some(scope) = &self.scope {
|
||||||
|
if let Some((name, var)) = scope.get_static_var(name) {
|
||||||
|
return Some((name, var));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(var) = self.static_vars.get(name) {
|
||||||
|
return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone())));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
pub fn get_const_var(&self, name: &Ident) -> Option<(Ident, LocBox<ConstVar>)> {
|
||||||
|
if let Some(scope) = &self.scope {
|
||||||
|
if let Some((name, var)) = scope.get_const_var(name) {
|
||||||
|
return Some((name, var));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(var) = self.const_vars.get(name) {
|
||||||
|
return Some((name.clone(), LocBox::new(var.typ.loc(), var.clone())));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub enum Ast {
|
pub enum Ast {
|
||||||
Expr(LocBox<expr::Expr>),
|
Expr(LocBox<expr::Expr>),
|
||||||
Statement(LocBox<statement::Statement>),
|
Statement(LocBox<statement::Statement>),
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
use std::{collections::HashMap, fmt::{Display, write}, sync::Mutex};
|
||||||
|
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use crate::{common::{loc::LocBox, Loc}, lerror};
|
use crate::{common::loc::LocBox, lerror};
|
||||||
|
|
||||||
use super::{expr::{Block, Expr}, literal::Literal, typ::Type, Char, Ident, Number, Program, TString};
|
use super::{expr::{Block, Expr}, literal::Literal, typ::Type, Char, Ident, Number, Program, TString};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Fn(Function),
|
Fn(Function),
|
||||||
TypeAlias(TypeAlias),
|
TypeAlias(TypeAlias),
|
||||||
@@ -15,69 +17,122 @@ pub enum Statement {
|
|||||||
Enum(Enum),
|
Enum(Enum),
|
||||||
ConstVar(ConstVar),
|
ConstVar(ConstVar),
|
||||||
StaticVar(StaticVar),
|
StaticVar(StaticVar),
|
||||||
Let {
|
Include,
|
||||||
name: Ident,
|
Let(Let),
|
||||||
typ: Option<LocBox<Type>>,
|
|
||||||
val: Option<LocBox<Expr>>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
|
pub struct Let {
|
||||||
|
pub name: Ident,
|
||||||
|
pub typ: Option<LocBox<Type>>,
|
||||||
|
pub val: Option<LocBox<Expr>>,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub struct ConstVar {
|
pub struct ConstVar {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub typ: LocBox<Type>,
|
pub typ: LocBox<Type>,
|
||||||
pub val: LocBox<Expr>,
|
pub val: LocBox<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub struct StaticVar {
|
pub struct StaticVar {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub typ: LocBox<Type>,
|
pub typ: LocBox<Type>,
|
||||||
pub val: LocBox<Expr>,
|
pub val: LocBox<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub struct TypeAlias {
|
pub struct TypeAlias {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub typ: LocBox<Type>,
|
pub typ: LocBox<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub struct Struct {
|
pub struct Struct {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub fields: Vec<(Ident, LocBox<Type>)>,
|
pub fields: Vec<(Ident, LocBox<Type>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub struct Enum {
|
pub struct Enum {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub fields: Vec<Ident>,
|
pub fields: Vec<Ident>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialOrd, Hash)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub struct_name: Option<Ident>,
|
pub struct_name: Option<Ident>,
|
||||||
pub name: Ident,
|
pub name: Option<Ident>,
|
||||||
pub params: Vec<(Ident, LocBox<Type>)>,
|
pub params: Vec<(Ident, LocBox<Type>)>,
|
||||||
pub ret_type: Option<LocBox<Type>>,
|
pub ret_type: Option<Box<LocBox<Type>>>,
|
||||||
pub qual_const: bool,
|
pub qual_const: bool,
|
||||||
pub qual_extern: Option<TString>, // abi
|
pub qual_extern: Option<TString>, // abi
|
||||||
pub body: Option<Block>, // If None then its a type declaration
|
pub body: Option<Block>, // If None then its a type declaration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Function {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
dbg!(self.params == other.params);
|
||||||
|
dbg!(self.ret_type == other.ret_type);
|
||||||
|
dbg!(self.qual_extern == other.qual_extern);
|
||||||
|
dbg!(self.qual_const == other.qual_const);
|
||||||
|
dbg!(&self.params);
|
||||||
|
dbg!(&self.ret_type);
|
||||||
|
self.params == other.params &&
|
||||||
|
self.ret_type == other.ret_type &&
|
||||||
|
self.qual_extern == other.qual_extern &&
|
||||||
|
self.qual_const == other.qual_const
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Function {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut extrn = String::new();
|
||||||
|
let mut args = Vec::new();
|
||||||
|
let mut ret_t = String::new();
|
||||||
|
|
||||||
|
if let Some(ex) = &self.qual_extern {
|
||||||
|
if ex.val.is_empty() {
|
||||||
|
extrn = format!("extern ");
|
||||||
|
} else {
|
||||||
|
extrn = format!("extern \"{}\"", ex.val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for arg in &self.params {
|
||||||
|
args.push(format!("{}: {}", arg.0, arg.1.inner()));
|
||||||
|
}
|
||||||
|
if let Some(ret) = &self.ret_type {
|
||||||
|
ret_t = format!(" -> {}", ret.inner());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
write!(f, "{extrn}fn {}({}){ret_t}", self.get_full_name_pretty(), args.join(", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn get_full_name(&self) -> String {
|
pub fn get_full_name(&self) -> String {
|
||||||
if let Some(sn) = &self.struct_name {
|
if let Some(name) = &self.name {
|
||||||
format!("{sn}__S__{}", self.name.0)
|
if let Some(sn) = &self.struct_name {
|
||||||
|
format!("{sn}__S__{}", name.0)
|
||||||
|
} else {
|
||||||
|
format!("{}", name.0)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("{}", self.name.0)
|
String::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_full_name_pretty(&self) -> String {
|
pub fn get_full_name_pretty(&self) -> String {
|
||||||
if let Some(sn) = &self.struct_name {
|
if let Some(name) = &self.name {
|
||||||
format!("{sn}::{}", self.name.0)
|
if let Some(sn) = &self.struct_name {
|
||||||
|
format!("{sn}::{}", name.0)
|
||||||
|
} else {
|
||||||
|
format!("{}", name.0)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("{}", self.name.0)
|
String::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_def_as_str(&self) -> String {
|
pub fn get_def_as_str(&self) -> String {
|
||||||
@@ -123,124 +178,183 @@ impl Struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub enum ConstDataTyp {
|
pub enum ConstDataTyp {
|
||||||
Byte(u8),
|
Bytes(Vec<u8>),
|
||||||
AddrOfConst(Ident),
|
AddrOfConst(Ident),
|
||||||
AddrOfStatic(Ident),
|
AddrOfStatic(Ident),
|
||||||
AddrOfFunc(Ident),
|
AddrOfFunc(Ident),
|
||||||
|
Variable(Ident, usize),
|
||||||
|
Array(Vec<ConstDataTyp>)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_constant_data_as_bytes(program: &Program, value: LocBox<Expr>, is_little_endian: bool, must_be_number: bool) -> anyhow::Result<Vec<ConstDataTyp>> {
|
lazy_static!(
|
||||||
|
static ref CTX: Mutex<ConstantCalcCtx> = Mutex::new(ConstantCalcCtx::default());
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
struct ConstantCalcCtx {
|
||||||
|
current_array_item_size: Option<usize>,
|
||||||
|
current_struct_items: Option<HashMap<Ident, usize>>,
|
||||||
|
current_struct_item_name: Option<Ident>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get_constant_data_as_bytes(program: &Program, value: LocBox<Expr>, is_big_endian: bool, must_be_number: bool) -> anyhow::Result<ConstDataTyp> {
|
||||||
match value.inner() {
|
match value.inner() {
|
||||||
Expr::Literal(lit) => {
|
Expr::Literal(_, lit) => {
|
||||||
match lit {
|
match lit {
|
||||||
Literal::Char(Char(c)) => {
|
Literal::Char(Char(c)) => {
|
||||||
if must_be_number {
|
if must_be_number {
|
||||||
lerror!(value.loc(), "Expected number got char");
|
lerror!(value.loc(), "Expected number got char");
|
||||||
bail!("")
|
bail!("")
|
||||||
}
|
}
|
||||||
Ok(vec![ConstDataTyp::Byte(*c as u8)])
|
Ok(ConstDataTyp::Bytes(vec![*c as u8]))
|
||||||
},
|
},
|
||||||
Literal::String(TString { val, cstr }) => {
|
Literal::String(TString { val, cstr }) => {
|
||||||
if must_be_number {
|
if must_be_number {
|
||||||
lerror!(value.loc(), "Expected number got string");
|
lerror!(value.loc(), "Expected number got string");
|
||||||
bail!("")
|
bail!("")
|
||||||
}
|
}
|
||||||
let mut val = val.chars().into_iter().map(|f| ConstDataTyp::Byte(f as u8)).collect::<Vec<_>>();
|
|
||||||
if *cstr {
|
if *cstr {
|
||||||
val.push(ConstDataTyp::Byte(0));
|
let mut val = val.chars().into_iter().map(|v| v as u8).collect::<Vec<u8>>();
|
||||||
|
val.push(0);
|
||||||
|
Ok(ConstDataTyp::Bytes(val))
|
||||||
|
} else {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let mut inc = 0;
|
||||||
|
while inc < 8 {
|
||||||
|
buf.push(((val.len() << 8*inc) & 0xff) as u8);
|
||||||
|
inc += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_big_endian {
|
||||||
|
buf.reverse();
|
||||||
|
}
|
||||||
|
let val = val.chars().into_iter().map(|v| v as u8).collect::<Vec<u8>>();
|
||||||
|
Ok(ConstDataTyp::Array(vec![
|
||||||
|
ConstDataTyp::Bytes(buf),
|
||||||
|
ConstDataTyp::Bytes(val)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
Ok(val)
|
|
||||||
}
|
}
|
||||||
Literal::Number(Number { val, base: _, signed: _ }) => {
|
Literal::Number(Number { val, base: _, signed: _ }) => {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let mut inc = 0;
|
let mut inc = 0;
|
||||||
while inc < 8 {
|
while inc < 8 {
|
||||||
buf.push(ConstDataTyp::Byte(((val << 8*inc) & 0xff) as u8));
|
buf.push(((val << 8*inc) & 0xff) as u8);
|
||||||
inc += 1;
|
inc += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_little_endian {
|
if is_big_endian {
|
||||||
buf.reverse();
|
buf.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(buf)
|
Ok(ConstDataTyp::Bytes(buf))
|
||||||
}
|
}
|
||||||
Literal::Array(arr) => {
|
Literal::Array { items: arr, item_size } => {
|
||||||
if must_be_number {
|
if must_be_number {
|
||||||
lerror!(value.loc(), "Expected number got array");
|
lerror!(value.loc(), "Expected number got array");
|
||||||
bail!("")
|
bail!("")
|
||||||
}
|
}
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
if arr.len() < 1 {
|
if arr.len() < 1 {
|
||||||
return Ok(vec![]);
|
return Ok(ConstDataTyp::Bytes(vec![]));
|
||||||
}
|
}
|
||||||
|
CTX.lock().unwrap().current_array_item_size = *item_size;
|
||||||
for item in arr {
|
for item in arr {
|
||||||
let mut data = get_constant_data_as_bytes(program, item.clone(), is_little_endian, must_be_number)?;
|
let data = get_constant_data_as_bytes(program, item.clone(), is_big_endian, must_be_number)?;
|
||||||
bytes.append(&mut data);
|
bytes.push(data);
|
||||||
}
|
}
|
||||||
|
CTX.lock().unwrap().current_array_item_size = None;
|
||||||
|
|
||||||
Ok(bytes)
|
Ok(ConstDataTyp::Array(bytes))
|
||||||
}
|
}
|
||||||
Literal::ArrayRepeat { val, count } => {
|
Literal::ArrayRepeat { val, count, item_size} => {
|
||||||
if must_be_number {
|
if must_be_number {
|
||||||
lerror!(value.loc(), "Expected number got repeating array");
|
lerror!(value.loc(), "Expected number got repeating array");
|
||||||
bail!("")
|
bail!("")
|
||||||
}
|
}
|
||||||
let mut val = get_constant_data_as_bytes(program, (**val).clone(), is_little_endian, must_be_number)?;
|
|
||||||
let num = get_constant_data_as_bytes(program, (**count).clone(), is_little_endian, true)?;
|
CTX.lock().unwrap().current_array_item_size = *item_size;
|
||||||
|
let val = get_constant_data_as_bytes(program, (**val).clone(), is_big_endian, must_be_number)?;
|
||||||
|
CTX.lock().unwrap().current_array_item_size = None;
|
||||||
|
|
||||||
|
let mut num = Vec::new();
|
||||||
|
let mut inc = 0;
|
||||||
|
while inc < 8 {
|
||||||
|
num.push(((count << 8*inc) & 0xff) as u8);
|
||||||
|
inc += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_big_endian {
|
||||||
|
num.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
let mut count = 0 as usize;
|
let mut count = 0 as usize;
|
||||||
for b in num {
|
for b in num {
|
||||||
let ConstDataTyp::Byte(b) = b else {unreachable!()};
|
|
||||||
count = b as usize;
|
count = b as usize;
|
||||||
count <<= 8;
|
count <<= 8;
|
||||||
}
|
}
|
||||||
let orig = val.clone();
|
let orig = val.clone();
|
||||||
|
let mut arr = Vec::new();
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
val.append(&mut orig.clone());
|
arr.push(orig.clone());
|
||||||
}
|
}
|
||||||
Ok(val)
|
Ok(ConstDataTyp::Array(arr))
|
||||||
}
|
}
|
||||||
Literal::Ident(name) => {
|
Literal::Bool(v) => {
|
||||||
if let Some(var) = program.const_vars.get(name) {
|
Ok(ConstDataTyp::Bytes(vec![*v as u8]))
|
||||||
Ok(get_constant_data_as_bytes(program, var.val.clone(), is_little_endian, must_be_number)?)
|
|
||||||
} else if let Some(_) = program.static_vars.get(name) {
|
|
||||||
lerror!(value.loc(), "Statics cannot be passed by value, use a reference");
|
|
||||||
bail!("")
|
|
||||||
} else if let Some(_) = program.functions.get(name) {
|
|
||||||
Ok(vec![ConstDataTyp::AddrOfFunc(name.clone())])
|
|
||||||
} else {
|
|
||||||
lerror!(value.loc(), "Unable to find ident '{name}'");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//Literal::Struct { name, fields } => {
|
Literal::Ident(_) => unreachable!()
|
||||||
// if must_be_number {
|
|
||||||
// lerror!(value.loc(), "Expected number got struct literal");
|
|
||||||
// bail!("")
|
|
||||||
// }
|
|
||||||
// let Some(strct) = program.structs.get(name) else {
|
|
||||||
// lerror!(value.loc(), "Could not find struct {name}");
|
|
||||||
// bail!("")
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// let mut strct_fields = HashMap::new();
|
|
||||||
// for (name, typ) in &strct.inner().fields {
|
|
||||||
// strct_fields.insert(name, typ);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (name, val) in fields {
|
|
||||||
// if let Some(_fld) = strct_fields.get(name) {
|
|
||||||
// // TODO: Actually check if the fields are the right type
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// todo!()
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
_ => unreachable!()
|
Expr::Struct(_, strct) => {
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
if must_be_number {
|
||||||
|
lerror!(value.loc(), "Expected number got struct literal");
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut items = HashMap::new();
|
||||||
|
|
||||||
|
for (name, item) in program.structs.get(&strct.path.0.first().unwrap()).unwrap().inner().fields.iter() {
|
||||||
|
items.insert(name.clone(), item.inner().size_of(program)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTX.lock().unwrap().current_struct_items = Some(items);
|
||||||
|
|
||||||
|
for field in &strct.fields {
|
||||||
|
CTX.lock().unwrap().current_struct_item_name = Some(field.0.clone());
|
||||||
|
bytes.push(get_constant_data_as_bytes(program, field.1.clone(), is_big_endian, must_be_number)?);
|
||||||
|
}
|
||||||
|
CTX.lock().unwrap().current_struct_item_name = None;
|
||||||
|
|
||||||
|
Ok(ConstDataTyp::Array(bytes))
|
||||||
|
}
|
||||||
|
Expr::Path(path) => {
|
||||||
|
let ctx = {CTX.lock().unwrap().clone()};
|
||||||
|
let name = path.0.last().unwrap();
|
||||||
|
if let Some(var) = program.const_vars.get(name) {
|
||||||
|
Ok(get_constant_data_as_bytes(program, var.val.clone(), is_big_endian, must_be_number)?)
|
||||||
|
} else if let Some(_) = program.static_vars.get(name) {
|
||||||
|
lerror!(value.loc(), "Statics cannot be passed by value, use a reference");
|
||||||
|
bail!("")
|
||||||
|
} else if let Some(_) = program.functions.get(name) {
|
||||||
|
Ok(ConstDataTyp::AddrOfFunc(name.clone()))
|
||||||
|
} else if let Some(struct_items) = ctx.current_struct_items &&
|
||||||
|
let Some(typ) = struct_items.get(&ctx.current_struct_item_name.unwrap()) {
|
||||||
|
Ok(ConstDataTyp::Variable(name.clone(), *typ))
|
||||||
|
} else if let Some(array_item_size) = {CTX.lock().unwrap().clone()}.current_array_item_size {
|
||||||
|
Ok(ConstDataTyp::Variable(name.clone(), array_item_size))
|
||||||
|
} else {
|
||||||
|
lerror!(value.loc(), "Unable to find ident '{name}'");
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
v => unreachable!("{v:?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use std::fmt::Display;
|
use std::{clone, collections::HashSet, fmt::Display};
|
||||||
|
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
|
|
||||||
use crate::{common::{loc::LocBox, Loc}, logger::log, validator::predefined::TypeType};
|
use crate::{common::loc::LocBox, error, info, parser::ast::statement::Function, validator::predefined::{BuiltinType, get_builtin_from_name}};
|
||||||
|
|
||||||
use super::{expr::Expr, literal::Literal, Ident, Program};
|
use super::{expr::Expr, literal::Literal, Ident, Program};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
Ref {
|
Ref {
|
||||||
inner: Box<Type>,
|
inner: Box<Type>,
|
||||||
@@ -20,41 +20,304 @@ pub enum Type {
|
|||||||
inner: Box<Type>,
|
inner: Box<Type>,
|
||||||
},
|
},
|
||||||
Owned(Ident),
|
Owned(Ident),
|
||||||
|
Builtin {
|
||||||
|
name: String,
|
||||||
|
size: u8,
|
||||||
|
signed: bool,
|
||||||
|
},
|
||||||
|
FnPtr(Function)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Type {
|
impl Type {
|
||||||
pub fn size_of(&self, program: &Program) -> anyhow::Result<usize> {
|
pub fn is_void(&self) -> bool {
|
||||||
match self {
|
*self == BuiltinType::void()
|
||||||
Self::Ref { inner, mutable } => {
|
}
|
||||||
// TODO: Use the actual ptr size
|
pub fn as_ref(self) -> Self {
|
||||||
Ok(size_of::<*const ()>())
|
Self::Ref {
|
||||||
}
|
inner: Box::new(self),
|
||||||
Self::UnsizedArray { .. } => {
|
mutable: false
|
||||||
bail!("Unsized arrays dont have a known size and must be behind a pointer")
|
}
|
||||||
}
|
}
|
||||||
Self::SizedArray { inner, .. } => {
|
pub fn as_ref_mut(self) -> Self {
|
||||||
Ok(inner.size_of(program)? )
|
Self::Ref {
|
||||||
}
|
inner: Box::new(self),
|
||||||
Self::Owned(name) => {
|
mutable: true
|
||||||
if let Some(v) = program.structs.get(&name) {
|
}
|
||||||
return Ok(v.inner().get_size(program)?);
|
}
|
||||||
}
|
pub fn new_builtin(name: &str, size: u8, signed: bool) -> Self {
|
||||||
if let Some(v) = program.types.get(&name) {
|
Self::Builtin { name: name.to_string(), size, signed }
|
||||||
match v {
|
}
|
||||||
TypeType::Normal(v) => {
|
pub fn should_deref_pointer(&self, program: &Program) -> bool {
|
||||||
return Ok(v.inner().size_of(program)?);
|
dbg!(&self);
|
||||||
}
|
let mut slf = self.clone();
|
||||||
TypeType::Builtin { size, .. } => {
|
match &mut slf {
|
||||||
return Ok(*size);
|
Self::Ref { inner, .. } => {
|
||||||
|
match inner.as_mut() {
|
||||||
|
Self::Ref { .. } => true,
|
||||||
|
Self::Owned(ident) => {
|
||||||
|
if program.structs.get(ident).is_some() {
|
||||||
|
false
|
||||||
|
} else
|
||||||
|
if let Some(typ) = program.types.get(ident) {
|
||||||
|
**inner = typ.inner().clone();
|
||||||
|
slf.should_deref_pointer(program)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => true,
|
||||||
}
|
}
|
||||||
if let Some(_) = program.enums.get(&name) {
|
},
|
||||||
return Ok(4); // TODO: Make enum size changeable
|
Self::FnPtr(_) |
|
||||||
|
Self::Owned(_) |
|
||||||
|
Self::SizedArray { .. } |
|
||||||
|
Self::Builtin { .. } |
|
||||||
|
Self::UnsizedArray { .. } => false,
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_real_value_of_alias(&self, name: &Ident, program: &Program) -> anyhow::Result<Self> {
|
||||||
|
let mut used_types = HashSet::new();
|
||||||
|
used_types.insert(name.clone());
|
||||||
|
fn f(slf: &mut Type, program: &Program, used_types: &mut HashSet<Ident>) -> anyhow::Result<Type> {
|
||||||
|
match slf {
|
||||||
|
Type::Ref {inner, ..} => {
|
||||||
|
f(inner, program, used_types)
|
||||||
}
|
}
|
||||||
bail!("Unknown type '{name}'")
|
Type::Owned(ident) => {
|
||||||
|
if let Some(name) = used_types.get(ident) {
|
||||||
|
error!("Recursive type alias: {name}");
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
if let Some(t) = program.types.get(ident) {
|
||||||
|
used_types.insert(ident.clone());
|
||||||
|
let mut t = t.inner().clone();
|
||||||
|
f(&mut t, program, used_types)
|
||||||
|
} else {
|
||||||
|
Ok(slf.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::SizedArray { inner, .. } |
|
||||||
|
Type::UnsizedArray { inner } => {
|
||||||
|
f(inner, program, used_types)
|
||||||
|
}
|
||||||
|
Type::FnPtr(_) |
|
||||||
|
Type::Builtin { .. } => Ok(slf.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut t = self.clone();
|
||||||
|
f(&mut t, program, &mut used_types)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_absolute_value(&self, program: &Program) -> anyhow::Result<Self> {
|
||||||
|
match self {
|
||||||
|
Self::Ref { inner, .. } => inner.get_absolute_value(program),
|
||||||
|
Self::Owned(ident) => {
|
||||||
|
if let Some(t) = get_builtin_from_name(ident.0.as_str()) {
|
||||||
|
return Ok(t)
|
||||||
|
}
|
||||||
|
if let Some(t) = program.curr_fn_args.get(ident) {
|
||||||
|
return Ok(t.inner().clone().get_absolute_value(program)?);
|
||||||
|
} else
|
||||||
|
if program.types.get(ident).is_some() ||
|
||||||
|
program.structs.get(ident).is_some() ||
|
||||||
|
program.enums.get(ident).is_some()
|
||||||
|
{
|
||||||
|
return Ok(self.clone());
|
||||||
|
}
|
||||||
|
if let Some(var) = program.get_var(ident) {
|
||||||
|
Ok(var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone().get_absolute_value(program)?)
|
||||||
|
} else {
|
||||||
|
bail!("owo? missing value: {ident}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::FnPtr(_) |
|
||||||
|
Self::SizedArray { .. } |
|
||||||
|
Self::Builtin { .. } |
|
||||||
|
Self::UnsizedArray { .. } => Ok(self.clone()),
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convert_owned_to_real_type(&mut self, program: &Program) {
|
||||||
|
match self {
|
||||||
|
Self::Owned(ident) => {
|
||||||
|
if let Some(t) = crate::validator::predefined::get_builtin_from_name(&ident.0) {
|
||||||
|
*self = t;
|
||||||
|
} else
|
||||||
|
if let Some(t) = program.types.get(ident) {
|
||||||
|
*self = t.inner().clone();
|
||||||
|
} else
|
||||||
|
if let Some(t) = program.curr_fn_args.get(ident) {
|
||||||
|
*self = t.inner().clone();
|
||||||
|
} else
|
||||||
|
if let Some(var) = program.get_var(ident) {
|
||||||
|
*self = var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Ref { inner, .. } |
|
||||||
|
Self::SizedArray { inner, .. } |
|
||||||
|
Self::UnsizedArray { inner, .. } => inner.convert_owned_to_real_type(program),
|
||||||
|
Self::FnPtr(_) |
|
||||||
|
Self::Builtin { .. } => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_variable_type(&self, program: &Program) -> anyhow::Result<Self> {
|
||||||
|
match self {
|
||||||
|
Self::Owned(ident) => {
|
||||||
|
dbg!(&ident);
|
||||||
|
dbg!(&crate::validator::predefined::get_builtin_from_name(&ident.0));
|
||||||
|
if let Some(t) = crate::validator::predefined::get_builtin_from_name(&ident.0) {
|
||||||
|
Ok(t)
|
||||||
|
} else
|
||||||
|
if let Some(t) = program.types.get(ident) {
|
||||||
|
Ok(t.inner().clone())
|
||||||
|
} else
|
||||||
|
if let Some(t) = program.curr_fn_args.get(ident) {
|
||||||
|
return Ok(t.inner().clone());
|
||||||
|
} else
|
||||||
|
if let Some(var) = program.get_var(ident) {
|
||||||
|
Ok(var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone())
|
||||||
|
} else
|
||||||
|
if let Some(func) = program.functions.get(ident) {
|
||||||
|
Ok(Type::FnPtr(func.inner().clone()))
|
||||||
|
} else {
|
||||||
|
bail!("owo? missing value: {ident}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Ref { .. } |
|
||||||
|
Self::SizedArray { .. } |
|
||||||
|
Self::Builtin { .. } |
|
||||||
|
Self::FnPtr(_) |
|
||||||
|
Self::UnsizedArray { .. } => Ok(self.clone()),
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_numeric(&self, program: &Program) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Ref { inner, .. } => {
|
||||||
|
inner.is_numeric(program)
|
||||||
|
}
|
||||||
|
Self::Owned(name) => {
|
||||||
|
match program.types.get(&name) {
|
||||||
|
Some(t) => t.inner().is_numeric(program),
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::SizedArray { .. } => false,
|
||||||
|
Self::FnPtr(_) |
|
||||||
|
Self::UnsizedArray { .. } => false,
|
||||||
|
Self::Builtin { name, .. } => {
|
||||||
|
match name.as_str() {
|
||||||
|
"u8" | "i8" |
|
||||||
|
"u16" | "i16" |
|
||||||
|
"u32" | "i32" |
|
||||||
|
"u64" | "i64" |
|
||||||
|
"usize" | "isize" => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Returns None if non numeric
|
||||||
|
pub fn is_signed(&self, program: &Program) -> Option<bool> {
|
||||||
|
if self.is_ptr(program) {
|
||||||
|
return Some(false);
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::Ref { inner, .. } => {
|
||||||
|
inner.is_signed(program)
|
||||||
|
}
|
||||||
|
Self::Owned(name) => {
|
||||||
|
match program.types.get(&name) {
|
||||||
|
Some(t) => t.inner().is_signed(program),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::FnPtr(_) |
|
||||||
|
Self::SizedArray { .. } => None,
|
||||||
|
Self::UnsizedArray { .. } => None,
|
||||||
|
Self::Builtin { name, .. } => {
|
||||||
|
match name.as_str() {
|
||||||
|
"i8" |
|
||||||
|
"i16" |
|
||||||
|
"i32" |
|
||||||
|
"i64" |
|
||||||
|
"isize" => Some(true),
|
||||||
|
"u8" |
|
||||||
|
"u16" |
|
||||||
|
"u32" |
|
||||||
|
"u64" |
|
||||||
|
"usize" => Some(false),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_ptr(&self, program: &Program) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Owned(name) => {
|
||||||
|
match program.types.get(&name) {
|
||||||
|
Some(t) => t.inner().is_ptr(program),
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::Ref { .. } => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_bool(&self, program: &Program) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Owned(name) => {
|
||||||
|
match program.types.get(&name) {
|
||||||
|
Some(t) => t.inner().is_bool(program),
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::Builtin { name, .. } if name.as_str() == "bool" => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn size_of(&self, program: &Program) -> anyhow::Result<usize> {
|
||||||
|
fn f(t: &Type, program: &Program, behind_a_ptr: bool) -> anyhow::Result<usize> {
|
||||||
|
match t {
|
||||||
|
Type::FnPtr(_) |
|
||||||
|
Type::Ref { .. } => {
|
||||||
|
// TODO: Use the actual ptr size
|
||||||
|
Ok(size_of::<*const ()>())
|
||||||
|
}
|
||||||
|
Type::UnsizedArray { .. } => {
|
||||||
|
if behind_a_ptr {
|
||||||
|
Ok(size_of::<*const ()>())
|
||||||
|
} else {
|
||||||
|
bail!("Unsized arrays dont have a known size and must be behind a pointer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::SizedArray { inner, .. } => {
|
||||||
|
Ok(inner.size_of(program)? )
|
||||||
|
}
|
||||||
|
Type::Builtin { size, .. } => {
|
||||||
|
Ok(*size as usize)
|
||||||
|
}
|
||||||
|
Type::Owned(name) => {
|
||||||
|
if let Some(v) = program.structs.get(&name) {
|
||||||
|
return Ok(v.inner().get_size(program)?);
|
||||||
|
}
|
||||||
|
if let Some(v) = program.types.get(&name) {
|
||||||
|
return Ok(f(v.inner(), program, behind_a_ptr)?);
|
||||||
|
}
|
||||||
|
if let Some(_) = program.enums.get(&name) {
|
||||||
|
return Ok(4); // TODO: Make enum size changeable
|
||||||
|
}
|
||||||
|
bail!("Unknown type '{name}'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f(self, program, false)
|
||||||
}
|
}
|
||||||
pub fn size_of_allow_unsized_arrays(&self, program: &Program) -> anyhow::Result<usize> {
|
pub fn size_of_allow_unsized_arrays(&self, program: &Program) -> anyhow::Result<usize> {
|
||||||
match self.size_of(program) {
|
match self.size_of(program) {
|
||||||
@@ -72,6 +335,9 @@ impl Type {
|
|||||||
impl Display for Type {
|
impl Display for Type {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
Self::FnPtr(func) => {
|
||||||
|
write!(f, "{func}")
|
||||||
|
}
|
||||||
Self::Ref { inner, mutable } => {
|
Self::Ref { inner, mutable } => {
|
||||||
if *mutable {
|
if *mutable {
|
||||||
write!(f, "&mut {inner}")
|
write!(f, "&mut {inner}")
|
||||||
@@ -87,13 +353,16 @@ impl Display for Type {
|
|||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
write!(f, "[{inner}; {p}]")
|
write!(f, "[{inner}; {p}]")
|
||||||
}
|
}
|
||||||
Expr::Literal(Literal::Number(n)) => {
|
Expr::Literal(_, Literal::Number(n)) => {
|
||||||
write!(f, "[{inner}; {n}]")
|
write!(f, "[{inner}; {n}]")
|
||||||
}
|
}
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Owned(ident) => write!(f, "{ident}")
|
Self::Owned(ident) => write!(f, "{ident}"),
|
||||||
|
Self::Builtin { name, size, signed } => {
|
||||||
|
write!(f, "(builtin) {} {}({})", name, if *signed { "signed" } else { "unsigned" }, size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
use crate::{common::loc::LocBox, debug, error, lerror, parser::{typ::parse_type, Punctuation}, tokeniser::Token};
|
use crate::{cli::CliArgs, common::loc::LocBox, debug, lerror, parser::{Punctuation, ast::{Program, expr::StructLit}, typ::parse_type}, tokeniser::Token};
|
||||||
|
|
||||||
use super::{ast::{expr::{Block, CallParams, Expr, IfBranchExpr, IfExpr, Path}, literal::Literal, TokenType}, parse_item, utils, Delimiter, Keyword};
|
use super::{ast::{expr::{Block, CallParams, Expr, IfBranchExpr, IfExpr, Path}, literal::Literal, TokenType}, parse_item, utils, Delimiter, Keyword};
|
||||||
|
|
||||||
@@ -31,20 +31,25 @@ const BINOP_LIST: &[TokenType] = &[
|
|||||||
TokenType::Punct(Punctuation::XorEq),
|
TokenType::Punct(Punctuation::XorEq),
|
||||||
TokenType::Punct(Punctuation::Eq),
|
TokenType::Punct(Punctuation::Eq),
|
||||||
TokenType::Punct(Punctuation::EqEq),
|
TokenType::Punct(Punctuation::EqEq),
|
||||||
|
TokenType::Punct(Punctuation::Neq),
|
||||||
TokenType::Punct(Punctuation::Lt),
|
TokenType::Punct(Punctuation::Lt),
|
||||||
TokenType::Punct(Punctuation::Gt),
|
TokenType::Punct(Punctuation::Gt),
|
||||||
TokenType::Punct(Punctuation::Le),
|
TokenType::Punct(Punctuation::Le),
|
||||||
TokenType::Punct(Punctuation::Ge),
|
TokenType::Punct(Punctuation::Ge),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool) -> Result<Option<LocBox<Expr>>> {
|
pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool, cli: &CliArgs, prog: &mut Program) -> Result<Option<LocBox<Expr>>> {
|
||||||
let res = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenL)) {
|
let res = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenL)) {
|
||||||
Some(parse_group(tokens)?)
|
Some(parse_group(tokens, cli, prog)?)
|
||||||
|
} else
|
||||||
|
if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Self_)) {
|
||||||
|
Some(LocBox::new(kw.loc(), Expr::Self_ { strct: None }))
|
||||||
} else
|
} else
|
||||||
if let Some(_) = utils::check(tokens, TokenType::ident("")) {
|
if let Some(_) = utils::check(tokens, TokenType::ident("")) {
|
||||||
|
debug!("is ident");
|
||||||
let p = parse_path(tokens)?;
|
let p = parse_path(tokens)?;
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||||
Some(parse_struct_literal(tokens, p.inner().unwrap_path())?)
|
Some(parse_struct_literal(tokens, p.inner().unwrap_path(), cli, prog)?)
|
||||||
} else {
|
} else {
|
||||||
Some(p)
|
Some(p)
|
||||||
}
|
}
|
||||||
@@ -56,29 +61,33 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
|
|||||||
TokenType::Punct(Punctuation::Ampersand),
|
TokenType::Punct(Punctuation::Ampersand),
|
||||||
TokenType::Punct(Punctuation::Star),
|
TokenType::Punct(Punctuation::Star),
|
||||||
]) {
|
]) {
|
||||||
Some(parse_unop(tokens)?)
|
Some(parse_unop(tokens, cli, prog)?)
|
||||||
} else
|
} else
|
||||||
if let Some(_) = utils::check_from_many(tokens, &[
|
if let Some(_) = utils::check_from_many(tokens, &[
|
||||||
TokenType::string("", false),
|
TokenType::string("", false),
|
||||||
TokenType::number(0, 0, false),
|
TokenType::number(0, 0, false),
|
||||||
TokenType::char('\0'),
|
TokenType::char('\0'),
|
||||||
TokenType::Delim(Delimiter::SquareL),
|
TokenType::Delim(Delimiter::SquareL),
|
||||||
|
TokenType::Keyword(Keyword::True),
|
||||||
|
TokenType::Keyword(Keyword::False)
|
||||||
]) {
|
]) {
|
||||||
Some(parse_literal(tokens)?)
|
Some(parse_literal(tokens, cli, prog)?)
|
||||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::While)) {
|
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::While)) {
|
||||||
return Ok(Some(parse_while_loop(tokens)?));
|
return Ok(Some(parse_while_loop(tokens, cli, prog)?));
|
||||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::For)) {
|
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::For)) {
|
||||||
return Ok(Some(parse_for_loop(tokens)?));
|
return Ok(Some(parse_for_loop(tokens, cli, prog)?));
|
||||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Loop)) {
|
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Loop)) {
|
||||||
return Ok(Some(parse_inf_loop(tokens)?));
|
return Ok(Some(parse_inf_loop(tokens, cli, prog)?));
|
||||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Return)) {
|
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Return)) {
|
||||||
return Ok(Some(parse_return(tokens)?));
|
return Ok(Some(parse_return(tokens, cli, prog)?));
|
||||||
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Break)) {
|
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Break)) {
|
||||||
|
let _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||||
return Ok(Some(LocBox::new(kw.loc(), Expr::Break)));
|
return Ok(Some(LocBox::new(kw.loc(), Expr::Break)));
|
||||||
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Continue)) {
|
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Continue)) {
|
||||||
|
let _ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||||
return Ok(Some(LocBox::new(kw.loc(), Expr::Continue)));
|
return Ok(Some(LocBox::new(kw.loc(), Expr::Continue)));
|
||||||
} else if let Some(kw) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
} else if let Some(kw) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
||||||
return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens)?))));
|
return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens, cli, prog)?))));
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -92,17 +101,21 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
|
|||||||
res = parse_ptr_field_access(tokens, res)?;
|
res = parse_ptr_field_access(tokens, res)?;
|
||||||
}
|
}
|
||||||
if utils::check(tokens, TokenType::Delim(Delimiter::ParenL)).is_some() {
|
if utils::check(tokens, TokenType::Delim(Delimiter::ParenL)).is_some() {
|
||||||
res = parse_fn_call(tokens, res)?;
|
res = parse_fn_call(tokens, res, cli, prog)?;
|
||||||
}
|
}
|
||||||
if utils::check(tokens, TokenType::Keyword(Keyword::As)).is_some() {
|
if utils::check(tokens, TokenType::Keyword(Keyword::As)).is_some() {
|
||||||
res = parse_cast(tokens, res)?;
|
res = parse_cast(tokens, res)?;
|
||||||
}
|
}
|
||||||
if utils::check(tokens, TokenType::Delim(Delimiter::SquareL)).is_some() {
|
if utils::check(tokens, TokenType::Delim(Delimiter::SquareL)).is_some() {
|
||||||
res = parse_array_index(tokens, res)?;
|
res = parse_array_index(tokens, res, cli, prog)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = utils::check_from_many(tokens, BINOP_LIST) {
|
if let Some(_) = utils::check_from_many(tokens, BINOP_LIST) {
|
||||||
return Ok(Some(parse_binop(tokens, res, precedence)?));
|
let v = parse_binop(tokens, res, precedence, cli, prog)?;
|
||||||
|
if consume_semi {
|
||||||
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||||
|
}
|
||||||
|
return Ok(Some(v));
|
||||||
} else {
|
} else {
|
||||||
if consume_semi {
|
if consume_semi {
|
||||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||||
@@ -110,12 +123,15 @@ pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool
|
|||||||
return Ok(Some(res));
|
return Ok(Some(res));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if consume_semi {
|
||||||
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||||
|
}
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_return(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
fn parse_return(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Return), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Return), "")?;
|
||||||
let item = parse_expr(tokens, 0, true)?;
|
let item = parse_expr(tokens, 0, true, cli, prog)?;
|
||||||
Ok(LocBox::new(kw.loc(), Expr::Return(Box::new(item))))
|
Ok(LocBox::new(kw.loc(), Expr::Return(Box::new(item))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,18 +143,20 @@ fn parse_cast(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr
|
|||||||
right: Box::new(typ)
|
right: Box::new(typ)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
|
fn parse_if(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<IfExpr> {
|
||||||
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::If), "")?;
|
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::If), "")?;
|
||||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||||
|
let Some(test) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||||
lerror!(loc.loc(), "Expected test for if statement, got nothing");
|
lerror!(loc.loc(), "Expected test for if statement, got nothing");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
|
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||||
let block = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
let block = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||||
if let Some(_) = utils::check_2_last(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
if let Some(_) = utils::check_2_last(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR));
|
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR));
|
||||||
Block(Vec::new())
|
Block(Vec::new())
|
||||||
} else {
|
} else {
|
||||||
parse_block(tokens)?
|
parse_block(tokens, cli, prog)?
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lerror!(loc.loc(), "Expected '{{'");
|
lerror!(loc.loc(), "Expected '{{'");
|
||||||
@@ -146,14 +164,14 @@ fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
|
|||||||
};
|
};
|
||||||
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Else)) {
|
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Else)) {
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
||||||
let branch = IfBranchExpr::ElseIf(Box::new(parse_if(tokens)?));
|
let branch = IfBranchExpr::ElseIf(Box::new(parse_if(tokens, cli, prog)?));
|
||||||
Ok(IfExpr {
|
Ok(IfExpr {
|
||||||
test: Box::new(test),
|
test: Box::new(test),
|
||||||
body: block,
|
body: block,
|
||||||
else_if: Some(branch)
|
else_if: Some(branch)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let branch = IfBranchExpr::Else(parse_block(tokens)?);
|
let branch = IfBranchExpr::Else(parse_block(tokens, cli, prog)?);
|
||||||
Ok(IfExpr {
|
Ok(IfExpr {
|
||||||
test: Box::new(test),
|
test: Box::new(test),
|
||||||
body: block,
|
body: block,
|
||||||
@@ -168,35 +186,39 @@ fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn parse_while_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
fn parse_while_loop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::While), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::While), "")?;
|
||||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||||
|
let Some(test) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||||
lerror!(kw.loc(), "Expected test comparrison for while loop, got nothing");
|
lerror!(kw.loc(), "Expected test comparrison for while loop, got nothing");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
let block = parse_block(tokens)?;
|
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||||
|
let block = parse_block(tokens, cli, prog)?;
|
||||||
Ok(LocBox::new(kw.loc(), Expr::WhileLoop {
|
Ok(LocBox::new(kw.loc(), Expr::WhileLoop {
|
||||||
test: Box::new(test),
|
test: Box::new(test),
|
||||||
body: block
|
body: block
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
fn parse_for_loop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::For), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::For), "")?;
|
||||||
let Some(pre) = parse_item(tokens)? else {
|
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||||
|
let Some(pre) = parse_item(tokens, cli, prog)? else {
|
||||||
lerror!(kw.loc(), "Expected init stat for a for loop, got nothing");
|
lerror!(kw.loc(), "Expected init stat for a for loop, got nothing");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
// Semicolon parsed out by parse_item above
|
// Semicolon parsed out by parse_item above
|
||||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
let Some(test) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||||
lerror!(kw.loc(), "Expected test comparrison for a for loop, got nothing");
|
lerror!(kw.loc(), "Expected test comparrison for a for loop, got nothing");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "");
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "");
|
||||||
let Some(post) = parse_expr(tokens, 0, false)? else {
|
let Some(post) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||||
lerror!(kw.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing");
|
lerror!(kw.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
let block = parse_block(tokens)?;
|
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||||
|
let block = parse_block(tokens, cli, prog)?;
|
||||||
|
|
||||||
Ok(LocBox::new(kw.loc(), Expr::ForLoop {
|
Ok(LocBox::new(kw.loc(), Expr::ForLoop {
|
||||||
init: Box::new(pre),
|
init: Box::new(pre),
|
||||||
@@ -205,12 +227,21 @@ fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
|||||||
body: block
|
body: block
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
fn parse_inf_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
fn parse_inf_loop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Loop), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Loop), "")?;
|
||||||
let block = parse_block(tokens)?;
|
let block = parse_block(tokens, cli, prog)?;
|
||||||
Ok(LocBox::new(kw.loc(), Expr::InfLoop { body: block }))
|
Ok(LocBox::new(kw.loc(), Expr::InfLoop { body: block }))
|
||||||
}
|
}
|
||||||
fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
|
fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
|
match left.inner() {
|
||||||
|
Expr::FieldAccess { .. } |
|
||||||
|
Expr::PtrFieldAccess { .. } => {
|
||||||
|
return parse_member_function_call(tokens, left, cli, prog);
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
@@ -218,7 +249,7 @@ fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<E
|
|||||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let Some(param) = parse_expr(tokens, 0, false)? else {break};
|
let Some(param) = parse_expr(tokens, 0, false, cli, prog)? else {break};
|
||||||
params.push(param);
|
params.push(param);
|
||||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||||
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||||
@@ -230,9 +261,9 @@ fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<E
|
|||||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
|
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
|
||||||
Ok(LocBox::new(start.loc(), Expr::Call { path: Box::new(left), params: CallParams(params) }))
|
Ok(LocBox::new(start.loc(), Expr::Call { path: Box::new(left), params: CallParams(params) }))
|
||||||
}
|
}
|
||||||
fn parse_array_index(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
|
fn parse_array_index(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareL), "")?;
|
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareL), "")?;
|
||||||
let Some(idx) = parse_expr(tokens, 0, false)? else {
|
let Some(idx) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||||
lerror!(start.loc(), "Expected index for in array index but found nothing.");
|
lerror!(start.loc(), "Expected index for in array index but found nothing.");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
@@ -257,7 +288,36 @@ fn parse_field_access(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<Loc
|
|||||||
};
|
};
|
||||||
Ok(LocBox::new(start.loc(), Expr::FieldAccess {
|
Ok(LocBox::new(start.loc(), Expr::FieldAccess {
|
||||||
left: Box::new(Some(left)),
|
left: Box::new(Some(left)),
|
||||||
right: Box::new(right)
|
right: Box::new(right),
|
||||||
|
offset: 0,
|
||||||
|
l_typ: None,
|
||||||
|
r_typ: None
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_member_function_call(tokens: &mut Vec<Token>, left: LocBox<Expr>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
|
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "unreachable")?;
|
||||||
|
let mut params = Vec::new();
|
||||||
|
|
||||||
|
while !tokens.is_empty() {
|
||||||
|
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let Some(param) = parse_expr(tokens, 0, false, cli, prog)? else {break};
|
||||||
|
params.push(param);
|
||||||
|
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||||
|
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||||
|
lerror!(&utils::get_last_loc(), "Expected ',' or ')' but didnt find either");
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
|
||||||
|
|
||||||
|
Ok(LocBox::new(start.loc(), Expr::MethodCall {
|
||||||
|
left: Box::new(left),
|
||||||
|
strct: None,
|
||||||
|
params: CallParams(params)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,35 +334,61 @@ fn parse_ptr_field_access(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result
|
|||||||
};
|
};
|
||||||
Ok(LocBox::new(start.loc(), Expr::PtrFieldAccess {
|
Ok(LocBox::new(start.loc(), Expr::PtrFieldAccess {
|
||||||
left: Box::new(Some(left)),
|
left: Box::new(Some(left)),
|
||||||
right: Box::new(right)
|
right: Box::new(right),
|
||||||
|
offset: 0,
|
||||||
|
l_typ: None,
|
||||||
|
r_typ: None
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
fn parse_literal(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
|
if let Some(tkn) = utils::check_consume(tokens, TokenType::Keyword(Keyword::True)) {
|
||||||
|
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Bool(true))));
|
||||||
|
} else
|
||||||
|
if let Some(tkn) = utils::check_consume(tokens, TokenType::Keyword(Keyword::False)) {
|
||||||
|
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Bool(false))));
|
||||||
|
} else
|
||||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::string("", false)) {
|
if let Some(tkn) = utils::check_consume(tokens, TokenType::string("", false)) {
|
||||||
let TokenType::String(str) = tkn.tt() else {unreachable!()};
|
let TokenType::String(str) = tkn.tt() else {unreachable!()};
|
||||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(Literal::String(str.clone()))));
|
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::String(str.clone()))));
|
||||||
} else
|
} else
|
||||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::number(0, 0, false)) {
|
if let Some(tkn) = utils::check_consume(tokens, TokenType::number(0, 0, false)) {
|
||||||
let TokenType::Number(val) = tkn.tt() else {unreachable!()};
|
let TokenType::Number(val) = tkn.tt() else {unreachable!()};
|
||||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(Literal::Number(val.clone()))));
|
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Number(val.clone()))));
|
||||||
} else
|
} else
|
||||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::char('\0')) {
|
if let Some(tkn) = utils::check_consume(tokens, TokenType::char('\0')) {
|
||||||
let TokenType::Char(val) = tkn.tt() else {unreachable!()};
|
let TokenType::Char(val) = tkn.tt() else {unreachable!()};
|
||||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(Literal::Char(val.clone()))));
|
return Ok(LocBox::new(tkn.loc(), Expr::Literal(String::new(), Literal::Char(val.clone()))));
|
||||||
} else
|
} else
|
||||||
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) {
|
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) {
|
||||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareR)) {
|
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareR)) {
|
||||||
return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::Array(Vec::new()))));
|
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array { items: Vec::new(), item_size: None })));
|
||||||
}
|
}
|
||||||
if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
|
/*if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
|
||||||
let first = parse_expr(tokens, 0, false)?;
|
|
||||||
let Some(first) = first else { unreachable!() };
|
} else */
|
||||||
|
if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
|
||||||
|
let Some(typ) = parse_expr(tokens, 0, true, cli, prog)? else {
|
||||||
|
lerror!(start.loc(), "Expected value, found nothing");
|
||||||
|
bail!("")
|
||||||
|
};
|
||||||
|
let count = parse_expr(tokens, 0, false, cli, prog)?.unwrap();
|
||||||
|
|
||||||
|
let Expr::Literal(_, Literal::Number(count)) = count.inner() else {
|
||||||
|
lerror!(count.loc(), "a repeating array accepts only literal numbers for count argument");
|
||||||
|
bail!("")
|
||||||
|
};
|
||||||
|
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||||
|
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::ArrayRepeat {
|
||||||
|
val: Box::new(typ),
|
||||||
|
count: count.val,
|
||||||
|
item_size: None,
|
||||||
|
})));
|
||||||
|
} else {
|
||||||
|
|
||||||
let mut values = Vec::new();
|
let mut values = Vec::new();
|
||||||
values.push(first);
|
|
||||||
while !tokens.is_empty() {
|
while !tokens.is_empty() {
|
||||||
let Some(val) = parse_expr(tokens, 0, false)? else{break};
|
let Some(val) = parse_expr(tokens, 0, false, cli, prog)? else{break};
|
||||||
|
|
||||||
values.push(val);
|
values.push(val);
|
||||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||||
@@ -310,53 +396,43 @@ fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||||
return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::Array(values))));
|
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array { items: values, item_size: None })));
|
||||||
} else if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
|
/*
|
||||||
let Some(typ) = parse_expr(tokens, 0, true)? else {
|
|
||||||
lerror!(start.loc(), "Expected value, found nothing");
|
|
||||||
bail!("")
|
|
||||||
};
|
|
||||||
let count = parse_expr(tokens, 0, false)?.unwrap();
|
|
||||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
|
||||||
return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::ArrayRepeat {
|
|
||||||
val: Box::new(typ),
|
|
||||||
count: Box::new(count)
|
|
||||||
})));
|
|
||||||
} else {
|
|
||||||
if let Some(curr) = tokens.last() {
|
if let Some(curr) = tokens.last() {
|
||||||
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found {}", curr.tt());
|
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found {}", curr.tt());
|
||||||
} else {
|
} else {
|
||||||
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found nothing");
|
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found nothing");
|
||||||
}
|
}
|
||||||
bail!("")
|
bail!("")
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path) -> Result<LocBox<Expr>> {
|
fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
||||||
let mut fields = HashMap::new();
|
let mut fields = BTreeMap::new();
|
||||||
while !tokens.is_empty() {
|
while !tokens.is_empty() {
|
||||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "wtf?")?;
|
||||||
let typ = parse_expr(tokens, 0, false)?.unwrap();
|
let typ = parse_expr(tokens, 0, false, cli, prog)?.unwrap();
|
||||||
fields.insert(name.tt().unwrap_ident(), typ);
|
fields.insert(name.tt().unwrap_ident(), typ);
|
||||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?;
|
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(LocBox::new(start.loc(), Expr::Struct { path: name, fields }))
|
Ok(LocBox::new(start.loc(), Expr::Struct(String::new(), StructLit { path: name, fields })))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_group(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
fn parse_group(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||||
let Some(expr) = parse_expr(tokens, 0, false)? else {
|
let Some(expr) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||||
lerror!(start.loc(), "Expected expr found nothing");
|
lerror!(start.loc(), "Expected expr found nothing");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
@@ -381,18 +457,18 @@ fn parse_path(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
|||||||
Ok(LocBox::new(part.loc(), Expr::Path(Path(buf))))
|
Ok(LocBox::new(part.loc(), Expr::Path(Path(buf))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_unop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
fn parse_unop(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
let typ = utils::check_consume_or_err_from_many(tokens, &[
|
let typ = utils::check_consume_or_err_from_many(tokens, &[
|
||||||
TokenType::Punct(Punctuation::Not),
|
TokenType::Punct(Punctuation::Not),
|
||||||
TokenType::Punct(Punctuation::Plus),
|
TokenType::Punct(Punctuation::Plus), // Make number positive
|
||||||
TokenType::Punct(Punctuation::Minus),
|
TokenType::Punct(Punctuation::Minus), // make number negative
|
||||||
TokenType::Punct(Punctuation::Ampersand),
|
TokenType::Punct(Punctuation::Ampersand),
|
||||||
TokenType::Punct(Punctuation::Star),
|
TokenType::Punct(Punctuation::Star),
|
||||||
], "")?;
|
], "")?;
|
||||||
let loc = typ.loc().clone();
|
let loc = typ.loc().clone();
|
||||||
let TokenType::Punct(typ) = typ.tt().clone() else {unreachable!()};
|
let TokenType::Punct(typ) = typ.tt().clone() else {unreachable!()};
|
||||||
|
|
||||||
let Some(right) = parse_expr(tokens, 5, false)? else {
|
let Some(right) = parse_expr(tokens, 5, false, cli, prog)? else {
|
||||||
lerror!(&loc, "Expected expression after unary token, found nothing");
|
lerror!(&loc, "Expected expression after unary token, found nothing");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
@@ -402,12 +478,12 @@ fn parse_unop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize) -> Result<LocBox<Expr>> {
|
fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Expr>> {
|
||||||
// TODO: https://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudocode
|
// TODO: https://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudocode
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let op_loc;
|
let op_loc;
|
||||||
let op = match tokens.last() {
|
let mut op = match tokens.last() {
|
||||||
Some(op) if BINOP_LIST.contains(&op.tt()) => {
|
Some(op) if BINOP_LIST.contains(&op.tt()) => {
|
||||||
op_loc = op.loc().clone();
|
op_loc = op.loc().clone();
|
||||||
let TokenType::Punct(op) = op.tt() else {unreachable!()};
|
let TokenType::Punct(op) = op.tt() else {unreachable!()};
|
||||||
@@ -426,18 +502,34 @@ fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize
|
|||||||
Some(_) |
|
Some(_) |
|
||||||
None => break,
|
None => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_ = tokens.pop();
|
||||||
|
|
||||||
|
// Fix for parsing multiple refs in types and references being parsed as && (logical and)
|
||||||
|
if op == Punctuation::Ampersand {
|
||||||
|
if let Some(tok) = tokens.last() {
|
||||||
|
match tok.tt() {
|
||||||
|
TokenType::Punct(Punctuation::Ampersand) => {
|
||||||
|
_ = tokens.pop();
|
||||||
|
op = Punctuation::AndAnd;
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug!("OP: {op:?}");
|
debug!("OP: {op:?}");
|
||||||
let (lp, rp) = op.precedence().unwrap();
|
let (lp, rp) = op.precedence().unwrap();
|
||||||
if lp < precedence {
|
if lp < precedence {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = tokens.pop();
|
let Some(rhs) = parse_expr(tokens, rp, false, cli, prog)? else {break;};
|
||||||
let Some(rhs) = parse_expr(tokens, rp, false)? else {break;};
|
|
||||||
lhs = LocBox::new(&op_loc, Expr::BinOp {
|
lhs = LocBox::new(&op_loc, Expr::BinOp {
|
||||||
typ: op,
|
typ: op,
|
||||||
left: Box::new(lhs),
|
left: Box::new(lhs),
|
||||||
right: Box::new(rhs)
|
right: Box::new(rhs),
|
||||||
|
signed: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,14 +538,14 @@ fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_block(tokens: &mut Vec<Token>) -> Result<Block> {
|
pub fn parse_block(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Block> {
|
||||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
while !tokens.is_empty() {
|
while !tokens.is_empty() {
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if let Some(item) = parse_item(tokens)? {
|
if let Some(item) = parse_item(tokens, cli, prog)? {
|
||||||
items.push(item);
|
items.push(item);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -2,45 +2,51 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use ast::{expr::Block, Ast, Program};
|
use ast::{expr::Block, Ast, Program};
|
||||||
|
|
||||||
use crate::tokeniser::{Token, tokentype::*};
|
use crate::{cli::CliArgs, tokeniser::{Token, tokentype::*}};
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
mod expr;
|
pub mod expr;
|
||||||
mod stat;
|
pub mod stat;
|
||||||
mod utils;
|
pub mod utils;
|
||||||
pub mod typ;
|
pub mod typ;
|
||||||
|
|
||||||
type Result<T> = anyhow::Result<T>;
|
type Result<T> = anyhow::Result<T>;
|
||||||
|
|
||||||
pub fn parse_program(mut tokens: Vec<Token>) -> Result<Program> {
|
pub fn parse_program(mut tokens: Vec<Token>, cli: &CliArgs) -> Result<Program> {
|
||||||
let mut prog_body = Vec::new();
|
let mut prog_body = Vec::new();
|
||||||
|
let mut prog = Program {
|
||||||
while !tokens.is_empty() {
|
ast: Block(prog_body.clone()),
|
||||||
if let Some(item) = parse_item(&mut tokens)? {
|
|
||||||
prog_body.push(item);
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Ok(Program {
|
|
||||||
ast: Block(prog_body),
|
|
||||||
enums: HashMap::new(),
|
enums: HashMap::new(),
|
||||||
functions: HashMap::new(),
|
functions: HashMap::new(),
|
||||||
member_functions: HashMap::new(),
|
member_functions: HashMap::new(),
|
||||||
types: HashMap::new(),
|
types: HashMap::new(),
|
||||||
structs: HashMap::new(),
|
structs: HashMap::new(),
|
||||||
static_vars: HashMap::new(),
|
static_vars: HashMap::new(),
|
||||||
const_vars: HashMap::new()
|
const_vars: HashMap::new(),
|
||||||
})
|
literals: HashMap::new(),
|
||||||
|
struct_literals: HashMap::new(),
|
||||||
|
scope: None,
|
||||||
|
curr_fn_args: HashMap::new(),
|
||||||
|
curr_struct: None,
|
||||||
|
included_files: Vec::new()
|
||||||
|
};
|
||||||
|
while !tokens.is_empty() {
|
||||||
|
if let Some(item) = parse_item(&mut tokens, cli, &mut prog)? {
|
||||||
|
prog_body.push(item);
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prog.ast = Block(prog_body);
|
||||||
|
Ok(prog)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_item(tokens: &mut Vec<Token>) -> Result<Option<Ast>> {
|
fn parse_item(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Option<Ast>> {
|
||||||
if let Some(stat) = stat::parse_statement(tokens)? {
|
if let Some(stat) = stat::parse_statement(tokens, cli, prog)? {
|
||||||
return Ok(Some(Ast::Statement(stat)));
|
return Ok(Some(Ast::Statement(stat)));
|
||||||
}
|
}
|
||||||
if let Some(expr) = expr::parse_expr(tokens, 0, true)? {
|
if let Some(expr) = expr::parse_expr(tokens, 0, true, cli, prog)? {
|
||||||
return Ok(Some(Ast::Expr(expr)));
|
return Ok(Some(Ast::Expr(expr)));
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
|
|
||||||
|
use crate::cli::CliArgs;
|
||||||
use crate::common::loc::LocBox;
|
use crate::common::loc::LocBox;
|
||||||
use crate::lerror;
|
use crate::{error, lerror};
|
||||||
use crate::parser::ast::TokenType;
|
use crate::parser::ast::{Program, TString, TokenType};
|
||||||
|
use crate::parser::ast::statement::Let;
|
||||||
use crate::parser::expr::parse_expr;
|
use crate::parser::expr::parse_expr;
|
||||||
use crate::parser::{Delimiter, Ident, Keyword, Punctuation};
|
use crate::parser::{Delimiter, Ident, Keyword, Punctuation};
|
||||||
use crate::tokeniser::Token;
|
use crate::tokeniser::Token;
|
||||||
@@ -14,18 +16,18 @@ use super::ast::statement::{ConstVar, Enum, Function, Statement, StaticVar, Stru
|
|||||||
|
|
||||||
type Result<T> = anyhow::Result<T>;
|
type Result<T> = anyhow::Result<T>;
|
||||||
|
|
||||||
pub fn parse_statement(tokens: &mut Vec<Token>) -> Result<Option<LocBox<Statement>>> {
|
pub fn parse_statement(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Option<LocBox<Statement>>> {
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
if let Some(_) = utils::check_from_many(tokens, &[TokenType::Keyword(Keyword::Fn), TokenType::Keyword(Keyword::Extern)]) {
|
||||||
Ok(Some(parse_fn(tokens)?))
|
Ok(Some(parse_fn(tokens, cli, prog)?))
|
||||||
} else
|
} else
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Type)) {
|
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Type)) {
|
||||||
Ok(Some(parse_type_alias(tokens)?))
|
Ok(Some(parse_type_alias(tokens)?))
|
||||||
} else
|
} else
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Const)) {
|
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Const)) {
|
||||||
Ok(Some(parse_constant(tokens)?))
|
Ok(Some(parse_constant(tokens, cli, prog)?))
|
||||||
} else
|
} else
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Static)) {
|
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Static)) {
|
||||||
Ok(Some(parse_static(tokens)?))
|
Ok(Some(parse_static(tokens, cli, prog)?))
|
||||||
} else
|
} else
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Struct)) {
|
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Struct)) {
|
||||||
Ok(Some(parse_struct(tokens)?))
|
Ok(Some(parse_struct(tokens)?))
|
||||||
@@ -34,13 +36,50 @@ pub fn parse_statement(tokens: &mut Vec<Token>) -> Result<Option<LocBox<Statemen
|
|||||||
Ok(Some(parse_enum(tokens)?))
|
Ok(Some(parse_enum(tokens)?))
|
||||||
} else
|
} else
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) {
|
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) {
|
||||||
Ok(Some(parse_let(tokens)?))
|
Ok(Some(parse_let(tokens, cli, prog)?))
|
||||||
|
} else
|
||||||
|
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Include)) {
|
||||||
|
Ok(Some(parse_include(tokens, cli, prog)?))
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_enum(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
pub fn parse_include(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
|
||||||
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Include), "")?;
|
||||||
|
let TokenType::String(include_path) = utils::check_consume_or_err(tokens, TokenType::String(TString::default()), "")?.tt().clone() else {panic!()};
|
||||||
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||||
|
let cwd = std::env::current_dir()?;
|
||||||
|
let mut found = false;
|
||||||
|
for p in &cli.include_paths {
|
||||||
|
let path = cwd.join(p);
|
||||||
|
let path = path.join(&include_path.val);
|
||||||
|
if path.exists() {
|
||||||
|
// This is the first path that the include found the file, so if its already included,
|
||||||
|
// skip it. Because the context is per file specified in the cli (not includes), that
|
||||||
|
// means weird includes (probably) wont clash
|
||||||
|
if prog.included_files.contains(&path.to_string_lossy().to_string()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prog.included_files.push(path.to_string_lossy().to_string().clone());
|
||||||
|
let data = std::fs::read_to_string(&path)?;
|
||||||
|
let mut tokens_imp = crate::tokeniser::tokenise(&data, &path.to_string_lossy().to_string())?;
|
||||||
|
tokens.append(&mut tokens_imp);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
lerror!(kw.loc(), "Could not find file {:?}", include_path.val);
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LocBox::new(kw.loc(), Statement::Include))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_enum(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Enum), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Enum), "")?;
|
||||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyL));
|
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyL));
|
||||||
@@ -64,7 +103,7 @@ fn parse_enum(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
|||||||
Ok(LocBox::new(kw.loc(), Statement::Enum(Enum { name, fields })))
|
Ok(LocBox::new(kw.loc(), Statement::Enum(Enum { name, fields })))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_struct(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
pub fn parse_struct(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Struct), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Struct), "")?;
|
||||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyL));
|
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyL));
|
||||||
@@ -90,14 +129,14 @@ fn parse_struct(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
|||||||
Ok(LocBox::new(kw.loc(), Statement::Struct(Struct { name, fields })))
|
Ok(LocBox::new(kw.loc(), Statement::Struct(Struct { name, fields })))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_static(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
pub fn parse_static(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Static), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Static), "")?;
|
||||||
|
|
||||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||||
let typ = parse_type(tokens)?;
|
let typ = parse_type(tokens)?;
|
||||||
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||||
let Some(val) = parse_expr(tokens, 0, false)? else {
|
let Some(val) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||||
lerror!(eq.loc(), "Expected expression found nothing");
|
lerror!(eq.loc(), "Expected expression found nothing");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
@@ -105,7 +144,7 @@ fn parse_static(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
|||||||
Ok(LocBox::new(kw.loc(), Statement::StaticVar(StaticVar { name, typ, val })))
|
Ok(LocBox::new(kw.loc(), Statement::StaticVar(StaticVar { name, typ, val })))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_let(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
pub fn parse_let(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Let), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Let), "")?;
|
||||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||||
let mut typ = None;
|
let mut typ = None;
|
||||||
@@ -114,16 +153,16 @@ fn parse_let(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
|||||||
typ = Some(parse_type(tokens)?);
|
typ = Some(parse_type(tokens)?);
|
||||||
}
|
}
|
||||||
if let Some(eq) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Eq)) {
|
if let Some(eq) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Eq)) {
|
||||||
let Some(_val) = parse_expr(tokens, 0, false)? else {
|
let Some(_val) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||||
lerror!(eq.loc(), "Expected expression found nothing");
|
lerror!(eq.loc(), "Expected expression found nothing");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
val = Some(_val);
|
val = Some(_val);
|
||||||
}
|
}
|
||||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||||
Ok(LocBox::new(kw.loc(), Statement::Let { name, typ, val }))
|
Ok(LocBox::new(kw.loc(), Statement::Let(Let{ name, typ, val })))
|
||||||
}
|
}
|
||||||
fn parse_constant(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
pub fn parse_constant(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Const), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Const), "")?;
|
||||||
|
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
||||||
@@ -133,7 +172,7 @@ fn parse_constant(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
|||||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||||
let typ = parse_type(tokens)?;
|
let typ = parse_type(tokens)?;
|
||||||
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||||
let Some(val) = parse_expr(tokens, 0, false)? else {
|
let Some(val) = parse_expr(tokens, 0, false, cli, prog)? else {
|
||||||
lerror!(eq.loc(), "Expected expression found nothing");
|
lerror!(eq.loc(), "Expected expression found nothing");
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
@@ -141,7 +180,7 @@ fn parse_constant(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
|||||||
Ok(LocBox::new(kw.loc(), Statement::ConstVar(ConstVar { name, typ, val })))
|
Ok(LocBox::new(kw.loc(), Statement::ConstVar(ConstVar { name, typ, val })))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_type_alias(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
pub fn parse_type_alias(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Type), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Type), "")?;
|
||||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||||
@@ -151,10 +190,21 @@ fn parse_type_alias(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
|||||||
Ok(LocBox::new(kw.loc(), Statement::TypeAlias(TypeAlias { name, typ })))
|
Ok(LocBox::new(kw.loc(), Statement::TypeAlias(TypeAlias { name, typ })))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_fn(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
pub fn parse_fn(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<LocBox<Statement>> {
|
||||||
|
error!("fnc");
|
||||||
|
let mut qual_extern = None;
|
||||||
|
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Extern)) {
|
||||||
|
if let Some(tok) = utils::check_consume(tokens, TokenType::string("", false)) {
|
||||||
|
if let TokenType::String(str) = tok.tt() {
|
||||||
|
qual_extern = Some(str.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qual_extern = Some(TString::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Just remove the kw since we checked it before
|
// Just remove the kw since we checked it before
|
||||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?;
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?;
|
||||||
|
|
||||||
let mut struct_name = None;
|
let mut struct_name = None;
|
||||||
let mut name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
let mut name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||||
// Check if this is a struct method
|
// Check if this is a struct method
|
||||||
@@ -162,44 +212,66 @@ fn parse_fn(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
|||||||
struct_name = Some(name);
|
struct_name = Some(name);
|
||||||
name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||||
}
|
}
|
||||||
let params = parse_fn_params(tokens)?;
|
let mut params_partial = parse_fn_params(tokens)?;
|
||||||
|
|
||||||
// Check for return type cause it optional
|
// Check for return type cause it optional
|
||||||
let mut ret_type = None;
|
let mut ret_type = None;
|
||||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||||
ret_type = Some(parse_type(tokens)?);
|
ret_type = Some(Box::new(parse_type(tokens)?));
|
||||||
}
|
}
|
||||||
let body;
|
let body;
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||||
body = Some(parse_block(tokens)?);
|
body = Some(parse_block(tokens, cli, prog)?);
|
||||||
} else {
|
} else {
|
||||||
// Check if its just a declaration
|
// Check if its just a declaration
|
||||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||||
body = None;
|
body = None;
|
||||||
}
|
}
|
||||||
|
let mut params = Vec::new();
|
||||||
|
if let Some(name) = &struct_name {
|
||||||
|
match params_partial.0 {
|
||||||
|
1 => params.push((Ident(String::from("self")), LocBox::new(kw.loc(), Type::Owned(name.clone()).as_ref()))),
|
||||||
|
2 => params.push((Ident(String::from("self")), LocBox::new(kw.loc(), Type::Owned(name.clone()).as_ref_mut()))),
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.append(&mut params_partial.1);
|
||||||
Ok(LocBox::new(kw.loc(), Statement::Fn(Function{
|
Ok(LocBox::new(kw.loc(), Statement::Fn(Function{
|
||||||
struct_name,
|
struct_name,
|
||||||
name,
|
name: Some(name),
|
||||||
params,
|
params,
|
||||||
ret_type,
|
ret_type,
|
||||||
qual_const: false,
|
qual_const: false,
|
||||||
qual_extern: None,
|
qual_extern,
|
||||||
body,
|
body,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// usize is: 0 = no self, static; 1 = self, ref; 2 = self, mut ref
|
||||||
fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, LocBox<Type>)>> {
|
pub fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<(usize, Vec<(Ident, LocBox<Type>)>)> {
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
|
|
||||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||||
|
|
||||||
|
let mut dis = 0;
|
||||||
|
|
||||||
|
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Ampersand)) {
|
||||||
|
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Mut)) {
|
||||||
|
_ = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Self_), "Expected 'this' keyword after &mut")?;
|
||||||
|
dis = 1
|
||||||
|
} else {
|
||||||
|
_ = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Self_), "Expected 'this' keyword after &")?;
|
||||||
|
dis = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
while !tokens.is_empty() {
|
while !tokens.is_empty() {
|
||||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||||
utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "huhhhhhhhh")?;
|
||||||
//dbg!(&name);
|
//dbg!(&name);
|
||||||
let typ = parse_type(tokens)?;
|
let typ = parse_type(tokens)?;
|
||||||
args.push((name.tt().unwrap_ident(), typ));
|
args.push((name.tt().unwrap_ident(), typ));
|
||||||
@@ -210,7 +282,7 @@ fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, LocBox<Type>)>
|
|||||||
}
|
}
|
||||||
|
|
||||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||||
Ok(args)
|
Ok((dis, args))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::{Result, bail};
|
||||||
|
|
||||||
use crate::{common::loc::LocBox, error, logger::log, parser::Delimiter, tokeniser::Token};
|
use crate::{cli::CliArgs, common::loc::LocBox, lerror, parser::{Delimiter, ast::{Ident, Program, expr::Expr, literal::Literal, statement::{Function, Statement}}, stat::parse_fn_params}, tokeniser::Token, validator::predefined::get_builtin_from_name};
|
||||||
|
|
||||||
use super::{ast::{typ::Type, TokenType}, expr::parse_expr, utils, Keyword, Punctuation};
|
use super::{ast::{typ::Type, TokenType}, expr::parse_expr, utils, Keyword, Punctuation};
|
||||||
|
|
||||||
@@ -19,16 +19,30 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut typ;
|
let mut typ;
|
||||||
|
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
||||||
|
let decl = parse_fn(tokens)?;
|
||||||
|
loc = Some(decl.loc().clone());
|
||||||
|
typ = Type::FnPtr(decl.inner().clone());
|
||||||
|
} else
|
||||||
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(super::Delimiter::SquareL)) {
|
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(super::Delimiter::SquareL)) {
|
||||||
if let None = loc {
|
if let None = loc {
|
||||||
loc = Some(start.loc().clone());
|
loc = Some(start.loc().clone());
|
||||||
}
|
}
|
||||||
let itm_typ = parse_type(tokens)?;
|
let itm_typ = parse_type(tokens)?;
|
||||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Semi)) {
|
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Semi)) {
|
||||||
let count = parse_expr(tokens, 0, false)?.unwrap();
|
let count = parse_expr(tokens, 0, false, &CliArgs::default(), &mut Program::default())?.unwrap();
|
||||||
|
|
||||||
|
match count.inner() {
|
||||||
|
Expr::Literal(_, Literal::Number(_)) |
|
||||||
|
Expr::Path(_) => (),
|
||||||
|
_ => {
|
||||||
|
lerror!(count.loc(), "Only literal numbers are allowed in sized arrays");
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
typ = Type::SizedArray {
|
typ = Type::SizedArray {
|
||||||
inner: Box::new(itm_typ.inner().clone()),
|
inner: Box::new(itm_typ.inner().clone()),
|
||||||
count
|
count: count
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
typ = Type::UnsizedArray {
|
typ = Type::UnsizedArray {
|
||||||
@@ -38,7 +52,11 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
|
|||||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||||
} else {
|
} else {
|
||||||
let ident = utils::check_consume_or_err(tokens, TokenType::ident(""), "a")?;
|
let ident = utils::check_consume_or_err(tokens, TokenType::ident(""), "a")?;
|
||||||
typ = Type::Owned(ident.tt().unwrap_ident());
|
if let Some(t) = get_builtin_from_name(&ident.tt().unwrap_ident().0) {
|
||||||
|
typ = t;
|
||||||
|
} else {
|
||||||
|
typ = Type::Owned(ident.tt().unwrap_ident());
|
||||||
|
}
|
||||||
if let None = loc {
|
if let None = loc {
|
||||||
loc = Some(ident.loc().clone());
|
loc = Some(ident.loc().clone());
|
||||||
}
|
}
|
||||||
@@ -62,3 +80,32 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
|
|||||||
}
|
}
|
||||||
Ok(LocBox::new(&loc.unwrap(), typ))
|
Ok(LocBox::new(&loc.unwrap(), typ))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_fn(tokens: &mut Vec<Token>) -> Result<LocBox<Function>> {
|
||||||
|
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?;
|
||||||
|
|
||||||
|
// disable member functions for types (for now)
|
||||||
|
// let mut struct_name = None;
|
||||||
|
// let mut name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||||
|
// Check if this is a struct method
|
||||||
|
// if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Fieldaccess)) {
|
||||||
|
// struct_name = Some(name);
|
||||||
|
// name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||||
|
// }
|
||||||
|
let params = parse_fn_params(tokens)?;
|
||||||
|
|
||||||
|
// Check for return type cause it optional
|
||||||
|
let mut ret_type = None;
|
||||||
|
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||||
|
ret_type = Some(Box::new(parse_type(tokens)?));
|
||||||
|
}
|
||||||
|
Ok(LocBox::new(kw.loc(),Function{
|
||||||
|
struct_name: None,
|
||||||
|
name: None,
|
||||||
|
params: params.1,
|
||||||
|
ret_type,
|
||||||
|
qual_const: false,
|
||||||
|
qual_extern: None,
|
||||||
|
body: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
use std::{fs::File, path::{Path, PathBuf}};
|
use std::{fs::File, path::{Path, PathBuf}};
|
||||||
use crate::{cli::CliArgs, parser::ast::Program};
|
use crate::{cli::CliArgs, parser::ast::Program};
|
||||||
|
|
||||||
mod x86_64;
|
pub mod x86_64;
|
||||||
mod none;
|
//mod none;
|
||||||
|
|
||||||
pub fn get_default_target() -> String {
|
pub fn get_default_target() -> String {
|
||||||
x86_64::cgen::linux::CGen::get_target_triple().to_string()
|
// x86_64::cgen::linux::CGen::get_target_triple().to_string()
|
||||||
|
x86_64::asmgen::linux::AsmGen::get_target_triple().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_targets() -> Vec<&'static str> {
|
pub fn get_all_targets() -> Vec<&'static str> {
|
||||||
vec![
|
vec![
|
||||||
// x86_64::asmgen::linux::AsmGen::get_target_triple(),
|
x86_64::asmgen::linux::AsmGen::get_target_triple(),
|
||||||
// x86_64::cgen::linux::CGen::get_target_triple(),
|
// x86_64::cgen::linux::CGen::get_target_triple(),
|
||||||
// x86_64::llvmgen::linux::LlvmGen::get_target_triple(),
|
// x86_64::llvmgen::linux::LlvmGen::get_target_triple(),
|
||||||
none::luagen::cct::LuaGen::get_target_triple(),
|
//none::luagen::cct::LuaGen::get_target_triple(),
|
||||||
x86_64::cgen::linux::CGen::get_target_triple(),
|
// x86_64::cgen::linux::CGen::get_target_triple(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_target_from_triple(triple: &str) -> Box<dyn Target> {
|
fn get_target_from_triple(triple: &str) -> Box<dyn Target> {
|
||||||
match triple {
|
match triple {
|
||||||
// _ if triple == x86_64::asmgen::linux::AsmGen::get_target_triple() => {
|
_ if triple == x86_64::asmgen::linux::AsmGen::get_target_triple() => {
|
||||||
// Box::new(x86_64::asmgen::linux::AsmGen::new())
|
Box::new(x86_64::asmgen::linux::AsmGen::new())
|
||||||
// }
|
|
||||||
_ if triple == x86_64::cgen::linux::CGen::get_target_triple() => {
|
|
||||||
Box::new(x86_64::cgen::linux::CGen::new())
|
|
||||||
}
|
}
|
||||||
|
// _ if triple == x86_64::cgen::linux::CGen::get_target_triple() => {
|
||||||
|
// Box::new(x86_64::cgen::linux::CGen::new())
|
||||||
|
// }
|
||||||
// _ if triple == x86_64::qbegen::linux::QbeGen::get_target_triple() => {
|
// _ if triple == x86_64::qbegen::linux::QbeGen::get_target_triple() => {
|
||||||
// Box::new(x86_64::qbegen::linux::QbeGen::new())
|
// Box::new(x86_64::qbegen::linux::QbeGen::new())
|
||||||
// }
|
// }
|
||||||
@@ -55,7 +56,10 @@ pub fn compile(cli: &CliArgs, programs: Vec<(PathBuf, Program)>) -> anyhow::Resu
|
|||||||
objs.push(obj_p);
|
objs.push(obj_p);
|
||||||
}
|
}
|
||||||
let out = Path::new(&cli.output);
|
let out = Path::new(&cli.output);
|
||||||
target.link(objs, &out)?;
|
|
||||||
|
let mut args = Vec::new();
|
||||||
|
args.append(&mut cli.linker_args.clone());
|
||||||
|
target.link(objs, &out, &args)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,5 +74,5 @@ pub trait Target {
|
|||||||
}
|
}
|
||||||
fn write_code(&mut self, program: &Program, f: &mut File) -> anyhow::Result<()>;
|
fn write_code(&mut self, program: &Program, f: &mut File) -> anyhow::Result<()>;
|
||||||
fn compile(&mut self, from: &Path, to: &Path) -> anyhow::Result<()>;
|
fn compile(&mut self, from: &Path, to: &Path) -> anyhow::Result<()>;
|
||||||
fn link(&mut self, from: Vec<PathBuf>, to: &Path) -> anyhow::Result<()>;
|
fn link(&mut self, from: Vec<PathBuf>, to: &Path, extra_args: &Vec<String>) -> anyhow::Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use crate::targets::Target;
|
use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Ident, Program, Punctuation, expr::{Expr, IfBranchExpr, Path}, literal::Literal, statement::{ConstDataTyp, Function, Statement, get_constant_data_as_bytes}, typ::Type}, targets::Target};
|
||||||
use std::io::Write;
|
use std::{collections::HashMap, fs::File, io::Write, process};
|
||||||
pub struct AsmGen;
|
pub struct AsmGen;
|
||||||
|
|
||||||
|
const RUNTIME_CODE: &'static str = include_str!("./runtime.s");
|
||||||
|
|
||||||
impl Target for AsmGen {
|
impl Target for AsmGen {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {}
|
Self {}
|
||||||
@@ -12,20 +14,770 @@ impl Target for AsmGen {
|
|||||||
fn get_int_ext(&self) -> &'static str {
|
fn get_int_ext(&self) -> &'static str {
|
||||||
"s"
|
"s"
|
||||||
}
|
}
|
||||||
fn write_code(&mut self, program: &crate::parser::ast::Program, path: &std::path::Path) -> anyhow::Result<()> {
|
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
|
||||||
let mut f = std::fs::File::open(path)?;
|
writeln!(f, "bits 64")?;
|
||||||
|
writeln!(f, "global _start")?;
|
||||||
|
self.write_extern_func_decls(program, f)?;
|
||||||
writeln!(f, "section .text")?;
|
writeln!(f, "section .text")?;
|
||||||
|
writeln!(f, "{}", RUNTIME_CODE)?;
|
||||||
|
|
||||||
writeln!(f, "section .rodata")?;
|
for item in &program.ast.0 {
|
||||||
for (name, constant) in &program.const_vars {
|
match item {
|
||||||
writeln!(f, "{name}:")?;
|
Ast::Statement(stat) => {
|
||||||
|
match stat.inner() {
|
||||||
|
Statement::Fn(func) => self.write_func(program, f, func.clone(), stat.loc().clone())?,
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
writeln!(f, "section .data")?;
|
||||||
|
self.write_constants(program, f)?;
|
||||||
|
self.write_literals(program, f)?;
|
||||||
|
writeln!(f, "section .data")?;
|
||||||
|
self.write_statics(program, f)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn compile(&mut self, _from: &std::path::Path, _to: &std::path::Path) -> anyhow::Result<()> {
|
fn compile(&mut self, from: &std::path::Path, to: &std::path::Path) -> anyhow::Result<()> {
|
||||||
|
let mut cmd = std::process::Command::new("nasm");
|
||||||
|
let cmd = cmd.args(&[
|
||||||
|
"-felf64",
|
||||||
|
"-o",
|
||||||
|
to.to_string_lossy().to_string().as_str(),
|
||||||
|
from.to_string_lossy().to_string().as_str(),
|
||||||
|
]);
|
||||||
|
info!("Running: {} {}", cmd.get_program().to_string_lossy(), cmd.get_args().map(|f| f.to_string_lossy().to_string()).collect::<Vec<String>>().join(" "));
|
||||||
|
cmd.output()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn link(&mut self, _from: Vec<std::path::PathBuf>, _to: &std::path::Path) -> anyhow::Result<()> {
|
fn link(&mut self, from: Vec<std::path::PathBuf>, to: &std::path::Path, extra_args: &Vec<String>) -> anyhow::Result<()> {
|
||||||
|
let mut cmd = std::process::Command::new("ld");
|
||||||
|
cmd.args(extra_args);
|
||||||
|
|
||||||
|
cmd.args(&[
|
||||||
|
"-o",
|
||||||
|
to.to_string_lossy().to_string().as_str(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
for item in &from {
|
||||||
|
cmd.arg(item.to_string_lossy().to_string().as_str());
|
||||||
|
}
|
||||||
|
info!("Running: {} {}", cmd.get_program().to_string_lossy(), cmd.get_args().map(|f| f.to_string_lossy().to_string()).collect::<Vec<String>>().join(" "));
|
||||||
|
cmd.output()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// also used for args
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum VarMapT {
|
||||||
|
Stack(usize, Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FunctionCtx {
|
||||||
|
vars: HashMap<Ident, VarMapT>,
|
||||||
|
stack_offset: usize,
|
||||||
|
loop_level: usize, // used for loops
|
||||||
|
if_level: usize, // used for if stats
|
||||||
|
cmp_level: usize, // used for logical comparisons
|
||||||
|
pub emit_short_circuit_label: bool,
|
||||||
|
pub is_last_item: bool,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionCtx {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
vars: Default::default(),
|
||||||
|
stack_offset: 0,
|
||||||
|
loop_level: 0,
|
||||||
|
if_level: 0,
|
||||||
|
cmp_level: 0,
|
||||||
|
emit_short_circuit_label: true,
|
||||||
|
is_last_item: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_stack(&mut self, name: &Ident, size: usize, typ: Type) -> usize {
|
||||||
|
let offset = self.stack_offset;
|
||||||
|
self.vars.insert(name.clone(), VarMapT::Stack(self.stack_offset, typ));
|
||||||
|
self.stack_offset += size;
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, name: &Ident) -> Option<&VarMapT> {
|
||||||
|
self.vars.get(name)
|
||||||
|
}
|
||||||
|
pub fn get_stack_offset(&self) -> usize {
|
||||||
|
self.stack_offset
|
||||||
|
}
|
||||||
|
pub fn register_id_to_str(&self, id: usize) -> &str {
|
||||||
|
match id {
|
||||||
|
0 => "rdi",
|
||||||
|
1 => "rsi",
|
||||||
|
2 => "rdx",
|
||||||
|
3 => "rcx",
|
||||||
|
4 => "r8",
|
||||||
|
5 => "r9",
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn inc_loop_level(&mut self) -> usize {
|
||||||
|
self.loop_level += 1;
|
||||||
|
self.loop_level
|
||||||
|
}
|
||||||
|
pub fn dec_loop_level(&mut self) -> usize {
|
||||||
|
self.loop_level -= 1;
|
||||||
|
self.loop_level
|
||||||
|
}
|
||||||
|
pub fn loop_level(&self) -> usize {
|
||||||
|
self.loop_level
|
||||||
|
}
|
||||||
|
pub fn inc_if_level(&mut self) -> usize {
|
||||||
|
self.if_level += 1;
|
||||||
|
self.if_level
|
||||||
|
}
|
||||||
|
pub fn dec_if_level(&mut self) -> usize {
|
||||||
|
self.if_level -= 1;
|
||||||
|
self.if_level
|
||||||
|
}
|
||||||
|
pub fn if_level(&self) -> usize {
|
||||||
|
self.if_level
|
||||||
|
}
|
||||||
|
pub fn inc_cmp_level(&mut self) -> usize {
|
||||||
|
self.if_level += 1;
|
||||||
|
self.if_level
|
||||||
|
}
|
||||||
|
pub fn dec_cmp_level(&mut self) -> usize {
|
||||||
|
self.if_level -= 1;
|
||||||
|
self.if_level
|
||||||
|
}
|
||||||
|
pub fn cmp_level(&self) -> usize {
|
||||||
|
self.cmp_level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl AsmGen {
|
||||||
|
pub fn write_func(&self, program: &Program, f: &mut File, func: Function, loc: Loc) -> anyhow::Result<()> {
|
||||||
|
if func.qual_extern.is_some() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if let Some(body) = &func.body && let Some(name) = &func.name {
|
||||||
|
let mut fc = FunctionCtx::new();
|
||||||
|
let name = if let Some(struct_name) = &func.struct_name {
|
||||||
|
format!("{}${}", struct_name, name)
|
||||||
|
} else {
|
||||||
|
name.to_string()
|
||||||
|
};
|
||||||
|
info!("writing norm function: {name}");
|
||||||
|
writeln!(f, "{}: ; {} {}", name, loc, func.get_full_name_pretty())?;
|
||||||
|
if body.0.is_empty() {
|
||||||
|
writeln!(f, " ret")?;
|
||||||
|
} else {
|
||||||
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
let mut last_item = None;
|
||||||
|
for (i, param) in func.params.iter().enumerate() {
|
||||||
|
let typ = param.1.clone();
|
||||||
|
let typ = typ.inner().clone();
|
||||||
|
let offset = fc.insert_stack(¶m.0.clone(), typ.size_of(program)?, typ.clone());
|
||||||
|
|
||||||
|
writeln!(&mut buf, " mov [rsp+{offset}], {} ; func arg", fc.register_id_to_str(i))?;
|
||||||
|
}
|
||||||
|
let body = func.body.expect("Safe as its checked already").0;
|
||||||
|
for (i, item) in body.iter().enumerate() {
|
||||||
|
if i == body.len() - 1 {
|
||||||
|
fc.is_last_item = true;
|
||||||
|
}
|
||||||
|
self.write_ast(program, &mut buf, &mut fc, item)?;
|
||||||
|
last_item = Some(item.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
if fc.stack_offset > 0 {
|
||||||
|
writeln!(f, " sub rsp, {}", fc.stack_offset)?;
|
||||||
|
}
|
||||||
|
f.write(&buf)?;
|
||||||
|
if fc.stack_offset > 0 {
|
||||||
|
writeln!(f, " add rsp, {}", fc.stack_offset)?;
|
||||||
|
}
|
||||||
|
match last_item {
|
||||||
|
Some(Ast::Expr(expr)) => {
|
||||||
|
match expr.inner() {
|
||||||
|
Expr::Return(_) => (),
|
||||||
|
_ => writeln!(f, " ret")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
// while writing the return expr, it changes is_last_item to false if it didnt write ret
|
||||||
|
// and it was the last item in a body
|
||||||
|
if !fc.is_last_item {
|
||||||
|
writeln!(f, " ret")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(_) = &func.name {
|
||||||
|
info!("writing function stub: {}", func.get_full_name_pretty());
|
||||||
|
writeln!(f, " ret")?;
|
||||||
|
}
|
||||||
|
writeln!(f, "\n")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn write_extern_func_decls(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
|
||||||
|
for (name, func) in &program.functions {
|
||||||
|
match func.inner().qual_extern {
|
||||||
|
Some(_) if func.inner().body.is_some() => {
|
||||||
|
writeln!(f, "global {name}")?;
|
||||||
|
}
|
||||||
|
Some(_) if func.inner().body.is_none() => {
|
||||||
|
writeln!(f, "extern {name}")?;
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn write_ast(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, ast: &Ast) -> anyhow::Result<()> {
|
||||||
|
match ast {
|
||||||
|
Ast::Expr(expr) => self.write_expr(program, f, fc, expr.inner())?,
|
||||||
|
Ast::Statement(stat) => self.write_stat(program, f, fc, stat.inner())?,
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn write_expr(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, expr: &Expr) -> anyhow::Result<()> {
|
||||||
|
match expr {
|
||||||
|
Expr::Cast { left, .. } => self.write_expr(program, f, fc, left.inner())?,
|
||||||
|
Expr::Literal(id, val) => {
|
||||||
|
match val {
|
||||||
|
Literal::Array{ items, item_size } => {
|
||||||
|
dbg!(&val);
|
||||||
|
writeln!(f, " lea r10, [rel mcl_lit_{id}] ; arr")?;
|
||||||
|
for (i, item) in items.iter().enumerate() {
|
||||||
|
self.write_expr(program, f, fc, item.inner())?;
|
||||||
|
let offset = i * item_size.unwrap();
|
||||||
|
writeln!(f, " mov [r10+{offset}], rax")?;
|
||||||
|
}
|
||||||
|
writeln!(f, " mov rax, r10")?;
|
||||||
|
}
|
||||||
|
Literal::ArrayRepeat { val, item_size, count} => {
|
||||||
|
writeln!(f, " lea r10, [rel mcl_lit_{id}] ; arr repeat")?;
|
||||||
|
for i in 0..*count {
|
||||||
|
self.write_expr(program, f, fc, val.inner())?;
|
||||||
|
let offset = i * item_size.unwrap();
|
||||||
|
writeln!(f, " mov [r10+{offset}], rax")?;
|
||||||
|
}
|
||||||
|
writeln!(f, " mov rax, r10")?;
|
||||||
|
},
|
||||||
|
Literal::Ident(_) => unreachable!(),
|
||||||
|
Literal::String(_) => {
|
||||||
|
writeln!(f, " lea rax, [rel mcl_lit_{id}] ; str")?;
|
||||||
|
}
|
||||||
|
Literal::Bool(v) => {
|
||||||
|
writeln!(f, " mov rax, {} ; {}", *v as u8, v)?;
|
||||||
|
}
|
||||||
|
Literal::Number(_) |
|
||||||
|
Literal::Char(_) => {
|
||||||
|
writeln!(f, " mov rax, [rel mcl_lit_{id}] ; num/char")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Struct(id, strct) => {
|
||||||
|
writeln!(f, " lea r10, [rel mcl_lit_{id}]")?;
|
||||||
|
let strct_t = program.structs.get(&strct.path.0[0]).unwrap();
|
||||||
|
for (name, expr) in &strct.fields {
|
||||||
|
self.write_expr(program, f, fc, expr.inner())?;
|
||||||
|
let offset = strct_t.inner().get_offset_of(program, name)?;
|
||||||
|
writeln!(f, " mov [r10+{offset}], rax")?;
|
||||||
|
}
|
||||||
|
writeln!(f, " mov rax, r10")?;
|
||||||
|
}
|
||||||
|
Expr::PtrFieldAccess { left, right, offset, l_typ, r_typ } => {
|
||||||
|
self.write_expr(program, f, fc, left.clone().unwrap().inner())?;
|
||||||
|
dbg!(&l_typ);
|
||||||
|
if l_typ.clone().unwrap().should_deref_pointer(program) {
|
||||||
|
writeln!(f, " mov rax, [rel rax] ; rqawr ->{:?}", right.inner())?;
|
||||||
|
}
|
||||||
|
if *offset > 0 {
|
||||||
|
writeln!(f, " add rax, {offset} ; ->{:?}", right.inner())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(t) = r_typ && t.is_numeric(program) {
|
||||||
|
writeln!(f, " mov rax, [rax] ; ->{:?}", right.inner())?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Expr::FieldAccess { left, right, offset, l_typ: _, r_typ } => {
|
||||||
|
self.write_expr(program, f, fc, left.clone().unwrap().inner())?;
|
||||||
|
if *offset > 0 {
|
||||||
|
writeln!(f, " add rax, {offset} ; .{:?}", right.inner())?;
|
||||||
|
}
|
||||||
|
if let Some(t) = r_typ && t.is_numeric(program) {
|
||||||
|
writeln!(f, " mov rax, [rax] ; .{:?}", right.inner())?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Expr::InfLoop { body } => {
|
||||||
|
let sl = fc.inc_loop_level();
|
||||||
|
writeln!(f, ".L{sl}_test: ; inf loop (named test for tehnical reason)")?;
|
||||||
|
for item in &body.0 {
|
||||||
|
self.write_ast(program, f, fc, item)?;
|
||||||
|
}
|
||||||
|
writeln!(f, ".L{sl}_end: ; inf loop")?;
|
||||||
|
|
||||||
|
}
|
||||||
|
Expr::ForLoop { init, test, on_loop, body } => {
|
||||||
|
let sl = fc.inc_loop_level();
|
||||||
|
writeln!(f, ".L{sl}_init: ; for loop")?;
|
||||||
|
self.write_ast(program, f, fc, &init)?;
|
||||||
|
writeln!(f, ".L{sl}_test: ; for loop")?;
|
||||||
|
writeln!(f, " xor rax, rax")?;
|
||||||
|
self.write_expr(program, f, fc, &test.inner())?;
|
||||||
|
writeln!(f, " test rax, rax")?;
|
||||||
|
writeln!(f, " jz .L{sl}_end")?;
|
||||||
|
writeln!(f, ".L{sl}_on_loop: ; for loop")?;
|
||||||
|
self.write_expr(program, f, fc, &on_loop.inner())?;
|
||||||
|
for item in &body.0 {
|
||||||
|
self.write_ast(program, f, fc, item)?;
|
||||||
|
}
|
||||||
|
writeln!(f, ".L{sl}_end: ; for loop")?;
|
||||||
|
}
|
||||||
|
Expr::WhileLoop { test, body } => {
|
||||||
|
let sl = fc.inc_loop_level();
|
||||||
|
writeln!(f, ".L{sl}_test: ; while loop")?;
|
||||||
|
writeln!(f, " xor rax, rax")?;
|
||||||
|
self.write_expr(program, f, fc, &test.inner())?;
|
||||||
|
writeln!(f, " test rax, rax")?;
|
||||||
|
writeln!(f, " jz .L{sl}_end")?;
|
||||||
|
for item in &body.0 {
|
||||||
|
self.write_ast(program, f, fc, item)?;
|
||||||
|
}
|
||||||
|
writeln!(f, ".L{sl}_end: ; while loop")?;
|
||||||
|
}
|
||||||
|
Expr::Continue => {
|
||||||
|
let sl = fc.loop_level();
|
||||||
|
writeln!(f, " jmp .L{sl}_test ; continue")?;
|
||||||
|
}
|
||||||
|
Expr::Break => {
|
||||||
|
let sl = fc.loop_level();
|
||||||
|
writeln!(f, " jmp .L{sl}_end ; break")?;
|
||||||
|
}
|
||||||
|
Expr::If(ifs) => {
|
||||||
|
let cl = fc.inc_if_level();
|
||||||
|
writeln!(f, " xor rax, rax")?;
|
||||||
|
self.write_expr(program, f, fc, ifs.test.inner())?;
|
||||||
|
writeln!(f, " test rax, rax")?;
|
||||||
|
if ifs.else_if.is_some() {
|
||||||
|
writeln!(f, " jz .C{cl}_branch0")?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " jz .C{cl}_end")?;
|
||||||
|
}
|
||||||
|
for item in &ifs.body.0 {
|
||||||
|
self.write_ast(program, f, fc, item)?;
|
||||||
|
}
|
||||||
|
self.write_expr(program, f, fc, ifs.test.inner())?;
|
||||||
|
if ifs.else_if.is_some() {
|
||||||
|
writeln!(f, " jmp .C{cl}_end")?;
|
||||||
|
}
|
||||||
|
fn x(slf: &AsmGen, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, els: &Option<IfBranchExpr>, depth: usize) -> anyhow::Result<()> {
|
||||||
|
let cl = fc.if_level();
|
||||||
|
if let Some(els) = els {
|
||||||
|
match els {
|
||||||
|
IfBranchExpr::Else(els) => {
|
||||||
|
for item in &els.0 {
|
||||||
|
writeln!(f, ".C{cl}_branch{depth}: ; if")?;
|
||||||
|
slf.write_ast(program, f, fc, item)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IfBranchExpr::ElseIf(elsif) => {
|
||||||
|
slf.write_expr(program, f, fc, elsif.test.inner())?;
|
||||||
|
writeln!(f, " test rax, rax")?;
|
||||||
|
if elsif.else_if.is_some() {
|
||||||
|
writeln!(f, " jz .C{cl}_branch{}", depth + 1)?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " jz .C{cl}_end")?;
|
||||||
|
}
|
||||||
|
for item in &elsif.body.0 {
|
||||||
|
slf.write_ast(program, f, fc, item)?;
|
||||||
|
}
|
||||||
|
writeln!(f, " jmp .C{cl}_end")?;
|
||||||
|
x(slf, program, f, fc, &elsif.else_if, depth+1)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
x(self, program, f, fc, &ifs.else_if, 0)?;
|
||||||
|
writeln!(f, ".C{cl}_end: ; if")?;
|
||||||
|
}
|
||||||
|
Expr::Group(grp) => {
|
||||||
|
self.write_expr(program, f, fc, grp.inner())?;
|
||||||
|
}
|
||||||
|
Expr::Return(ret) => {
|
||||||
|
if let Some(ret) = &**ret {
|
||||||
|
self.write_expr(program, f, fc, ret.inner())?;
|
||||||
|
}
|
||||||
|
if !fc.is_last_item {
|
||||||
|
writeln!(f, " ret")?;
|
||||||
|
} else {
|
||||||
|
fc.is_last_item = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::MethodCall { left, strct, params } => {
|
||||||
|
let method_name;
|
||||||
|
match left.inner() {
|
||||||
|
Expr::FieldAccess { left, right, .. } |
|
||||||
|
Expr::PtrFieldAccess { left, right, .. } => {
|
||||||
|
self.write_expr(program, f, fc, left.clone().unwrap().inner())?;
|
||||||
|
match right.inner() {
|
||||||
|
Expr::Path(path) => {
|
||||||
|
method_name = path.0.first().unwrap().clone();
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
let reg = fc.register_id_to_str(0);
|
||||||
|
writeln!(f, " mov {reg}, rax")?;
|
||||||
|
for (i, param) in params.0.iter().enumerate() {
|
||||||
|
self.write_expr(program, f, fc, param.inner())?;
|
||||||
|
if i <= 5 {
|
||||||
|
let reg = fc.register_id_to_str(i+1);
|
||||||
|
writeln!(f, " mov {reg}, rax")?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " push rax")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(f, " call {}", Path(vec![strct.clone().unwrap(), method_name]).display_asm_compat())?;
|
||||||
|
}
|
||||||
|
Expr::Call { path, params } => {
|
||||||
|
for (i, param) in params.0.iter().enumerate() {
|
||||||
|
self.write_expr(program, f, fc, param.inner())?;
|
||||||
|
if i <= 5 {
|
||||||
|
let reg = fc.register_id_to_str(i);
|
||||||
|
writeln!(f, " mov {reg}, rax")?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " push rax")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(f, " call {}", path.inner().unwrap_path().display_asm_compat())?;
|
||||||
|
}
|
||||||
|
Expr::UnOp { typ, right } => {
|
||||||
|
self.write_expr(program, f, fc, right.inner())?;
|
||||||
|
match typ {
|
||||||
|
Punctuation::Not => {
|
||||||
|
writeln!(f, " test rax, rax ; logical not")?;
|
||||||
|
writeln!(f, " sete al")?;
|
||||||
|
writeln!(f, " movzx rax, al")?;
|
||||||
|
},
|
||||||
|
Punctuation::Plus => {
|
||||||
|
writeln!(f, " mov rdx, rax ; +x")?;
|
||||||
|
writeln!(f, " sar rdx, 63")?;
|
||||||
|
writeln!(f, " xor rax, rdx")?;
|
||||||
|
writeln!(f, " sub rax, rdx")?;
|
||||||
|
},
|
||||||
|
Punctuation::Minus => {
|
||||||
|
writeln!(f, " neg rax ; -x")?;
|
||||||
|
},
|
||||||
|
Punctuation::Ampersand => {
|
||||||
|
writeln!(f, " ; noop?")?;
|
||||||
|
},
|
||||||
|
Punctuation::Star => {
|
||||||
|
writeln!(f, " ; noop deref")?;
|
||||||
|
},
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::BinOp { typ, left, right, signed } => {
|
||||||
|
self.write_expr(program, f, fc, left.inner())?;
|
||||||
|
writeln!(f, " mov r10, rax")?;
|
||||||
|
self.write_expr(program, f, fc, right.inner())?;
|
||||||
|
match typ {
|
||||||
|
Punctuation::Plus => {
|
||||||
|
writeln!(f, " add rax, r10")?;
|
||||||
|
},
|
||||||
|
Punctuation::Minus => {
|
||||||
|
writeln!(f, " sub rax, r10")?;
|
||||||
|
},
|
||||||
|
Punctuation::Div => {
|
||||||
|
writeln!(f, " mov rdi, rax")?;
|
||||||
|
writeln!(f, " mov rax, r10 ")?;
|
||||||
|
if *signed {
|
||||||
|
writeln!(f, " cqo")?;
|
||||||
|
writeln!(f, " idiv rdi")?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " xor rdx, rdx")?;
|
||||||
|
writeln!(f, " div rdi")?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Punctuation::Star => {
|
||||||
|
writeln!(f, " mov rdi, rax")?;
|
||||||
|
writeln!(f, " mov rax, r10 ")?;
|
||||||
|
|
||||||
|
if *signed {
|
||||||
|
writeln!(f, " imul r10 ")?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " mul r10 ")?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Punctuation::Mod => {
|
||||||
|
writeln!(f, " mov rdi, rax")?;
|
||||||
|
writeln!(f, " mov rax, r10 ")?;
|
||||||
|
if *signed {
|
||||||
|
writeln!(f, " cqo")?;
|
||||||
|
writeln!(f, " idiv rdi")?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, " xor rdx, rdx")?;
|
||||||
|
writeln!(f, " div rdi")?;
|
||||||
|
}
|
||||||
|
writeln!(f, " mov rax, rdx")?;
|
||||||
|
},
|
||||||
|
Punctuation::Shl => {
|
||||||
|
writeln!(f, " shl r10, rax")?;
|
||||||
|
},
|
||||||
|
Punctuation::Shr => {
|
||||||
|
writeln!(f, " shr r10, rax")?;
|
||||||
|
},
|
||||||
|
Punctuation::AndAnd => {
|
||||||
|
let l = fc.cmp_level();
|
||||||
|
let should_emit = fc.emit_short_circuit_label;
|
||||||
|
self.write_expr(program, f, fc, left.inner())?;
|
||||||
|
writeln!(f, " test rax, rax")?;
|
||||||
|
writeln!(f, " jz .M{l}_false")?;
|
||||||
|
fc.emit_short_circuit_label = false;
|
||||||
|
self.write_expr(program, f, fc, right.inner())?;
|
||||||
|
fc.emit_short_circuit_label = should_emit;
|
||||||
|
if !should_emit {
|
||||||
|
writeln!(f, " setnz al")?;
|
||||||
|
writeln!(f, " movzx rax, al")?;
|
||||||
|
writeln!(f, " jmp .M{l}_sc")?;
|
||||||
|
writeln!(f, ".M{l}_true:")?;
|
||||||
|
writeln!(f, " mov rax, 1")?;
|
||||||
|
writeln!(f, ".M{l}_false:")?;
|
||||||
|
writeln!(f, " xor rax, rax")?;
|
||||||
|
writeln!(f, ".M{l}_sc:")?;
|
||||||
|
fc.inc_cmp_level();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Punctuation::OrOr => {
|
||||||
|
let l = fc.cmp_level();
|
||||||
|
let should_emit = fc.emit_short_circuit_label;
|
||||||
|
self.write_expr(program, f, fc, left.inner())?;
|
||||||
|
writeln!(f, " cmp rax, rax")?;
|
||||||
|
writeln!(f, " jnz .M{l}_true")?;
|
||||||
|
fc.emit_short_circuit_label = false;
|
||||||
|
self.write_expr(program, f, fc, right.inner())?;
|
||||||
|
fc.emit_short_circuit_label = should_emit;
|
||||||
|
if !should_emit {
|
||||||
|
writeln!(f, " setnz al")?;
|
||||||
|
writeln!(f, " movzx rax, al")?;
|
||||||
|
writeln!(f, " jmp .M{l}_sc")?;
|
||||||
|
writeln!(f, ".M{l}_true:")?;
|
||||||
|
writeln!(f, " mov rax, 1")?;
|
||||||
|
writeln!(f, ".M{l}_false:")?;
|
||||||
|
writeln!(f, " xor rax, rax")?;
|
||||||
|
writeln!(f, ".M{l}_sc:")?;
|
||||||
|
fc.inc_cmp_level();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Punctuation::Ampersand => {},
|
||||||
|
Punctuation::Or => {},
|
||||||
|
Punctuation::Xor => {},
|
||||||
|
Punctuation::AddEq => {},
|
||||||
|
Punctuation::SubEq => {},
|
||||||
|
Punctuation::DivEq => {},
|
||||||
|
Punctuation::MulEq => {},
|
||||||
|
Punctuation::ModEq => {},
|
||||||
|
Punctuation::ShlEq => {},
|
||||||
|
Punctuation::ShrEq => {},
|
||||||
|
Punctuation::AndEq => {},
|
||||||
|
Punctuation::OrEq => {},
|
||||||
|
Punctuation::XorEq => {},
|
||||||
|
Punctuation::Eq => {
|
||||||
|
self.write_expr(program, f, fc, right.inner())?;
|
||||||
|
let VarMapT::Stack(offset, _) = fc.get(&left.inner().unwrap_path().0.clone()[0]).unwrap();
|
||||||
|
writeln!(f, "mov [rbx+{offset}], rax")?;
|
||||||
|
},
|
||||||
|
Punctuation::EqEq => {},
|
||||||
|
Punctuation::Neq => {},
|
||||||
|
Punctuation::Lt => {},
|
||||||
|
Punctuation::Gt => {},
|
||||||
|
Punctuation::Le => {},
|
||||||
|
Punctuation::Ge => {},
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Path(path) => {
|
||||||
|
let ident = path.0.last().unwrap().clone();
|
||||||
|
if let Some(var) = fc.get(&ident) {
|
||||||
|
match var {
|
||||||
|
VarMapT::Stack(offset, typ) => {
|
||||||
|
match typ {
|
||||||
|
/*Type::Builtin { .. } => {
|
||||||
|
writeln!(f, " mov rax, [rsp+{offset}] ; var {ident}")?;
|
||||||
|
writeln!(f, " mov rax, [rax] ; var {ident}")?;
|
||||||
|
}*/
|
||||||
|
_ => writeln!(f, " mov rax, [rsp+{offset}] ; var {ident}")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(_) = program.get_const_var(&ident) {
|
||||||
|
writeln!(f, " lea rax, [rel {ident}] ; const {ident}")?;
|
||||||
|
} else if let Some(_) = program.functions.get(&ident) {
|
||||||
|
writeln!(f, " lea rax, [rel {ident}] ; fn ptr {ident}")?;
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Expr::Self_ { strct: _ } => {
|
||||||
|
if let Some(var) = fc.get(&Ident::new("self")) {
|
||||||
|
match var {
|
||||||
|
VarMapT::Stack(offset, _) => {
|
||||||
|
writeln!(f, " mov rax, [rsp+{offset}] ; self")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v => unreachable!("{v:?}")
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn write_stat(&self, program: &Program, f: &mut impl Write, fc: &mut FunctionCtx, stat: &Statement) -> anyhow::Result<()> {
|
||||||
|
match stat {
|
||||||
|
Statement::Let(lt) => {
|
||||||
|
let typ = lt.typ.clone().unwrap();
|
||||||
|
let loc = typ.loc().clone();
|
||||||
|
let typ = typ.inner().clone();
|
||||||
|
let offset = fc.insert_stack(<.name, typ.size_of(program)?, typ.clone());
|
||||||
|
|
||||||
|
if let Some(value) = <.val {
|
||||||
|
self.write_expr(program, f, fc, value.inner())?;
|
||||||
|
writeln!(f, " mov [rsp+{offset}], rax ; {loc} let {};", lt.name)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn write_constants(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
|
||||||
|
for (name, constant) in &program.const_vars {
|
||||||
|
writeln!(f, "{name}: ; const")?;
|
||||||
|
let bytes = get_constant_data_as_bytes(program, constant.val.clone(), false, false)?;
|
||||||
|
fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
|
||||||
|
match bytes {
|
||||||
|
ConstDataTyp::Array(arr) => {
|
||||||
|
for v in arr {
|
||||||
|
x(v, f)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConstDataTyp::Bytes(bytes) => {
|
||||||
|
write!(f, " db ")?;
|
||||||
|
for (i, b) in bytes.into_iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
write!(f, "0x{:02x}", b)?;
|
||||||
|
}
|
||||||
|
writeln!(f, "")?;
|
||||||
|
}
|
||||||
|
ConstDataTyp::AddrOfFunc(name) |
|
||||||
|
ConstDataTyp::AddrOfConst(name) |
|
||||||
|
ConstDataTyp::AddrOfStatic(name) => {
|
||||||
|
writeln!(f, " dq {name}")?;
|
||||||
|
},
|
||||||
|
ConstDataTyp::Variable(..) => unreachable!(),
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
x(&bytes, f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn write_statics(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
|
||||||
|
for (name, statc) in &program.static_vars {
|
||||||
|
writeln!(f, "{name}: ; static")?;
|
||||||
|
let bytes = get_constant_data_as_bytes(program, statc.val.clone(), false, false)?;
|
||||||
|
fn x(bytes: &ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
|
||||||
|
match bytes {
|
||||||
|
ConstDataTyp::Array(arr) => {
|
||||||
|
for v in arr {
|
||||||
|
x(v, f)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConstDataTyp::Bytes(bytes) => {
|
||||||
|
write!(f, " db ")?;
|
||||||
|
for (i, b) in bytes.into_iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
write!(f, "0x{:02x}", b)?;
|
||||||
|
}
|
||||||
|
writeln!(f, "")?;
|
||||||
|
}
|
||||||
|
ConstDataTyp::AddrOfFunc(name) |
|
||||||
|
ConstDataTyp::AddrOfConst(name) |
|
||||||
|
ConstDataTyp::AddrOfStatic(name) => {
|
||||||
|
writeln!(f, " dq {name}")?;
|
||||||
|
},
|
||||||
|
ConstDataTyp::Variable(..) => unreachable!(),
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
x(&bytes, f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn write_literals(&self, program: &Program, f: &mut File) -> anyhow::Result<()> {
|
||||||
|
fn w(bytes: ConstDataTyp, f: &mut File) -> anyhow::Result<()> {
|
||||||
|
match bytes {
|
||||||
|
ConstDataTyp::Array(arr) => {
|
||||||
|
for v in arr {
|
||||||
|
w(v, f)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConstDataTyp::Bytes(bytes) => {
|
||||||
|
write!(f, " db ")?;
|
||||||
|
for (i, b) in bytes.into_iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
write!(f, "0x{:02x}", b)?;
|
||||||
|
}
|
||||||
|
writeln!(f, "")?;
|
||||||
|
}
|
||||||
|
ConstDataTyp::AddrOfFunc(name) |
|
||||||
|
ConstDataTyp::AddrOfConst(name) |
|
||||||
|
ConstDataTyp::AddrOfStatic(name) => {
|
||||||
|
writeln!(f, " dq {name}")?;
|
||||||
|
},
|
||||||
|
ConstDataTyp::Variable(name, size) => {
|
||||||
|
writeln!(f, " resb {size} ; {name}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
for lit in &program.literals {
|
||||||
|
writeln!(f, "mcl_lit_{}:", lit.0)?;
|
||||||
|
w(get_constant_data_as_bytes(program, LocBox::new(&Loc::default(), Expr::Literal(lit.0.clone(), lit.1.clone())), false, false)?, f)?;
|
||||||
|
}
|
||||||
|
for lit in &program.struct_literals {
|
||||||
|
writeln!(f, "mcl_lit_{}:", lit.0)?;
|
||||||
|
w(get_constant_data_as_bytes(program, LocBox::new(&Loc::default(), Expr::Struct(lit.0.clone(), lit.1.clone())), false, false)?, f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
66
src/targets/x86_64/asmgen/linux/runtime.s
Normal file
66
src/targets/x86_64/asmgen/linux/runtime.s
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
__INTERNAL_syscall0:
|
||||||
|
mov rax, rdi
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
|
__INTERNAL_syscall1:
|
||||||
|
mov rax, rdi
|
||||||
|
mov rdi, rsi
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
|
__INTERNAL_syscall2:
|
||||||
|
mov rax, rdi
|
||||||
|
mov rdi, rsi
|
||||||
|
mov rsi, rdx
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
|
__INTERNAL_syscall3:
|
||||||
|
mov rax, rdi
|
||||||
|
mov rdi, rsi
|
||||||
|
mov rsi, rdx
|
||||||
|
mov rdx, rcx
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
|
__INTERNAL_syscall4:
|
||||||
|
mov rax, rdi
|
||||||
|
mov rdi, rsi
|
||||||
|
mov rsi, rdx
|
||||||
|
mov rdx, rcx
|
||||||
|
mov r10, r8
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
|
__INTERNAL_syscall5:
|
||||||
|
mov rax, rdi
|
||||||
|
mov rdi, rsi
|
||||||
|
mov rsi, rdx
|
||||||
|
mov rdx, rcx
|
||||||
|
mov r10, r8
|
||||||
|
mov r8, r9
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
|
__INTERNAL_syscall6:
|
||||||
|
mov rax, rdi
|
||||||
|
mov rdi, rsi
|
||||||
|
mov rsi, rdx
|
||||||
|
mov rdx, rcx
|
||||||
|
mov r10, r8
|
||||||
|
mov r8, r9
|
||||||
|
mov r9, [rsp + 8]
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
global _start
|
||||||
|
_start:
|
||||||
|
xor rax, rax
|
||||||
|
call main
|
||||||
|
|
||||||
|
mov rdi, rax
|
||||||
|
mov rax, 60
|
||||||
|
syscall
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{fs::File, ops::Deref};
|
use std::{fs::File, ops::Deref};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use crate::{common::Loc, parser::ast::{expr::Expr, literal::Literal, statement::{Function, Statement, Struct}, typ::Type, Ast, Ident, Program, Punctuation}, targets::Target, validator::predefined::TypeType};
|
use crate::{common::Loc, parser::ast::{expr::Expr, literal::Literal, statement::{Function, Statement, Struct}, typ::Type, Ast, Ident, Program, Punctuation}, targets::Target};
|
||||||
|
|
||||||
const INTERNAL_CODE: &'static str = include_str!("internals.c");
|
const INTERNAL_CODE: &'static str = include_str!("internals.c");
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ impl Target for CGen {
|
|||||||
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
|
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
|
||||||
self.write_fat_comment(f, "Internal number types");
|
self.write_fat_comment(f, "Internal number types");
|
||||||
for (name, typ) in &program.types {
|
for (name, typ) in &program.types {
|
||||||
if let TypeType::Builtin { size: _, signed: _ } = typ {
|
if let Type::Builtin { name: _, size: _, signed: _ } = typ.inner() {
|
||||||
self.write_internal_type(f, program, name, typ)?;
|
self.write_internal_type(f, program, name, typ.inner())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,9 +34,7 @@ impl Target for CGen {
|
|||||||
|
|
||||||
self.write_fat_comment(f, "Type aliases");
|
self.write_fat_comment(f, "Type aliases");
|
||||||
for (name, typ) in &program.types {
|
for (name, typ) in &program.types {
|
||||||
if let TypeType::Normal(t) = typ {
|
self.write_typedef(f, program, typ.inner(), name)?;
|
||||||
self.write_typedef(f, program, t.inner(), name)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_fat_comment(f, "Struct definitions");
|
self.write_fat_comment(f, "Struct definitions");
|
||||||
@@ -45,7 +43,7 @@ impl Target for CGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.write_fat_comment(f, "Function forward declarations");
|
self.write_fat_comment(f, "Function forward declarations");
|
||||||
fn write_fn_fwdclr(slf: &mut CGen, program: &Program, f: &mut File, name: &Ident, func: &Function) -> anyhow::Result<()> {
|
fn write_fn_fwdclr(slf: &mut CGen, program: &Program, f: &mut File, _name: &Ident, func: &Function) -> anyhow::Result<()> {
|
||||||
writeln!(f, "// {}", func.get_def_as_str())?;
|
writeln!(f, "// {}", func.get_def_as_str())?;
|
||||||
if let Some(rett) = &func.ret_type {
|
if let Some(rett) = &func.ret_type {
|
||||||
slf.write_type(f, program, rett.inner())?;
|
slf.write_type(f, program, rett.inner())?;
|
||||||
@@ -121,7 +119,7 @@ impl CGen {
|
|||||||
Expr::If(_ifexpr) => {
|
Expr::If(_ifexpr) => {
|
||||||
|
|
||||||
}
|
}
|
||||||
Expr::BinOp { typ, left, right } => {
|
Expr::BinOp { typ, .. } => {
|
||||||
match typ {
|
match typ {
|
||||||
Punctuation::Eq => {
|
Punctuation::Eq => {
|
||||||
|
|
||||||
@@ -182,6 +180,7 @@ impl CGen {
|
|||||||
pf.push_str(&part(f, program, inner)?);
|
pf.push_str(&part(f, program, inner)?);
|
||||||
pf.push_str("[]");
|
pf.push_str("[]");
|
||||||
}
|
}
|
||||||
|
Type::Builtin { .. } => ()
|
||||||
}
|
}
|
||||||
Ok(pf)
|
Ok(pf)
|
||||||
}
|
}
|
||||||
@@ -223,14 +222,14 @@ impl CGen {
|
|||||||
write!(f, "[]")?;
|
write!(f, "[]")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Type::Builtin { .. } => ()
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_internal_type(&mut self, f: &mut File, program: &Program, name: &Ident, typ: &TypeType) -> anyhow::Result<()> {
|
fn write_internal_type(&mut self, f: &mut File, _program: &Program, _name: &Ident, typ: &Type) -> anyhow::Result<()> {
|
||||||
match typ {
|
match typ {
|
||||||
TypeType::Normal(typ) => unreachable!(),
|
Type::Builtin{ name, size, signed } => {
|
||||||
TypeType::Builtin{ size, signed } => {
|
|
||||||
if *size == 0 {
|
if *size == 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -249,7 +248,7 @@ impl CGen {
|
|||||||
8 => write!(f, "long long ")?,
|
8 => write!(f, "long long ")?,
|
||||||
3 | 5 | 6 | 7 => unreachable!(),
|
3 | 5 | 6 | 7 => unreachable!(),
|
||||||
}
|
}
|
||||||
writeln!(f, "{};", name.0)?;
|
writeln!(f, "{};", name)?;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@@ -267,7 +266,7 @@ impl CGen {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_fn(&mut self, f: &mut File, program: &Program, func: &Function, loc: &Loc) -> anyhow::Result<()> {
|
fn write_fn(&mut self, f: &mut File, program: &Program, func: &Function, _loc: &Loc) -> anyhow::Result<()> {
|
||||||
writeln!(f, "\n\n// {}", func.get_def_as_str())?;
|
writeln!(f, "\n\n// {}", func.get_def_as_str())?;
|
||||||
if let Some(rett) = &func.ret_type {
|
if let Some(rett) = &func.ret_type {
|
||||||
self.write_type(f, program, rett.inner())?;
|
self.write_type(f, program, rett.inner())?;
|
||||||
@@ -295,7 +294,7 @@ impl CGen {
|
|||||||
Ast::Expr(expr) => {
|
Ast::Expr(expr) => {
|
||||||
self.write_expr(f, program, expr.inner(), expr.loc())?
|
self.write_expr(f, program, expr.inner(), expr.loc())?
|
||||||
}
|
}
|
||||||
Ast::Statement(stat) => {
|
Ast::Statement(_) => {
|
||||||
//todo!()
|
//todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// pub mod asmgen;
|
pub mod asmgen;
|
||||||
pub mod cgen;
|
// pub mod cgen;
|
||||||
// pub mod qbegen;
|
// pub mod qbegen;
|
||||||
// pub mod llvmgen;
|
// pub mod llvmgen;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{collections::HashMap, fmt::Display};
|
use std::{collections::HashMap, fmt::Display};
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use parse_int::parse;
|
use parse_int::parse;
|
||||||
use crate::{common::{loc::LocIncr, Loc}, error, lerror};
|
use crate::{common::{loc::LocIncr, Loc},lerror};
|
||||||
|
|
||||||
pub mod tokentype;
|
pub mod tokentype;
|
||||||
use tokentype::*;
|
use tokentype::*;
|
||||||
@@ -30,6 +30,12 @@ impl Token {
|
|||||||
pub fn tt(&self) -> &TokenType {
|
pub fn tt(&self) -> &TokenType {
|
||||||
&self.tt
|
&self.tt
|
||||||
}
|
}
|
||||||
|
pub fn new_test(tt: TokenType) -> Self {
|
||||||
|
Self {
|
||||||
|
loc: Loc::default(),
|
||||||
|
tt
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -81,6 +87,9 @@ pub fn tokenise(s: &str, file_p: &str) -> anyhow::Result<Vec<Token>> {
|
|||||||
buf.push(*c);
|
buf.push(*c);
|
||||||
last = *c;
|
last = *c;
|
||||||
}
|
}
|
||||||
|
let buf = buf
|
||||||
|
.replace("\\n", "\n")
|
||||||
|
.replace("\\r", "\r");
|
||||||
tokens.push(Token::new(TokenType::string(&buf, false), &loc));
|
tokens.push(Token::new(TokenType::string(&buf, false), &loc));
|
||||||
}
|
}
|
||||||
'\'' => {
|
'\'' => {
|
||||||
@@ -251,6 +260,7 @@ lazy_static::lazy_static!(
|
|||||||
("return", TokenType::Keyword(Keyword::Return)),
|
("return", TokenType::Keyword(Keyword::Return)),
|
||||||
("loop", TokenType::Keyword(Keyword::Loop)),
|
("loop", TokenType::Keyword(Keyword::Loop)),
|
||||||
("as", TokenType::Keyword(Keyword::As)),
|
("as", TokenType::Keyword(Keyword::As)),
|
||||||
|
("self", TokenType::Keyword(Keyword::Self_)),
|
||||||
("{", TokenType::Delim(Delimiter::CurlyL)),
|
("{", TokenType::Delim(Delimiter::CurlyL)),
|
||||||
("}", TokenType::Delim(Delimiter::CurlyR)),
|
("}", TokenType::Delim(Delimiter::CurlyR)),
|
||||||
("[", TokenType::Delim(Delimiter::SquareL)),
|
("[", TokenType::Delim(Delimiter::SquareL)),
|
||||||
@@ -272,7 +282,6 @@ lazy_static::lazy_static!(
|
|||||||
("%", TokenType::Punct(Punctuation::Mod)),
|
("%", TokenType::Punct(Punctuation::Mod)),
|
||||||
("<<", TokenType::Punct(Punctuation::Shl)),
|
("<<", TokenType::Punct(Punctuation::Shl)),
|
||||||
(">>", TokenType::Punct(Punctuation::Shr)),
|
(">>", TokenType::Punct(Punctuation::Shr)),
|
||||||
("&&", TokenType::Punct(Punctuation::AndAnd)),
|
|
||||||
("||", TokenType::Punct(Punctuation::OrOr)),
|
("||", TokenType::Punct(Punctuation::OrOr)),
|
||||||
("|", TokenType::Punct(Punctuation::Or)),
|
("|", TokenType::Punct(Punctuation::Or)),
|
||||||
(">", TokenType::Punct(Punctuation::Gt)),
|
(">", TokenType::Punct(Punctuation::Gt)),
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use core::panic;
|
use core::panic;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::parser::ast::expr::Path;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Ident(pub String);
|
pub struct Ident(pub String);
|
||||||
|
|
||||||
@@ -10,6 +12,15 @@ impl Display for Ident {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Ident {
|
||||||
|
pub fn as_path(self) -> Path {
|
||||||
|
Path(vec![self])
|
||||||
|
}
|
||||||
|
pub fn new(s: impl ToString) -> Self {
|
||||||
|
Self(s.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Number {
|
pub struct Number {
|
||||||
@@ -63,7 +74,7 @@ pub enum Keyword {
|
|||||||
Type, While, For, Break, Continue,
|
Type, While, For, Break, Continue,
|
||||||
Let, Const, Mut, Static,
|
Let, Const, Mut, Static,
|
||||||
True, False, Include, Extern, Return,
|
True, False, Include, Extern, Return,
|
||||||
As, Loop
|
As, Loop, Self_
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
@@ -162,7 +173,7 @@ impl TokenType {
|
|||||||
_ => panic!("Expected {}, got {self}", Self::ident(""))
|
_ => panic!("Expected {}, got {self}", Self::ident(""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn ident(s: &str) -> Self {
|
pub fn ident(s: impl ToString) -> Self {
|
||||||
Self::Ident(Ident(s.to_string()))
|
Self::Ident(Ident(s.to_string()))
|
||||||
}
|
}
|
||||||
pub fn number(val: usize, base: u8, signed: bool) -> Self {
|
pub fn number(val: usize, base: u8, signed: bool) -> Self {
|
||||||
|
|||||||
@@ -1,51 +1,619 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, panic};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail};
|
use anyhow::bail;
|
||||||
|
|
||||||
use crate::{common::{loc::LocBox, Loc}, parser::ast::{expr::{Block, Expr}, statement::{Statement, TypeAlias}, typ::Type, Ast, Program}};
|
use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Ident, Program, Punctuation, Scope, TokenType, expr::*, literal::Literal, statement::*, typ::Type}, validator::predefined::get_builtin_from_name};
|
||||||
|
|
||||||
pub mod predefined;
|
pub mod predefined;
|
||||||
|
|
||||||
struct Scope {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> {
|
pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> {
|
||||||
let Block(items) = prog.ast.clone();
|
let Block(mut items) = prog.ast.clone();
|
||||||
predefined::load_builtin(prog);
|
predefined::load_builtin(prog);
|
||||||
collect_types_and_constants(prog, &items);
|
collect_types_and_constants(prog, &mut items);
|
||||||
check_that_types_exist_for_items(prog, &items)?;
|
check_that_types_exist_for_items(prog, &items)?;
|
||||||
//dbg!(&prog.types);
|
//dbg!(&prog.types);
|
||||||
//dbg!(&prog.structs);
|
//dbg!(&prog.structs);
|
||||||
//dbg!(&prog.enums);
|
//dbg!(&prog.enums);
|
||||||
//dbg!(&prog.member_functions);
|
//dbg!(&prog.member_functions);
|
||||||
//dbg!(&prog.functions);
|
//dbg!(&prog.functions);
|
||||||
for item in items {
|
for item in items.iter_mut() {
|
||||||
match item {
|
match item {
|
||||||
Ast::Statement(stat) => {
|
Ast::Statement(stat) => validate_stat(prog, stat, CurrentState::Outside)?,
|
||||||
match stat.inner() {
|
|
||||||
Statement::Fn(func) => {}
|
|
||||||
Statement::Let { .. } => {
|
|
||||||
lerror!(stat.loc(), "Let statements are not allowed outside a function");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
Statement::ConstVar(var) => {
|
|
||||||
}
|
|
||||||
Statement::StaticVar(var) => {
|
|
||||||
}
|
|
||||||
Statement::Enum(enm) => {}
|
|
||||||
Statement::Struct(strct) => {}
|
|
||||||
Statement::TypeAlias(alias) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ast::Expr(_) => unreachable!()
|
Ast::Expr(_) => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prog.ast.0 = items;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
enum CurrentState {
|
||||||
|
InFunc,
|
||||||
|
Outside,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn validate_stat(prog: &mut Program, stat: &mut LocBox<Statement>, current_state: CurrentState) -> anyhow::Result<()> {
|
||||||
|
match stat.inner_mut() {
|
||||||
|
Statement::Fn(func) => validate_fn(prog, func)?,
|
||||||
|
Statement::Let(lt) if current_state == CurrentState::InFunc => validate_stat_let(prog, lt)?,
|
||||||
|
Statement::Let(_) if current_state == CurrentState::Outside => {
|
||||||
|
lerror!(stat.loc(), "Let statements are not allowed outside a function");
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
Statement::ConstVar(var) => validate_const_var(prog, var)?,
|
||||||
|
Statement::StaticVar(var) => validate_static_var(prog, var)?,
|
||||||
|
Statement::Enum(enm) => validate_enum(prog, enm)?,
|
||||||
|
Statement::Struct(strct) => validate_struct(prog, strct)?,
|
||||||
|
Statement::TypeAlias(alias) => validate_type_alias(prog, alias)?,
|
||||||
|
Statement::Let(_) => unreachable!(),
|
||||||
|
Statement::Include => (),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_stat_let(prog: &mut Program, lt: &mut Let) -> anyhow::Result<()> {
|
||||||
|
if let Some(val) = &mut lt.val && let Some(t) = &mut lt.typ {
|
||||||
|
let val_t = validate_expr(prog, val)?.unwrap();
|
||||||
|
if val_t != *t.inner_mut() {
|
||||||
|
lerror!(t.loc(), "Cannot assign {val_t} to {}", t.inner_mut());
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(val) = &mut lt.val && let None = &mut lt.typ {
|
||||||
|
let Some(t) = validate_expr(prog, val)? else {
|
||||||
|
lerror!(val.loc(), "Expected a type, go none");
|
||||||
|
bail!("");
|
||||||
|
};
|
||||||
|
lt.typ = Some(LocBox::new(val.loc(), t));
|
||||||
|
}
|
||||||
|
if let Some(scope) = &mut prog.scope {
|
||||||
|
scope.vars.insert(lt.name.clone(), LocBox::new(&Default::default(), lt.clone()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_ast(prog: &mut Program, ast: &mut Ast) -> anyhow::Result<Option<Type>> {
|
||||||
|
match ast {
|
||||||
|
Ast::Expr(expr) => {
|
||||||
|
validate_expr(prog, expr)
|
||||||
|
}
|
||||||
|
Ast::Statement(stat) => {
|
||||||
|
validate_stat(prog, stat, CurrentState::InFunc)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate_expr(prog: &mut Program, expr: &mut LocBox<Expr>) -> anyhow::Result<Option<Type>> {
|
||||||
|
match expr.inner_mut() {
|
||||||
|
Expr::Self_ { strct } => {
|
||||||
|
if let Some(name) = &prog.curr_struct {
|
||||||
|
*strct = Some(name.clone());
|
||||||
|
let mut t = Type::Owned(name.clone());
|
||||||
|
t.convert_owned_to_real_type(prog);
|
||||||
|
Ok(Some(t))
|
||||||
|
} else {
|
||||||
|
lerror!(expr.loc(), "");
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Break | Expr::Continue => Ok(None),
|
||||||
|
Expr::Return(ret) => {
|
||||||
|
if let Some(expr) = &mut**ret {
|
||||||
|
validate_expr(prog, expr)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Struct(id, strct) => {
|
||||||
|
// this is probably fucked so fix this later
|
||||||
|
let name = strct.path.0.last().expect("Paths always have at least one part");
|
||||||
|
let typ = Type::Owned(name.clone());
|
||||||
|
// this is so ass, this checks if struct exists
|
||||||
|
validate_type(prog, &LocBox::new(&Loc::default(), typ.clone()))?;
|
||||||
|
|
||||||
|
for field in strct.fields.iter_mut() {
|
||||||
|
validate_expr(prog, field.1)?;
|
||||||
|
}
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
(*strct).hash(&mut hasher);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
*id = hash.to_string();
|
||||||
|
|
||||||
|
prog.struct_literals.insert(id.clone(), strct.clone());
|
||||||
|
|
||||||
|
Ok(Some(typ))
|
||||||
|
},
|
||||||
|
Expr::If(ifs) => {
|
||||||
|
validate_expr(prog, &mut ifs.test)?;
|
||||||
|
for item in ifs.body.0.iter_mut() {
|
||||||
|
validate_ast(prog, item)?;
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Expr::Path(path) => {
|
||||||
|
// TODO: Path hell TBD
|
||||||
|
Ok(Some(Type::Owned(path.0.last().unwrap().clone())))
|
||||||
|
}
|
||||||
|
Expr::Group(group) => validate_expr(prog, group),
|
||||||
|
Expr::Call { path, params } => {
|
||||||
|
let loc = path.loc().clone();
|
||||||
|
let Expr::Path(path) = path.inner_mut() else {
|
||||||
|
panic!("fml");
|
||||||
|
};
|
||||||
|
|
||||||
|
match path.0.len() {
|
||||||
|
1 => {
|
||||||
|
let f = prog.functions.get(&path.0[0]).cloned();
|
||||||
|
match f {
|
||||||
|
Some(func) => {
|
||||||
|
for (i, param) in params.0.iter_mut().enumerate() {
|
||||||
|
let mut ft = func.inner().params[i].1.inner().clone();
|
||||||
|
let t = validate_expr(prog, param)?.clone();
|
||||||
|
match t {
|
||||||
|
Some(mut t) => {
|
||||||
|
t.convert_owned_to_real_type(prog);
|
||||||
|
ft.convert_owned_to_real_type(prog);
|
||||||
|
let t = t.get_variable_type(prog)?;
|
||||||
|
let ft = ft.get_variable_type(prog)?;
|
||||||
|
if t != ft {
|
||||||
|
lerror!(param.loc(), "expected {ft}, got {t}");
|
||||||
|
bail!("owo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
lerror!(param.loc(), "expected {ft}, got Nothing");
|
||||||
|
bail!("nya")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(t) = &func.inner().ret_type {
|
||||||
|
Ok(Some(t.inner().clone()))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
lerror!(loc, "Could not find function {}", path);
|
||||||
|
panic!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
let s = prog.member_functions.get(&path.0[0]).unwrap();
|
||||||
|
let f = s.get(&path.0[1]).cloned();
|
||||||
|
match f {
|
||||||
|
Some(func) => {
|
||||||
|
for (i, param) in params.0.iter_mut().enumerate() {
|
||||||
|
let ft = func.inner().params[i].1.inner().clone();
|
||||||
|
let t = validate_expr(prog, param)?.clone();
|
||||||
|
match t {
|
||||||
|
Some(t) => {
|
||||||
|
if t.get_absolute_value(prog)? != ft.get_absolute_value(prog)? {
|
||||||
|
lerror!(param.loc(), "expected {ft}, got {}", t);
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
lerror!(param.loc(), "expected {ft}, got Nothing");
|
||||||
|
bail!("")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(t) = &func.inner().ret_type {
|
||||||
|
Ok(Some(t.inner().clone()))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
lerror!(loc, "Could not find function {}", path);
|
||||||
|
panic!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
Expr::MethodCall { left, strct, params } => {
|
||||||
|
let var_t;
|
||||||
|
let method_name;
|
||||||
|
match left.inner_mut() {
|
||||||
|
Expr::FieldAccess { left, right, .. } |
|
||||||
|
Expr::PtrFieldAccess { left, right, .. } => {
|
||||||
|
var_t = validate_expr(prog, &mut left.clone().unwrap())?;
|
||||||
|
let name = validate_expr(prog, right)?;
|
||||||
|
match name.unwrap() {
|
||||||
|
Type::Owned(name) => method_name = name,
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
let Type::Owned(struct_name) = var_t.unwrap().get_absolute_value(prog)? else {
|
||||||
|
panic!("fml");
|
||||||
|
};
|
||||||
|
*strct = Some(struct_name.clone());
|
||||||
|
let mut strct = prog.member_functions.get_mut(&struct_name).unwrap().clone();
|
||||||
|
let func = strct.get_mut(&method_name).unwrap();
|
||||||
|
|
||||||
|
for (i, param) in params.0.iter_mut().enumerate() {
|
||||||
|
let t = validate_expr(prog, param)?;
|
||||||
|
let ft = func.inner_mut().params[i].1.inner_mut();
|
||||||
|
if t.as_ref() != Some(ft) {
|
||||||
|
lerror!(param.loc(), "expected {ft}, got {}", t.unwrap());
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(t) = &mut func.inner_mut().ret_type {
|
||||||
|
Ok(Some(t.inner_mut().clone()))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::ForLoop { init, test, on_loop, body } => {
|
||||||
|
let _ = validate_ast(prog, init)?;
|
||||||
|
let _ = validate_expr(prog, test)?;
|
||||||
|
let _ = validate_expr(prog, on_loop)?;
|
||||||
|
for item in body.0.iter_mut() {
|
||||||
|
let _ = validate_ast(prog, item)?;
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
},
|
||||||
|
Expr::Cast { left, right } => {
|
||||||
|
validate_expr(prog, left)?;
|
||||||
|
Ok(Some(right.inner_mut().clone()))
|
||||||
|
}
|
||||||
|
Expr::Literal(id, lit) => {
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
(*lit).hash(&mut hasher);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
*id = hash.to_string();
|
||||||
|
match lit {
|
||||||
|
Literal::Array { items, item_size } => {
|
||||||
|
for item in items {
|
||||||
|
let typ = validate_expr(prog, item)?;
|
||||||
|
if item_size.is_none() {
|
||||||
|
*item_size = Some(typ.unwrap().get_variable_type(prog)?.size_of(prog)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Literal::ArrayRepeat { val, item_size, count: _ } => {
|
||||||
|
let typ = validate_expr(prog, val)?;
|
||||||
|
*item_size = Some(typ.unwrap().size_of(prog)?);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
prog.literals.insert(id.clone(), lit.clone());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Ok(Some(lit.to_type(prog)?))
|
||||||
|
}
|
||||||
|
Expr::UnOp { typ, right } => {
|
||||||
|
match typ {
|
||||||
|
Punctuation::Not => {
|
||||||
|
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||||
|
if !t.is_bool(prog) {
|
||||||
|
lerror!(right.loc(), "Expected bool, got {t}");
|
||||||
|
}
|
||||||
|
Ok(Some(t))
|
||||||
|
}
|
||||||
|
Punctuation::Minus |
|
||||||
|
Punctuation::Plus => {
|
||||||
|
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||||
|
if !t.is_numeric(prog) {
|
||||||
|
lerror!(right.loc(), "Expected number, got {t}");
|
||||||
|
}
|
||||||
|
Ok(Some(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
Punctuation::Ampersand => {
|
||||||
|
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||||
|
Ok(Some(Type::Ref { inner: Box::new(t), mutable: false }))
|
||||||
|
}
|
||||||
|
Punctuation::Star => {
|
||||||
|
let t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||||
|
if !t.is_ptr(prog) {
|
||||||
|
lerror!(right.loc(), "Expected pointer, got {t}");
|
||||||
|
}
|
||||||
|
|
||||||
|
match t {
|
||||||
|
Type::Ref { inner, .. } => Ok(Some(*inner)),
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Expr::BinOp { typ, left, right, signed } => {
|
||||||
|
match typ {
|
||||||
|
Punctuation::Ampersand |
|
||||||
|
Punctuation::Or |
|
||||||
|
Punctuation::Xor |
|
||||||
|
Punctuation::Plus |
|
||||||
|
Punctuation::Minus |
|
||||||
|
Punctuation::Div |
|
||||||
|
Punctuation::Star |
|
||||||
|
Punctuation::Mod |
|
||||||
|
Punctuation::Shl |
|
||||||
|
Punctuation::Shr => {
|
||||||
|
let t1_s;
|
||||||
|
let t2_s;
|
||||||
|
let t1 = validate_expr(prog, left)?.unwrap().get_absolute_value(prog)?;
|
||||||
|
let t2 = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||||
|
if !(t1.is_numeric(prog) || t1.is_ptr(prog)) {
|
||||||
|
lerror!(right.loc(), "Expected number, got {t1}");
|
||||||
|
}
|
||||||
|
if !(t2.is_numeric(prog) || t2.is_ptr(prog)) {
|
||||||
|
lerror!(right.loc(), "Expected number, got {t2}");
|
||||||
|
}
|
||||||
|
match &t1 {
|
||||||
|
Type::Builtin { name: _, size, .. } => t1_s = *size,
|
||||||
|
_ => unreachable!("1: {t1:?}\n2: {t2:?}")
|
||||||
|
}
|
||||||
|
|
||||||
|
match &t2 {
|
||||||
|
Type::Builtin { name: _, size, .. } => t2_s = *size,
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
*signed = t1.is_signed(prog).expect("verified as numeric");
|
||||||
|
if t2_s > t1_s {
|
||||||
|
Ok(Some(t2))
|
||||||
|
} else {
|
||||||
|
Ok(Some(t1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Punctuation::EqEq |
|
||||||
|
Punctuation::Lt |
|
||||||
|
Punctuation::Gt |
|
||||||
|
Punctuation::Le |
|
||||||
|
Punctuation::Ge |
|
||||||
|
Punctuation::AndAnd |
|
||||||
|
Punctuation::OrOr => {
|
||||||
|
let t1 = validate_expr(prog, left)?.unwrap().get_absolute_value(prog)?;
|
||||||
|
let t2 = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||||
|
if !(t1.is_numeric(prog) || t1.is_ptr(prog)) {
|
||||||
|
lerror!(right.loc(), "Expected number or pointer, got {t1}");
|
||||||
|
}
|
||||||
|
if !(t2.is_numeric(prog) || t2.is_ptr(prog)) {
|
||||||
|
lerror!(right.loc(), "Expected number or pointer, got {t2}");
|
||||||
|
}
|
||||||
|
*signed = t1.is_signed(prog).expect("verified as numeric");
|
||||||
|
Ok(Some(t1))
|
||||||
|
}
|
||||||
|
|
||||||
|
Punctuation::AddEq |
|
||||||
|
Punctuation::SubEq |
|
||||||
|
Punctuation::DivEq |
|
||||||
|
Punctuation::MulEq |
|
||||||
|
Punctuation::ModEq |
|
||||||
|
Punctuation::ShlEq |
|
||||||
|
Punctuation::ShrEq |
|
||||||
|
Punctuation::AndEq |
|
||||||
|
Punctuation::OrEq |
|
||||||
|
Punctuation::XorEq => {
|
||||||
|
let var = validate_expr(prog, left)?.unwrap();
|
||||||
|
let var_t = var.get_absolute_value(prog)?;
|
||||||
|
let val_t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||||
|
|
||||||
|
if !((var_t.is_numeric(prog) || var_t.is_ptr(prog)) && (val_t.is_numeric(prog) || val_t.is_ptr(prog))) {
|
||||||
|
lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}");
|
||||||
|
bail!("");
|
||||||
|
}
|
||||||
|
|
||||||
|
*signed = var_t.is_signed(prog).expect("verified as numeric");
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Punctuation::Neq |
|
||||||
|
Punctuation::Eq => {
|
||||||
|
let var = validate_expr(prog, left)?.unwrap();
|
||||||
|
let var_t = var.get_absolute_value(prog)?;
|
||||||
|
let val_t = validate_expr(prog, right)?.unwrap().get_absolute_value(prog)?;
|
||||||
|
|
||||||
|
if !(var_t == val_t || var_t.is_numeric(prog) && val_t.is_numeric(prog)) {
|
||||||
|
lerror!(left.loc(), "Mismatched types, assigning {val_t} to {var_t}");
|
||||||
|
bail!("");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
v => unreachable!("{v:?}")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Expr::ArrayIndex { name, index } => {
|
||||||
|
let left = validate_expr(prog, name)?;
|
||||||
|
let Some(left) = left else {
|
||||||
|
lerror!(name.loc(), "expected value, got nothing, cannot index nothing");
|
||||||
|
bail!("")
|
||||||
|
};
|
||||||
|
fn f(prog: &mut Program, left: &Type, loc: &Loc, index: &LocBox<Expr>) -> anyhow::Result<Option<Type>> {
|
||||||
|
match left {
|
||||||
|
Type::SizedArray { inner, count } => {
|
||||||
|
let val1 = count.inner().as_number(prog).unwrap();
|
||||||
|
let val2 = index.inner().as_number(prog).unwrap();
|
||||||
|
if val1.signed && (val1.val as isize) < 0 {
|
||||||
|
lerror!(index.loc(), "Cannot index bellow 0");
|
||||||
|
bail!("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if val1.val <= val2.val {
|
||||||
|
lerror!(index.loc(), "Cannot index outside aray boundaries");
|
||||||
|
bail!("");
|
||||||
|
}
|
||||||
|
Ok(Some(*inner.clone()))
|
||||||
|
}
|
||||||
|
Type::UnsizedArray { inner } => {
|
||||||
|
Ok(Some(*inner.clone()))
|
||||||
|
}
|
||||||
|
Type::Owned(name) => {
|
||||||
|
let t = prog.types.get(name).cloned();
|
||||||
|
if let Some(t) = &t {
|
||||||
|
f(prog, t.inner(), t.loc(), index)
|
||||||
|
} else {
|
||||||
|
lerror!(loc, "Unknown type {name}");
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Ref { .. } |
|
||||||
|
Type::FnPtr(_) |
|
||||||
|
Type::Builtin { .. } => {
|
||||||
|
lerror!(loc, "Numeric, pointer, and void types are not indexable (TODO: Maybe allow indexing pointers?)");
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(prog, &left, name.loc(), &index)
|
||||||
|
},
|
||||||
|
Expr::PtrFieldAccess { left, right, offset, l_typ, r_typ } => {
|
||||||
|
let left = validate_expr(prog, &mut left.clone().unwrap())?;
|
||||||
|
let right = validate_expr(prog, &mut right.clone())?.unwrap();
|
||||||
|
let Type::Owned(right) = right else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
let mut left = left.unwrap();
|
||||||
|
left.convert_owned_to_real_type(prog);
|
||||||
|
dbg!(&prog.curr_fn_args);
|
||||||
|
*l_typ = Some(Box::new(left.clone()));
|
||||||
|
match left {
|
||||||
|
Type::Ref { inner, mutable: _ } => {
|
||||||
|
match *inner {
|
||||||
|
Type::Owned(name) => {
|
||||||
|
if let Some(strct) = prog.structs.get(&name) {
|
||||||
|
for field in strct.inner().fields.iter() {
|
||||||
|
if field.0 == right {
|
||||||
|
*r_typ = Some(Box::new(field.1.inner().clone()));
|
||||||
|
*offset = strct.inner().clone().get_offset_of(prog, &right)?;
|
||||||
|
info!("Offset {right:?} = {offset}");
|
||||||
|
return Ok(Some(field.1.inner().clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("couldnt find struct {name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v => panic!("{v:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v => panic!("{} {v:?}", expr.loc()),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
},
|
||||||
|
Expr::FieldAccess { left, right, offset, l_typ, r_typ } => {
|
||||||
|
let left = validate_expr(prog, &mut left.clone().unwrap())?;
|
||||||
|
let right = validate_expr(prog, &mut right.clone())?.unwrap();
|
||||||
|
let Type::Owned(right) = right else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
let mut left = left.unwrap();
|
||||||
|
left.convert_owned_to_real_type(prog);
|
||||||
|
*l_typ = Some(Box::new(left.clone()));
|
||||||
|
match left {
|
||||||
|
Type::Owned(name) => {
|
||||||
|
if let Some(strct) = prog.structs.get(&name) {
|
||||||
|
for field in strct.inner().fields.iter() {
|
||||||
|
if field.0 == right {
|
||||||
|
*r_typ = Some(Box::new(field.1.inner().clone()));
|
||||||
|
*offset = strct.inner().clone().get_offset_of(prog, &right)?;
|
||||||
|
return Ok(Some(field.1.inner().clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("couldnt find struct {name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v => panic!("{v:?}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
},
|
||||||
|
Expr::WhileLoop { test, body } => {
|
||||||
|
let _ = validate_expr(prog, test.as_mut())?;
|
||||||
|
for item in body.0.iter_mut() {
|
||||||
|
let _ = validate_ast(prog, item)?;
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
|
||||||
|
},
|
||||||
|
Expr::InfLoop { body } => {
|
||||||
|
for item in body.0.iter_mut() {
|
||||||
|
let _ = validate_ast(prog, item)?;
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_fn(prog: &mut Program, func: &mut Function) -> anyhow::Result<()> {
|
||||||
|
prog.scope = Some(Scope::default());
|
||||||
|
prog.curr_struct = func.struct_name.clone();
|
||||||
|
dbg!(&func.params);
|
||||||
|
for param in &func.params {
|
||||||
|
let t = validate_type(prog, ¶m.1)?;
|
||||||
|
dbg!(&t);
|
||||||
|
prog.curr_fn_args.insert(param.0.clone(), LocBox::new(&Loc::default(), t.clone()));
|
||||||
|
}
|
||||||
|
if let Some(body) = &mut func.body {
|
||||||
|
for item in body.0.iter_mut() {
|
||||||
|
validate_ast(prog, item)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prog.curr_struct = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_const_var(prog: &mut Program, var: &mut ConstVar) -> anyhow::Result<()> {
|
||||||
|
validate_type(prog, &var.typ)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_static_var(prog: &mut Program, var: &mut StaticVar) -> anyhow::Result<()> {
|
||||||
|
validate_type(prog, &var.typ)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_enum(_: &mut Program, _: &Enum) -> anyhow::Result<()> {
|
||||||
|
// Enum has no actual types
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_struct(prog: &mut Program, strct: &mut Struct) -> anyhow::Result<()> {
|
||||||
|
for field in &strct.fields {
|
||||||
|
if let Err(e) = validate_type(prog, &field.1) {
|
||||||
|
error!("Could not find type in field {}::{}", strct.name, field.0);
|
||||||
|
anyhow::bail!(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_type_alias(prog: &mut Program, alias: &TypeAlias) -> anyhow::Result<()> {
|
||||||
|
validate_type(prog, &alias.typ)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> anyhow::Result<()> {
|
fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> anyhow::Result<()> {
|
||||||
let mut errored = false;
|
let mut errored = false;
|
||||||
for item in items {
|
for item in items {
|
||||||
@@ -56,20 +624,21 @@ fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> any
|
|||||||
for (name, t) in &func.params {
|
for (name, t) in &func.params {
|
||||||
if let Err(_) = validate_type(prog, t) {
|
if let Err(_) = validate_type(prog, t) {
|
||||||
if let Some(sn) = &func.struct_name {
|
if let Some(sn) = &func.struct_name {
|
||||||
lerror!(t.loc(), "Type '{}', of argument, '{name}' in function '{}::{}' does not exist", t.inner(), sn, func.name);
|
lerror!(t.loc(), "Type '{}', of argument, '{name}' in function '{}::{}' does not exist", t.inner(), sn, func.get_full_name_pretty());
|
||||||
} else {
|
} else {
|
||||||
lerror!(t.loc(), "Type '{}', of argument, '{name}' in function '{}' does not exist", t.inner(), func.name);
|
lerror!(t.loc(), "Type '{}', of argument, '{name}' in function '{}' does not exist", t.inner(), func.get_full_name_pretty());
|
||||||
}
|
}
|
||||||
errored = true;
|
errored = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(ret_typ) = &func.ret_type {
|
if let Some(ret_typ) = &func.ret_type {
|
||||||
if let Err(_) = validate_type(prog, &ret_typ) {
|
if let Err(_) = validate_type(prog, &ret_typ) {
|
||||||
lerror!(ret_typ.loc(), "Return type '{}' of function '{}' does not exist", ret_typ.inner(), func.name);
|
lerror!(ret_typ.loc(), "Return type '{}' of function '{}' does not exist", ret_typ.inner(), func.get_full_name_pretty());
|
||||||
errored = true;
|
errored = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Statement::Include |
|
||||||
Statement::Let { .. } => (),
|
Statement::Let { .. } => (),
|
||||||
Statement::ConstVar(var) => {
|
Statement::ConstVar(var) => {
|
||||||
validate_type(prog, &var.typ)?;
|
validate_type(prog, &var.typ)?;
|
||||||
@@ -79,7 +648,7 @@ fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> any
|
|||||||
}
|
}
|
||||||
Statement::Enum(_) => (),
|
Statement::Enum(_) => (),
|
||||||
Statement::Struct(strct) => {
|
Statement::Struct(strct) => {
|
||||||
for (name, t) in &strct.fields {
|
for (name, t) in strct.fields.iter() {
|
||||||
if let Err(_) = validate_type(prog, t) {
|
if let Err(_) = validate_type(prog, t) {
|
||||||
lerror!(t.loc(), "Type '{}', of field, '{}.{name}' does not exist", t.inner(), strct.name);
|
lerror!(t.loc(), "Type '{}', of field, '{}.{name}' does not exist", t.inner(), strct.name);
|
||||||
errored = true;
|
errored = true;
|
||||||
@@ -104,57 +673,66 @@ fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> any
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_type(prog: &mut Program, typ: &LocBox<Type>) -> anyhow::Result<()> {
|
pub fn validate_type(prog: &mut Program, typ: &LocBox<Type>) -> anyhow::Result<Type> {
|
||||||
fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> anyhow::Result<()> {
|
fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> anyhow::Result<Type> {
|
||||||
match typ {
|
match typ {
|
||||||
Type::SizedArray { inner, .. } |
|
Type::SizedArray { .. } |
|
||||||
Type::UnsizedArray { inner, .. } |
|
Type::UnsizedArray { .. } |
|
||||||
Type::Ref { inner, .. } => f(prog, inner, loc),
|
Type::FnPtr(_) |
|
||||||
Type::Owned(typ) => {
|
Type::Ref { .. } => Ok(typ.clone()),
|
||||||
if prog.enums.get(typ).is_some() ||
|
Type::Owned(ident) => {
|
||||||
prog.types.get(typ).is_some() ||
|
if let Some(builtin) = get_builtin_from_name(&ident.0) {
|
||||||
prog.structs.get(typ).is_some() {
|
Ok(builtin)
|
||||||
Ok(())
|
} else
|
||||||
|
if prog.enums.get(ident).is_some() ||
|
||||||
|
prog.types.get(ident).is_some() ||
|
||||||
|
prog.structs.get(ident).is_some() {
|
||||||
|
Ok(typ.clone())
|
||||||
} else {
|
} else {
|
||||||
// lerror!(loc, "Could not find type '{}'", typ.0);
|
lerror!(loc, "Could not find type '{}'", ident.0);
|
||||||
bail!("")
|
bail!("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Type::Builtin { .. } => Ok(typ.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f(prog, typ.inner(), typ.loc())
|
f(prog, &typ.inner(), typ.loc())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn collect_types_and_constants(prog: &mut Program, items: &Vec<Ast>) {
|
fn collect_types_and_constants(prog: &mut Program, items: &mut Vec<Ast>) {
|
||||||
for item in items {
|
for item in items.iter_mut() {
|
||||||
match item {
|
match item {
|
||||||
Ast::Statement(stat) => {
|
Ast::Statement(stat) => {
|
||||||
match stat.inner() {
|
let loc = stat.loc().clone();
|
||||||
|
match stat.inner_mut() {
|
||||||
Statement::Fn(func)=> {
|
Statement::Fn(func)=> {
|
||||||
if let Some(struct_name) = &func.struct_name {
|
if let Some(name) = &func.name {
|
||||||
if let Some(v) = prog.member_functions.get_mut(&struct_name) {
|
if let Some(struct_name) = &func.struct_name {
|
||||||
v.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone()));
|
if let Some(v) = prog.member_functions.get_mut(&struct_name) {
|
||||||
|
v.insert(name.clone(), LocBox::new(&loc, func.clone()));
|
||||||
|
} else {
|
||||||
|
let mut v = HashMap::new();
|
||||||
|
v.insert(name.clone(), LocBox::new(&loc, func.clone()));
|
||||||
|
prog.member_functions.insert(struct_name.clone(), v);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut v = HashMap::new();
|
prog.functions.insert(name.clone(), LocBox::new(&loc, func.clone()));
|
||||||
v.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone()));
|
|
||||||
prog.member_functions.insert(struct_name.clone(), v);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
prog.functions.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Enum(enm) => {
|
Statement::Enum(enm) => {
|
||||||
prog.enums.insert(enm.name.clone(), LocBox::new(stat.loc(), enm.clone()));
|
prog.enums.insert(enm.name.clone(), LocBox::new(&loc, enm.clone()));
|
||||||
}
|
}
|
||||||
Statement::Struct(strct) => {
|
Statement::Struct(strct) => {
|
||||||
prog.structs.insert(strct.name.clone(), LocBox::new(stat.loc(), strct.clone()));
|
prog.structs.insert(strct.name.clone(), LocBox::new(&loc, strct.clone()));
|
||||||
}
|
}
|
||||||
Statement::TypeAlias(alias) => {
|
Statement::TypeAlias(alias) => {
|
||||||
let typ = alias.clone().typ.inner().clone();
|
let typ = alias.clone().typ.inner_mut().clone();
|
||||||
prog.types.insert(alias.name.clone(), predefined::TypeType::Normal(LocBox::new(stat.loc(), typ)));
|
prog.types.insert(alias.name.clone(), LocBox::new(stat.loc(), typ));
|
||||||
}
|
}
|
||||||
|
Statement::Include |
|
||||||
Statement::Let { .. } => (),
|
Statement::Let { .. } => (),
|
||||||
Statement::ConstVar(var) => {
|
Statement::ConstVar(var) => {
|
||||||
prog.const_vars.insert(var.name.clone(), var.clone());
|
prog.const_vars.insert(var.name.clone(), var.clone());
|
||||||
@@ -164,7 +742,33 @@ fn collect_types_and_constants(prog: &mut Program, items: &Vec<Ast>) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ast::Expr(_) => unreachable!()
|
Ast::Expr(expr) => {
|
||||||
|
match expr.inner_mut() {
|
||||||
|
Expr::Literal(id, val) => {
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
(*val).hash(&mut hasher);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
*id = hash.to_string();
|
||||||
|
|
||||||
|
prog.literals.insert(id.clone(), val.clone());
|
||||||
|
}
|
||||||
|
Expr::Struct(id, strct) => {
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
(*strct).hash(&mut hasher);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
*id = hash.to_string();
|
||||||
|
|
||||||
|
prog.struct_literals.insert(id.clone(), strct.clone());
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,68 +1,157 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use crate::common::Loc;
|
use crate::common::Loc;
|
||||||
|
use crate::parser::ast::expr::Path;
|
||||||
use crate::parser::typ::parse_type;
|
use crate::parser::typ::parse_type;
|
||||||
use crate::{common::loc::LocBox, parser::ast::{statement::Function, typ::Type, Ident, Program}};
|
use crate::{common::loc::LocBox, parser::ast::{statement::Function, typ::Type, Ident, Program}};
|
||||||
|
|
||||||
#[cfg(target_arch="x86_64")]
|
#[cfg(target_arch="x86_64")]
|
||||||
const SIZE: usize = 8;
|
pub const SIZE: usize = 8;
|
||||||
#[cfg(target_arch="x86")]
|
#[cfg(target_arch="x86")]
|
||||||
const SIZE: usize = 4;
|
pub const SIZE: usize = 4;
|
||||||
|
|
||||||
|
pub struct BuiltinType;
|
||||||
|
|
||||||
|
impl BuiltinType {
|
||||||
|
pub fn void() -> Type {
|
||||||
|
Type::new_builtin("void", 0, false)
|
||||||
|
}
|
||||||
|
pub fn usize() -> Type {
|
||||||
|
Type::new_builtin("usize", SIZE as u8, false)
|
||||||
|
}
|
||||||
|
pub fn isize() -> Type {
|
||||||
|
Type::new_builtin("isize", SIZE as u8, true)
|
||||||
|
}
|
||||||
|
pub fn u8() -> Type {
|
||||||
|
Type::new_builtin("u8", 1, false)
|
||||||
|
}
|
||||||
|
pub fn u16() -> Type {
|
||||||
|
Type::new_builtin("u16", 2, false)
|
||||||
|
}
|
||||||
|
pub fn u32() -> Type {
|
||||||
|
Type::new_builtin("u32", 4, false)
|
||||||
|
}
|
||||||
|
pub fn u64() -> Type {
|
||||||
|
Type::new_builtin("u64", 8, false)
|
||||||
|
}
|
||||||
|
pub fn i8() -> Type {
|
||||||
|
Type::new_builtin("i8", 1, true)
|
||||||
|
}
|
||||||
|
pub fn i16() -> Type {
|
||||||
|
Type::new_builtin("i16", 2, true)
|
||||||
|
}
|
||||||
|
pub fn i32() -> Type {
|
||||||
|
Type::new_builtin("i32", 4, true)
|
||||||
|
}
|
||||||
|
pub fn i64() -> Type {
|
||||||
|
Type::new_builtin("i64", 8, true)
|
||||||
|
}
|
||||||
|
pub fn bool() -> Type {
|
||||||
|
Type::new_builtin("bool", 1, true)
|
||||||
|
}
|
||||||
|
pub fn char() -> Type {
|
||||||
|
Type::new_builtin("char", 1, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
lazy_static!(
|
lazy_static!(
|
||||||
pub static ref TYPES_RAW: HashMap<&'static str, (usize, bool)> = [
|
pub static ref TYPES_RAW: Vec<Type> = [
|
||||||
("void", (0, false)),
|
BuiltinType::void(),
|
||||||
("usize", (SIZE, false)),
|
BuiltinType::usize(),
|
||||||
("isize", (SIZE, true)),
|
BuiltinType::isize(),
|
||||||
("u8", (1, false)),
|
BuiltinType::u8(),
|
||||||
("u16", (2, false)),
|
BuiltinType::u16(),
|
||||||
("u32", (4, false)),
|
BuiltinType::u32(),
|
||||||
("u64", (8, false)),
|
BuiltinType::u64(),
|
||||||
("i8", (1, true)),
|
BuiltinType::i8(),
|
||||||
("i16", (2, true)),
|
BuiltinType::i16(),
|
||||||
("i32", (4, true)),
|
BuiltinType::i32(),
|
||||||
("i64", (8, true)),
|
BuiltinType::i64(),
|
||||||
].into();
|
BuiltinType::bool(),
|
||||||
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, &'static str)>, &'static str)> = [
|
BuiltinType::char()
|
||||||
("syscall", (vec![
|
].to_vec();
|
||||||
("arg_count", "u8"),
|
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, Type)>, Type)> = [
|
||||||
("sc_num", "u8"),
|
("__INTERNAL_syscall0", (vec![
|
||||||
("args", "&[&void]")
|
("sc_num", BuiltinType::usize()),
|
||||||
], "usize")),
|
], BuiltinType::usize())),
|
||||||
|
("__INTERNAL_syscall1", (vec![
|
||||||
|
("sc_num", BuiltinType::usize()),
|
||||||
|
("arg0", BuiltinType::void().as_ref()),
|
||||||
|
], BuiltinType::usize())),
|
||||||
|
("__INTERNAL_syscall2", (vec![
|
||||||
|
("sc_num", BuiltinType::usize()),
|
||||||
|
("arg0", BuiltinType::void().as_ref()),
|
||||||
|
("arg1", BuiltinType::void().as_ref()),
|
||||||
|
], BuiltinType::usize())),
|
||||||
|
("__INTERNAL_syscall3", (vec![
|
||||||
|
("sc_num", BuiltinType::usize()),
|
||||||
|
("arg0", BuiltinType::void().as_ref()),
|
||||||
|
("arg1", BuiltinType::void().as_ref()),
|
||||||
|
("arg2", BuiltinType::void().as_ref()),
|
||||||
|
], BuiltinType::usize())),
|
||||||
|
("__INTERNAL_syscall4", (vec![
|
||||||
|
("sc_num", BuiltinType::usize()),
|
||||||
|
("arg0", BuiltinType::void().as_ref()),
|
||||||
|
("arg1", BuiltinType::void().as_ref()),
|
||||||
|
("arg2", BuiltinType::void().as_ref()),
|
||||||
|
("arg3", BuiltinType::void().as_ref()),
|
||||||
|
], BuiltinType::usize())),
|
||||||
|
("__INTERNAL_syscall5", (vec![
|
||||||
|
("sc_num", BuiltinType::usize()),
|
||||||
|
("arg0", BuiltinType::void().as_ref()),
|
||||||
|
("arg1", BuiltinType::void().as_ref()),
|
||||||
|
("arg2", BuiltinType::void().as_ref()),
|
||||||
|
("arg3", BuiltinType::void().as_ref()),
|
||||||
|
("arg4", BuiltinType::void().as_ref()),
|
||||||
|
], BuiltinType::usize())),
|
||||||
|
("__INTERNAL_syscall6", (vec![
|
||||||
|
("sc_num", BuiltinType::usize()),
|
||||||
|
("arg0", BuiltinType::void().as_ref()),
|
||||||
|
("arg1", BuiltinType::void().as_ref()),
|
||||||
|
("arg2", BuiltinType::void().as_ref()),
|
||||||
|
("arg3", BuiltinType::void().as_ref()),
|
||||||
|
("arg4", BuiltinType::void().as_ref()),
|
||||||
|
("arg5", BuiltinType::void().as_ref()),
|
||||||
|
], BuiltinType::usize())),
|
||||||
].into();
|
].into();
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
pub fn get_builtin_from_name(type_name: &str) -> Option<Type> {
|
||||||
pub enum TypeType {
|
for t in TYPES_RAW.iter() {
|
||||||
Normal(LocBox<Type>),
|
match t {
|
||||||
Builtin {
|
Type::Builtin { name, .. } if name.as_str() == type_name => {
|
||||||
size: usize,
|
return Some(t.clone());
|
||||||
signed: bool,
|
}
|
||||||
|
Type::Builtin { .. } => (),
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_builtin(prog: &mut Program) {
|
pub fn load_builtin(prog: &mut Program) {
|
||||||
for (name, (size, signed)) in TYPES_RAW.iter() {
|
let loc = Loc::new("(internal)", 0, 0);
|
||||||
prog.types.insert(Ident(name.to_string()), TypeType::Builtin{ size: *size, signed: *signed });
|
for t in TYPES_RAW.iter() {
|
||||||
|
let Type::Builtin { name, size, signed } = t else {unreachable!()};
|
||||||
|
prog.types.insert(
|
||||||
|
Ident(name.clone()),
|
||||||
|
LocBox::new(&loc, Type::Builtin{ name: name.to_string(), size: *size as u8, signed: *signed })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (name, (args, ret_typ)) in FUNCTIONS.iter() {
|
for (name, (args, ret_typ)) in FUNCTIONS.iter() {
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
let mut ret_type = None;
|
let mut ret_type = None;
|
||||||
if ret_typ.len() > 0 {
|
if !ret_typ.is_void() {
|
||||||
let mut ret_t_tokens = crate::tokeniser::tokenise(&ret_typ, "(internal)").unwrap();
|
|
||||||
let typ = parse_type(&mut ret_t_tokens).unwrap();
|
ret_type = Some(Box::new(LocBox::new(&Loc::default(), ret_typ.clone())));
|
||||||
ret_type = Some(LocBox::new(&Loc::default(), typ.inner().clone()));
|
|
||||||
}
|
}
|
||||||
for (name, typ) in args {
|
for (name, typ) in args {
|
||||||
let mut tokens = crate::tokeniser::tokenise(&typ, "(internal)").unwrap();
|
params.push((Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), typ.clone())));
|
||||||
let typ = parse_type(&mut tokens).unwrap();
|
|
||||||
params.push((Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), typ.inner().clone())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let f = Function {
|
let f = Function {
|
||||||
struct_name: None,
|
struct_name: None,
|
||||||
name: Ident(name.to_string()),
|
name: Some(Ident(name.to_string())),
|
||||||
params,
|
params,
|
||||||
ret_type,
|
ret_type,
|
||||||
qual_const: false,
|
qual_const: false,
|
||||||
|
|||||||
2
std/core.mcl
Normal file
2
std/core.mcl
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
include "std/str.mcl";
|
||||||
|
include "std/io/mod.mcl";
|
||||||
12
std/io/mod.mcl
Normal file
12
std/io/mod.mcl
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
fn write_str(fd: usize, s: &str) -> usize {
|
||||||
|
return __INTERNAL_syscall3(1, fd as &void, s->inner as &void, s->len as &void);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn puts(s: &str) {
|
||||||
|
write_str(1, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eputs(s: &str) {
|
||||||
|
write_str(2, s);
|
||||||
|
}
|
||||||
107
std/lib/beaker.mcl
Normal file
107
std/lib/beaker.mcl
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
include "std/core.mcl";
|
||||||
|
|
||||||
|
const MAX_CONTEXT_VARS: usize = 32;
|
||||||
|
const MAX_KEY_LEN: usize = 64;
|
||||||
|
const MAX_VALUE_LEN: usize = 256;
|
||||||
|
const MAX_PATH_LEN: usize = 256;
|
||||||
|
const MAX_HANDLERS: usize = 32;
|
||||||
|
const BUFFER_SIZE: usize = 4096;
|
||||||
|
const MAX_URL_PARAMS: usize = 16;
|
||||||
|
const MAX_COOKIES: usize = 10;
|
||||||
|
const MAX_OUTER_ARRAY_ITEMS: usize = 100;
|
||||||
|
const MAX_INNER_ARRAY_ITEMS: usize = 200;
|
||||||
|
|
||||||
|
const TEMPLATES_DIR: &str = "templates/";
|
||||||
|
const STATIC_DIR: &str = "static/";
|
||||||
|
/*
|
||||||
|
enum ContextType {
|
||||||
|
CONTEXT_TYPE_STRING,
|
||||||
|
CONTEXT_TYPE_STRING_ARRAY,
|
||||||
|
CONTEXT_TYPE_STRING_2D_ARRAY,
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
type ContextType = i32;
|
||||||
|
struct CV_array_data {
|
||||||
|
values: &&char,
|
||||||
|
count: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CV_2d_array_data {
|
||||||
|
values: &&&char,
|
||||||
|
count: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct ContextVarStr {
|
||||||
|
key: [char; MAX_KEY_LEN],
|
||||||
|
typ: ContextType,
|
||||||
|
value: [char; MAX_VALUE_LEN]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ContextVarArr {
|
||||||
|
key: [char; MAX_KEY_LEN],
|
||||||
|
typ: ContextType,
|
||||||
|
value: CV_array_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ContextVar2dArr {
|
||||||
|
key: [char; MAX_KEY_LEN],
|
||||||
|
typ: ContextType,
|
||||||
|
value: CV_2d_array_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ContextVar {
|
||||||
|
key: [char; MAX_KEY_LEN],
|
||||||
|
typ: ContextType,
|
||||||
|
value: [char; MAX_VALUE_LEN]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TemplateContext {
|
||||||
|
vars: [ContextVar; MAX_CONTEXT_VARS],
|
||||||
|
count: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UrlParam {
|
||||||
|
key: [char; MAX_KEY_LEN],
|
||||||
|
value: [char; MAX_VALUE_LEN],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UrlParams {
|
||||||
|
params: [UrlParam; MAX_URL_PARAMS],
|
||||||
|
count: i32
|
||||||
|
}
|
||||||
|
struct Cookie {
|
||||||
|
name: [char; MAX_KEY_LEN],
|
||||||
|
value: [char; MAX_VALUE_LEN],
|
||||||
|
expires: [char; MAX_VALUE_LEN],
|
||||||
|
path: [char; MAX_KEY_LEN],
|
||||||
|
http_only: bool,
|
||||||
|
secure: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestHandler = fn(params: &UrlParams) -> i32;
|
||||||
|
|
||||||
|
struct RouteHandler {
|
||||||
|
path: [char; MAX_PATH_LEN],
|
||||||
|
handler: RequestHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn new_context() -> TemplateContext;
|
||||||
|
extern fn context_set(ctx: &mut TemplateContext, key: &cstr, value: &cstr);
|
||||||
|
extern fn context_set_string_array(ctx: &mut TemplateContext, key: &cstr, values: &cstr, count: i32);
|
||||||
|
extern fn context_set_array_of_arrays(ctx: &mut TemplateContext, key: &cstr, values: [[&cstr]], outer_count: i32, inner_count: i32);
|
||||||
|
extern fn free_context(ctx: &mut TemplateContext);
|
||||||
|
extern fn render_template(template_file: &cstr, ctx: &mut TemplateContext) -> &cstr;
|
||||||
|
|
||||||
|
extern fn send_response(html: &cstr);
|
||||||
|
extern fn send_redirect(location: &cstr);
|
||||||
|
extern fn set_cookie(name: &cstr, value: &cstr, expires: &cstr, path: &cstr, http_only: bool, secure: bool);
|
||||||
|
extern fn get_cookie(name: &cstr) -> &cstr;
|
||||||
|
|
||||||
|
extern fn set_handler(path: &cstr, handler: RequestHandler);
|
||||||
|
extern fn parse_request_url(request_buffer: &cstr, params: &UrlParams) -> &cstr;
|
||||||
|
extern fn get_mime_type(file_path: &cstr) -> &cstr;
|
||||||
|
extern fn serve_static_file(request_path_relative_to_static: &cstr) -> bool;
|
||||||
|
|
||||||
|
extern fn beaker_run(ip: &cstr, port: i32) -> i32;
|
||||||
|
|
||||||
14
std/str.mcl
Normal file
14
std/str.mcl
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
type cstr = [u8];
|
||||||
|
|
||||||
|
|
||||||
|
struct str {
|
||||||
|
len: usize,
|
||||||
|
inner: &cstr
|
||||||
|
}
|
||||||
|
|
||||||
|
fn str.len(&self) -> usize {
|
||||||
|
return self.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
40
test.mcl
40
test.mcl
@@ -1,21 +1,47 @@
|
|||||||
type str = [u8];
|
type str = [u8];
|
||||||
|
|
||||||
|
include "std.mcl";
|
||||||
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
a: usize,
|
a: usize,
|
||||||
b: &str
|
b: &str
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Foo.new(a: usize, b: &str) -> Foo {
|
fn Foo.new(a1: usize, b: &str) -> &Foo {
|
||||||
return Foo {
|
return &Foo {
|
||||||
a: a,
|
a: a1,
|
||||||
b: b
|
b: b
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print(s: &str) {
|
||||||
fn main() -> i32 {
|
// do nothign for now
|
||||||
let obj = Foo::new();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mul(n: usize, n2: usize) -> usize {
|
||||||
|
return n * n2;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
let obj = Foo::new(1, "owo");
|
||||||
|
obj->b;
|
||||||
|
let owo = "ahahaha";
|
||||||
|
loop {
|
||||||
|
print(owo);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i += 1) {
|
||||||
|
puts("nyaaa");
|
||||||
|
if (i > 7) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
print("cant see me!");
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
mul(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FOO: usize = main;
|
||||||
|
|||||||
18
test2.mcl
Normal file
18
test2.mcl
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// include "std/core.mcl";
|
||||||
|
include "std/lib/beaker.mcl";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn hello_world_handler(params: &UrlParams) -> i32 {
|
||||||
|
send_response(c"Hello, World!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
set_handler(c"/", hello_world_handler);
|
||||||
|
let result = beaker_run(c"127.0.0.1", 0xff00000000000000 as i32);
|
||||||
|
|
||||||
|
if (!(result as bool)) {
|
||||||
|
eputs("[APP] Error: Beaker server failed to start.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
1
tests/mod.rs
Normal file
1
tests/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
mod parser;
|
||||||
3
tests/parser/expr.rs
Normal file
3
tests/parser/expr.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
3
tests/parser/mod.rs
Normal file
3
tests/parser/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
mod expr;
|
||||||
|
mod stat;
|
||||||
|
mod typ;
|
||||||
52
tests/parser/stat/constant.rs
Normal file
52
tests/parser/stat/constant.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use mclangc::{
|
||||||
|
cli::CliArgs,
|
||||||
|
common::{Loc, loc::LocBox},
|
||||||
|
parser::{self, ast::{
|
||||||
|
Ident, Keyword, Number, Punctuation, TokenType, expr::Expr, literal::Literal,
|
||||||
|
statement::{Statement, ConstVar},
|
||||||
|
}},
|
||||||
|
tokeniser::Token, validator::predefined::BuiltinType
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_const_stat() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Keyword(Keyword::Const)),
|
||||||
|
Token::new_test(TokenType::ident("MyConstant")),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Colon)),
|
||||||
|
Token::new_test(TokenType::ident("usize")),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Eq)),
|
||||||
|
Token::new_test(TokenType::number(69, 10, false)),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Semi)),
|
||||||
|
Token::new_test(TokenType::ident("overflow")),
|
||||||
|
Token::new_test(TokenType::ident("overflow")),
|
||||||
|
Token::new_test(TokenType::ident("overflow")),
|
||||||
|
];
|
||||||
|
tokens.reverse();
|
||||||
|
let res = parser::stat::parse_statement(&mut tokens, &CliArgs::default(), &mut super::get_prog());
|
||||||
|
assert!(res.is_ok(), "{res:?}");
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(res) => {
|
||||||
|
assert!(res.is_some(), "{res:?}");
|
||||||
|
match res {
|
||||||
|
Some(res) => {
|
||||||
|
assert_eq!(res.inner().clone(), Statement::ConstVar(ConstVar {
|
||||||
|
name: Ident::new("MyConstant"),
|
||||||
|
typ: LocBox::new(&Loc::default(), BuiltinType::usize()),
|
||||||
|
val: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 69, base: 10, signed: false })))
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
None => unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Err(_) => unreachable!()
|
||||||
|
}
|
||||||
|
assert!(tokens.len() == 3, "leftover token count incorrect");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
45
tests/parser/stat/mod.rs
Normal file
45
tests/parser/stat/mod.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use mclangc::{
|
||||||
|
common::{Loc, loc::LocBox},
|
||||||
|
parser::{
|
||||||
|
ast::{
|
||||||
|
Ident, Program,
|
||||||
|
statement::{Enum, Struct},
|
||||||
|
typ::Type
|
||||||
|
}
|
||||||
|
},
|
||||||
|
validator::predefined::{BuiltinType, load_builtin}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
mod constant;
|
||||||
|
mod statc;
|
||||||
|
mod type_alias;
|
||||||
|
|
||||||
|
pub fn get_prog() -> Program {
|
||||||
|
let mut prog = Program::default();
|
||||||
|
load_builtin(&mut prog);
|
||||||
|
let loc = Loc::default();
|
||||||
|
prog.structs.insert(Ident::new("MyStruct"), LocBox::new(&loc, Struct {
|
||||||
|
name: Ident::new("MyStruct"),
|
||||||
|
fields: vec![
|
||||||
|
(Ident::new("foo"), LocBox::new(&loc, BuiltinType::usize())),
|
||||||
|
(Ident::new("bar"), LocBox::new(&loc, BuiltinType::bool())),
|
||||||
|
(Ident::new("baz"), LocBox::new(&loc, Type::Owned(Ident::new("str")).as_ref())),
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
prog.enums.insert(Ident::new("MyEnum"), LocBox::new(&loc, Enum {
|
||||||
|
name: Ident::new("MyEnum"),
|
||||||
|
fields: vec![],
|
||||||
|
}));
|
||||||
|
prog.types.insert(Ident::new("MyType"), LocBox::new(&loc, Type::Owned(Ident::new("MyStruct"))));
|
||||||
|
prog.types.insert(Ident::new("MyType2"), LocBox::new(&loc, Type::Owned(Ident::new("MyType"))));
|
||||||
|
prog.types.insert(Ident::new("MyType3"), LocBox::new(&loc, Type::Owned(Ident::new("MyType2")).as_ref()));
|
||||||
|
prog.types.insert(Ident::new("MyTypeInf"), LocBox::new(&loc, Type::Owned(Ident::new("MyTypeInf"))));
|
||||||
|
prog.types.insert(Ident::new("MyTypeInfIndirect"), LocBox::new(&loc, Type::Owned(Ident::new("MyType4"))));
|
||||||
|
prog.types.insert(Ident::new("MyType4"), LocBox::new(&loc, Type::Owned(Ident::new("MyType5"))));
|
||||||
|
prog.types.insert(Ident::new("MyType5"), LocBox::new(&loc, Type::Owned(Ident::new("MyType4"))));
|
||||||
|
|
||||||
|
prog
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
48
tests/parser/stat/statc.rs
Normal file
48
tests/parser/stat/statc.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use mclangc::{
|
||||||
|
cli::CliArgs,
|
||||||
|
common::{Loc, loc::LocBox},
|
||||||
|
parser::{self, ast::{
|
||||||
|
Ident, Keyword, Number, Punctuation, TokenType, expr::Expr, literal::Literal,
|
||||||
|
statement::{Statement, StaticVar},
|
||||||
|
}},
|
||||||
|
tokeniser::Token, validator::predefined::BuiltinType
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_static_stat() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Keyword(Keyword::Static)),
|
||||||
|
Token::new_test(TokenType::ident("MyStatic")),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Colon)),
|
||||||
|
Token::new_test(TokenType::ident("usize")),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Eq)),
|
||||||
|
Token::new_test(TokenType::number(69, 10, false)),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Semi)),
|
||||||
|
Token::new_test(TokenType::ident("overflow")),
|
||||||
|
Token::new_test(TokenType::ident("overflow")),
|
||||||
|
Token::new_test(TokenType::ident("overflow")),
|
||||||
|
];
|
||||||
|
tokens.reverse();
|
||||||
|
let res = parser::stat::parse_statement(&mut tokens, &CliArgs::default(), &mut super::get_prog());
|
||||||
|
assert!(res.is_ok(), "{res:?}");
|
||||||
|
match res {
|
||||||
|
Ok(res) => {
|
||||||
|
assert!(res.is_some(), "{res:?}");
|
||||||
|
match res {
|
||||||
|
Some(res) => {
|
||||||
|
assert_eq!(res.inner().clone(), Statement::StaticVar(StaticVar {
|
||||||
|
name: Ident::new("MyStatic"),
|
||||||
|
typ: LocBox::new(&Loc::default(), BuiltinType::usize()),
|
||||||
|
val: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 69, base: 10, signed: false })))
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
None => unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Err(_) => unreachable!()
|
||||||
|
}
|
||||||
|
assert!(tokens.len() == 3, "leftover token count incorrect");
|
||||||
|
}
|
||||||
48
tests/parser/stat/type_alias.rs
Normal file
48
tests/parser/stat/type_alias.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use mclangc::{
|
||||||
|
cli::CliArgs,
|
||||||
|
common::{Loc, loc::LocBox},
|
||||||
|
parser::{self, ast::{
|
||||||
|
Ident, Keyword, Number, Punctuation, TokenType, expr::Expr, literal::Literal,
|
||||||
|
statement::{Statement, StaticVar},
|
||||||
|
}},
|
||||||
|
tokeniser::Token, validator::predefined::BuiltinType
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_alias_stat() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Keyword(Keyword::Static)),
|
||||||
|
Token::new_test(TokenType::ident("MyStatic")),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Colon)),
|
||||||
|
Token::new_test(TokenType::ident("usize")),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Eq)),
|
||||||
|
Token::new_test(TokenType::number(69, 10, false)),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Semi)),
|
||||||
|
Token::new_test(TokenType::ident("overflow")),
|
||||||
|
Token::new_test(TokenType::ident("overflow")),
|
||||||
|
Token::new_test(TokenType::ident("overflow")),
|
||||||
|
];
|
||||||
|
tokens.reverse();
|
||||||
|
let res = parser::stat::parse_statement(&mut tokens, &CliArgs::default(), &mut super::get_prog());
|
||||||
|
assert!(res.is_ok(), "{res:?}");
|
||||||
|
match res {
|
||||||
|
Ok(res) => {
|
||||||
|
assert!(res.is_some(), "{res:?}");
|
||||||
|
match res {
|
||||||
|
Some(res) => {
|
||||||
|
assert_eq!(res.inner().clone(), Statement::StaticVar(StaticVar {
|
||||||
|
name: Ident::new("MyStatic"),
|
||||||
|
typ: LocBox::new(&Loc::default(), BuiltinType::usize()),
|
||||||
|
val: LocBox::new(&Loc::default(), Expr::Literal(String::new(), Literal::Number(Number { val: 69, base: 10, signed: false })))
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
None => unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Err(_) => unreachable!()
|
||||||
|
}
|
||||||
|
assert!(tokens.len() == 3, "leftover token count incorrect");
|
||||||
|
|
||||||
|
}
|
||||||
245
tests/parser/typ.rs
Normal file
245
tests/parser/typ.rs
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
use std::vec;
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
use mclangc::{common::{Loc, loc::LocBox}, parser::{self, ast::{Ident, Keyword, Program, Punctuation, TokenType, statement::{Enum, Struct}, typ::Type}}, tokeniser::Token, validator::{self, predefined::{BuiltinType, get_builtin_from_name, load_builtin}}};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn get_prog() -> Program {
|
||||||
|
let mut prog = Program::default();
|
||||||
|
load_builtin(&mut prog);
|
||||||
|
let loc = Loc::default();
|
||||||
|
prog.structs.insert(Ident::new("MyStruct"), LocBox::new(&loc, Struct {
|
||||||
|
name: Ident::new("MyStruct"),
|
||||||
|
fields: vec![
|
||||||
|
(Ident::new("foo"), LocBox::new(&loc, BuiltinType::usize())),
|
||||||
|
(Ident::new("bar"), LocBox::new(&loc, BuiltinType::bool())),
|
||||||
|
(Ident::new("baz"), LocBox::new(&loc, Type::Owned(Ident::new("str")).as_ref())),
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
prog.enums.insert(Ident::new("MyEnum"), LocBox::new(&loc, Enum {
|
||||||
|
name: Ident::new("MyEnum"),
|
||||||
|
fields: vec![],
|
||||||
|
}));
|
||||||
|
prog.types.insert(Ident::new("MyType"), LocBox::new(&loc, Type::Owned(Ident::new("MyStruct"))));
|
||||||
|
prog.types.insert(Ident::new("MyType2"), LocBox::new(&loc, Type::Owned(Ident::new("MyType"))));
|
||||||
|
prog.types.insert(Ident::new("MyType3"), LocBox::new(&loc, Type::Owned(Ident::new("MyType2")).as_ref()));
|
||||||
|
prog.types.insert(Ident::new("MyTypeInf"), LocBox::new(&loc, Type::Owned(Ident::new("MyTypeInf"))));
|
||||||
|
prog.types.insert(Ident::new("MyTypeInfIndirect"), LocBox::new(&loc, Type::Owned(Ident::new("MyType4"))));
|
||||||
|
prog.types.insert(Ident::new("MyType4"), LocBox::new(&loc, Type::Owned(Ident::new("MyType5"))));
|
||||||
|
prog.types.insert(Ident::new("MyType5"), LocBox::new(&loc, Type::Owned(Ident::new("MyType4"))));
|
||||||
|
|
||||||
|
prog
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_alias_simple() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyType")))
|
||||||
|
];
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
let mut prog = get_prog();
|
||||||
|
let ret = validator::validate_type(&mut prog, &ret.unwrap());
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), Type::Owned(Ident::new("MyStruct")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_alias_deep() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyType3")))
|
||||||
|
];
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
let mut prog = get_prog();
|
||||||
|
let ret = validator::validate_type(&mut prog, &ret.unwrap());
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), Type::Owned(Ident::new("MyStruct")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_alias_recursive_simple() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyTypeInf")))
|
||||||
|
];
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
let mut prog = get_prog();
|
||||||
|
let ret = validator::validate_type(&mut prog, &ret.unwrap());
|
||||||
|
assert!(ret.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_alias_recursive_indirect() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyType4")))
|
||||||
|
];
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
let mut prog = get_prog();
|
||||||
|
let ret = validator::validate_type(&mut prog, &ret.unwrap());
|
||||||
|
assert!(ret.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_named_struct() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||||
|
];
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct"))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_named_enum() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyEnum")))
|
||||||
|
];
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyEnum"))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_ref() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||||
|
];
|
||||||
|
tokens.reverse();
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_ref2() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||||
|
];
|
||||||
|
tokens.reverse();
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref().as_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_ref3() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||||
|
];
|
||||||
|
tokens.reverse();
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref().as_ref().as_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_mut_ref() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||||
|
];
|
||||||
|
tokens.reverse();
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref_mut()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_mut_ref2() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||||
|
];
|
||||||
|
tokens.reverse();
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref_mut().as_ref_mut()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_mut_ref3() {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||||
|
Token::new_test(TokenType::Punct(Punctuation::Ampersand)),
|
||||||
|
Token::new_test(TokenType::Keyword(Keyword::Mut)),
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new("MyStruct")))
|
||||||
|
];
|
||||||
|
tokens.reverse();
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), Type::Owned(Ident::new("MyStruct")).as_ref_mut().as_ref_mut().as_ref_mut()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_builtin(s: &str) {
|
||||||
|
let mut tokens = vec![
|
||||||
|
Token::new_test(TokenType::Ident(Ident::new(s)))
|
||||||
|
];
|
||||||
|
let ret = parser::typ::parse_type(&mut tokens);
|
||||||
|
assert!(ret.is_ok());
|
||||||
|
assert_eq!(ret.unwrap(), LocBox::new(&Loc::default(), get_builtin_from_name(s).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_usize() {
|
||||||
|
test_builtin("usize");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_isize() {
|
||||||
|
test_builtin("isize");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_u8() {
|
||||||
|
test_builtin("u8");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_i8() {
|
||||||
|
test_builtin("i8");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_u16() {
|
||||||
|
test_builtin("u16");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_i16() {
|
||||||
|
test_builtin("i16");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_u32() {
|
||||||
|
test_builtin("u32");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_i32() {
|
||||||
|
test_builtin("i32");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_u64() {
|
||||||
|
test_builtin("u64");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_i64() {
|
||||||
|
test_builtin("i64");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_void() {
|
||||||
|
test_builtin("void");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_char() {
|
||||||
|
test_builtin("char");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn parse_type_builtin_bool() {
|
||||||
|
test_builtin("bool");
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
[]
|
|
||||||
Reference in New Issue
Block a user