Compare commits

...

8 Commits

Author SHA1 Message Date
dd1b6af259 Fix bunch of bugs, endianess of numbers seems to befucked, i have no idea why 2026-02-12 23:26:59 +02:00
96f8aa3bbb Docs 2026-02-05 12:46:36 +02:00
660b9245fb Add all type parsing tests 2026-02-05 11:40:49 +02:00
4b61adea5d Fix bugs, add propper tests 2026-02-03 13:20:23 +02:00
081ff9a27a Add external functions (both import and export), make includes work
still need to fix literal arrays, it has the same problem as struct
literals had with moving the literal into memory and modifying the
memory with variables
2026-01-29 22:37:04 +02:00
834b5b1213 no segfault! 2026-01-28 22:30:02 +02:00
1f4645ed24 Upgrade to rust 2024
Wow this project is old
2026-01-17 16:39:01 +02:00
158b76fe39 :3 2026-01-17 16:31:40 +02:00
69 changed files with 3622 additions and 441 deletions

2
.gitignore vendored
View File

@@ -1,4 +1,6 @@
/target
/*.c
/*.s
/*.o
/*.ssa
/test

View File

@@ -1,7 +1,7 @@
[package]
name = "mclangc"
version = "0.1.0"
edition = "2021"
edition = "2024"
default-run = "mclangc"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

174
docs/main.md Normal file
View 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;
```

View 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

Binary file not shown.

BIN
libbeaker.so Executable file

Binary file not shown.

View File

@@ -0,0 +1 @@
[]

View File

@@ -59,4 +59,4 @@
SquareL,
),
},
]
]

View File

@@ -133,7 +133,7 @@ fn test_parser(cf: &CollectedFiles, compile: bool) -> anyhow::Result<usize> {
continue;
}
};
let ast = match mclangc::parser::parse_program(tokens) {
let ast = match mclangc::parser::parse_program(tokens, &mclangc::cli::CliArgs::default()) {
Ok(v) => v,
Err(e) => {
crate::error!("Test parser/{name} had an error: {e}");

View File

@@ -3,7 +3,7 @@ use clap::{error::ErrorKind, CommandFactory};
use crate::logger::Level;
#[derive(Debug, clap::Parser)]
#[derive(Debug, Default, clap::Parser)]
pub struct CliArgs {
/// Output more info, will get overwritten if -q|--quiet is specified
#[arg(long, short)]
@@ -13,9 +13,20 @@ pub struct CliArgs {
quiet: bool,
#[arg(long, short, value_parser=crate::targets::get_all_targets(), default_value_t=crate::targets::get_default_target())]
pub target: String,
#[arg(long="include", short='I', default_values=[".", "include"])]
pub include_paths: Vec<String>,
/// Output file
#[arg(long, short, default_value="a.out")]
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
#[clap(num_args = 1..)]
pub input: Vec<String>

View File

@@ -1,6 +1,6 @@
use std::fmt::{Debug, Display};
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
#[derive(Debug, Clone, PartialOrd, Ord, Eq, Hash)]
pub struct Loc {
file: String,
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> {
inner: T,
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 {
fn inc_line(&mut self);
fn inc_col(&mut self);

View File

@@ -1,3 +1,6 @@
#![doc = include_str!("../docs/main.md")]
pub mod common;
pub mod tokeniser;
pub mod parser;

View File

@@ -110,7 +110,7 @@ pub mod log {
#[macro_export]
macro_rules! lerror {
($loc:expr, $($arg:tt)*) => {
($loc:expr_2021, $($arg:tt)*) => {
if cfg!(debug_assertions) {
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Error, &format!($($arg)*))
} else {
@@ -120,7 +120,7 @@ pub mod log {
}
#[macro_export]
macro_rules! lwarn {
($loc:expr, $($arg:tt)*) => {
($loc:expr_2021, $($arg:tt)*) => {
if cfg!(debug_assertions) {
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Warn, &format!($($arg)*))
} else {
@@ -130,7 +130,7 @@ pub mod log {
}
#[macro_export]
macro_rules! linfo {
($loc:expr, $($arg:tt)*) => {
($loc:expr_2021, $($arg:tt)*) => {
if cfg!(debug_assertions) {
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Info, &format!($($arg)*))
} else {
@@ -140,7 +140,7 @@ pub mod log {
}
#[macro_export]
macro_rules! lhelp {
($loc:expr, $($arg:tt)*) => {
($loc:expr_2021, $($arg:tt)*) => {
if cfg!(debug_assertions) {
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Help, &format!($($arg)*))
} else {
@@ -150,7 +150,7 @@ pub mod log {
}
#[macro_export]
macro_rules! ldebug {
($loc:expr, $($arg:tt)*) => {
($loc:expr_2021, $($arg:tt)*) => {
if cfg!(debug_assertions) {
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Debug, &format!($($arg)*))
} else {

View File

@@ -1,4 +1,6 @@
use std::{path::PathBuf, process::ExitCode};
#![doc = include_str!("../docs/main.md")]
use std::path::PathBuf;
use clap::Parser;
// Importing logger here too cause the logger macros dont work outside the mclanc lib
@@ -6,7 +8,6 @@ mod logger;
fn main() -> anyhow::Result<()> {
let cli = mclangc::cli::CliArgs::parse();
cli.set_log_level();
@@ -22,11 +23,16 @@ fn main() -> anyhow::Result<()> {
let data = std::fs::read_to_string(&fp).unwrap();
info!("Tokenising {file}");
let tokens = mclangc::tokeniser::tokenise(&data, &file)?;
// dbg!(&tokens);
info!("Parsing {file}");
let mut prog = mclangc::parser::parse_program(tokens)?;
let mut prog = mclangc::parser::parse_program(tokens, &cli)?;
info!("Validating {file}");
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));
}

View File

@@ -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};
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum Expr {
// Comment(Comment),
Group(Box<LocBox<Expr>>),
@@ -16,8 +16,9 @@ pub enum Expr {
typ: Punctuation,
left: Box<LocBox<Expr>>,
right: Box<LocBox<Expr>>,
signed: bool,
},
Literal(super::literal::Literal),
Literal(String, super::literal::Literal), // string is id
ArrayIndex {
name: Box<LocBox<Expr>>,
index: Box<LocBox<Expr>>,
@@ -27,20 +28,26 @@ pub enum Expr {
path: Box<LocBox<Expr>>,
params: CallParams, // LocBox<Expr> ~ (, Expr)*
},
//MethodCall {
// var_name: Box<LocBox<Expr>>,
// method_name: Ident,
// params: CallParams,
//},
MethodCall {
left: Box<LocBox<Expr>>,
strct: Option<Ident>,
params: CallParams,
},
/// the left side only exists on the /.|->/ chain
FieldAccess {
left: Box<Option<LocBox<Expr>>>,
right: Box<LocBox<Expr>>,
offset: usize,
l_typ: Option<Box<Type>>,
r_typ: Option<Box<Type>>,
},
PtrFieldAccess {
left: Box<Option<LocBox<Expr>>>,
right: Box<LocBox<Expr>>,
offset: usize,
l_typ: Option<Box<Type>>,
r_typ: Option<Box<Type>>
},
ForLoop {
init: Box<Ast>,
@@ -56,10 +63,7 @@ pub enum Expr {
body: Block,
},
If(IfExpr),
Struct {
path: Path,
fields: HashMap<Ident, LocBox<Expr>>,
},
Struct(String, StructLit),
Return(Box<Option<LocBox<Expr>>>),
Break,
Continue,
@@ -67,6 +71,15 @@ pub enum Expr {
left: Box<LocBox<Expr>>,
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 {
@@ -74,17 +87,36 @@ impl Expr {
let Expr::Path(p) = self else {panic!("Unwrapping")};
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>>);
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
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>);
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 test: Box<LocBox<Expr>>,
pub body: Block,
pub else_if: Option<IfBranchExpr>
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum IfBranchExpr {
ElseIf(Box<IfExpr>),
Else(Block)

View File

@@ -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 {
Number(Number),
Ident(Ident),
String(TString),
Char(Char),
Array(Vec<LocBox<Expr>>),
Array{
items: Vec<LocBox<Expr>>,
item_size: Option<usize>,
},
Bool(bool),
ArrayRepeat {
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)
}
}

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
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 mod expr;
@@ -10,19 +10,104 @@ pub mod literal;
pub mod statement;
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 ast: expr::Block,
pub structs: HashMap<Ident, LocBox<Struct>>,
pub enums: HashMap<Ident, LocBox<Enum>>,
pub types: HashMap<Ident, TypeType>,
pub types: HashMap<Ident, LocBox<Type>>,
pub functions: HashMap<Ident, LocBox<Function>>,
pub member_functions: HashMap<Ident, HashMap<Ident, LocBox<Function>>>,
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 {
Expr(LocBox<expr::Expr>),
Statement(LocBox<statement::Statement>),

View File

@@ -1,13 +1,15 @@
use std::collections::HashMap;
use std::{collections::HashMap, fmt::{Display, write}, sync::Mutex};
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};
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum Statement {
Fn(Function),
TypeAlias(TypeAlias),
@@ -15,69 +17,122 @@ pub enum Statement {
Enum(Enum),
ConstVar(ConstVar),
StaticVar(StaticVar),
Let {
name: Ident,
typ: Option<LocBox<Type>>,
val: Option<LocBox<Expr>>,
},
Include,
Let(Let),
}
#[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 name: Ident,
pub typ: LocBox<Type>,
pub val: LocBox<Expr>,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct StaticVar {
pub name: Ident,
pub typ: LocBox<Type>,
pub val: LocBox<Expr>,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct TypeAlias {
pub name: Ident,
pub typ: LocBox<Type>,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct Struct {
pub name: Ident,
pub fields: Vec<(Ident, LocBox<Type>)>,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub struct Enum {
pub name: Ident,
pub fields: Vec<Ident>,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialOrd, Hash)]
pub struct Function {
pub struct_name: Option<Ident>,
pub name: Ident,
pub name: Option<Ident>,
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_extern: Option<TString>, // abi
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 {
pub fn get_full_name(&self) -> String {
if let Some(sn) = &self.struct_name {
format!("{sn}__S__{}", self.name.0)
if let Some(name) = &self.name {
if let Some(sn) = &self.struct_name {
format!("{sn}__S__{}", name.0)
} else {
format!("{}", name.0)
}
} else {
format!("{}", self.name.0)
String::new()
}
}
pub fn get_full_name_pretty(&self) -> String {
if let Some(sn) = &self.struct_name {
format!("{sn}::{}", self.name.0)
if let Some(name) = &self.name {
if let Some(sn) = &self.struct_name {
format!("{sn}::{}", name.0)
} else {
format!("{}", name.0)
}
} else {
format!("{}", self.name.0)
String::new()
}
}
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 {
Byte(u8),
Bytes(Vec<u8>),
AddrOfConst(Ident),
AddrOfStatic(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() {
Expr::Literal(lit) => {
Expr::Literal(_, lit) => {
match lit {
Literal::Char(Char(c)) => {
if must_be_number {
lerror!(value.loc(), "Expected number got char");
bail!("")
}
Ok(vec![ConstDataTyp::Byte(*c as u8)])
Ok(ConstDataTyp::Bytes(vec![*c as u8]))
},
Literal::String(TString { val, cstr }) => {
if must_be_number {
lerror!(value.loc(), "Expected number got string");
bail!("")
}
let mut val = val.chars().into_iter().map(|f| ConstDataTyp::Byte(f as u8)).collect::<Vec<_>>();
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: _ }) => {
let mut buf = Vec::new();
let mut inc = 0;
while inc < 8 {
buf.push(ConstDataTyp::Byte(((val << 8*inc) & 0xff) as u8));
buf.push(((val << 8*inc) & 0xff) as u8);
inc += 1;
}
if is_little_endian {
if is_big_endian {
buf.reverse();
}
Ok(buf)
Ok(ConstDataTyp::Bytes(buf))
}
Literal::Array(arr) => {
Literal::Array { items: arr, item_size } => {
if must_be_number {
lerror!(value.loc(), "Expected number got array");
bail!("")
}
let mut bytes = Vec::new();
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 {
let mut data = get_constant_data_as_bytes(program, item.clone(), is_little_endian, must_be_number)?;
bytes.append(&mut data);
let data = get_constant_data_as_bytes(program, item.clone(), is_big_endian, must_be_number)?;
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 {
lerror!(value.loc(), "Expected number got repeating array");
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;
for b in num {
let ConstDataTyp::Byte(b) = b else {unreachable!()};
count = b as usize;
count <<= 8;
}
let orig = val.clone();
let mut arr = Vec::new();
for _ in 0..count {
val.append(&mut orig.clone());
arr.push(orig.clone());
}
Ok(val)
Ok(ConstDataTyp::Array(arr))
}
Literal::Ident(name) => {
if let Some(var) = program.const_vars.get(name) {
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::Bool(v) => {
Ok(ConstDataTyp::Bytes(vec![*v as u8]))
}
//Literal::Struct { name, fields } => {
// 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!()
//}
Literal::Ident(_) => unreachable!()
}
}
_ => 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:?}")
}
}

View File

@@ -1,12 +1,12 @@
use std::fmt::Display;
use std::{clone, collections::HashSet, fmt::Display};
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};
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum Type {
Ref {
inner: Box<Type>,
@@ -20,41 +20,304 @@ pub enum Type {
inner: Box<Type>,
},
Owned(Ident),
Builtin {
name: String,
size: u8,
signed: bool,
},
FnPtr(Function)
}
impl Type {
pub fn size_of(&self, program: &Program) -> anyhow::Result<usize> {
match self {
Self::Ref { inner, mutable } => {
// TODO: Use the actual ptr size
Ok(size_of::<*const ()>())
}
Self::UnsizedArray { .. } => {
bail!("Unsized arrays dont have a known size and must be behind a pointer")
}
Self::SizedArray { inner, .. } => {
Ok(inner.size_of(program)? )
}
Self::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) {
match v {
TypeType::Normal(v) => {
return Ok(v.inner().size_of(program)?);
}
TypeType::Builtin { size, .. } => {
return Ok(*size);
pub fn is_void(&self) -> bool {
*self == BuiltinType::void()
}
pub fn as_ref(self) -> Self {
Self::Ref {
inner: Box::new(self),
mutable: false
}
}
pub fn as_ref_mut(self) -> Self {
Self::Ref {
inner: Box::new(self),
mutable: true
}
}
pub fn new_builtin(name: &str, size: u8, signed: bool) -> Self {
Self::Builtin { name: name.to_string(), size, signed }
}
pub fn should_deref_pointer(&self, program: &Program) -> bool {
dbg!(&self);
let mut slf = self.clone();
match &mut slf {
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)
}
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}'")
}
bail!("Unknown type '{name}'")
}
}
f(self, program, false)
}
pub fn size_of_allow_unsized_arrays(&self, program: &Program) -> anyhow::Result<usize> {
match self.size_of(program) {
@@ -72,6 +335,9 @@ impl Type {
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::FnPtr(func) => {
write!(f, "{func}")
}
Self::Ref { inner, mutable } => {
if *mutable {
write!(f, "&mut {inner}")
@@ -87,13 +353,16 @@ impl Display for Type {
Expr::Path(p) => {
write!(f, "[{inner}; {p}]")
}
Expr::Literal(Literal::Number(n)) => {
Expr::Literal(_, Literal::Number(n)) => {
write!(f, "[{inner}; {n}]")
}
_ => 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)
}
}
}
}

View File

@@ -1,8 +1,8 @@
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
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};
@@ -31,20 +31,25 @@ const BINOP_LIST: &[TokenType] = &[
TokenType::Punct(Punctuation::XorEq),
TokenType::Punct(Punctuation::Eq),
TokenType::Punct(Punctuation::EqEq),
TokenType::Punct(Punctuation::Neq),
TokenType::Punct(Punctuation::Lt),
TokenType::Punct(Punctuation::Gt),
TokenType::Punct(Punctuation::Le),
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)) {
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
if let Some(_) = utils::check(tokens, TokenType::ident("")) {
debug!("is ident");
let p = parse_path(tokens)?;
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 {
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::Star),
]) {
Some(parse_unop(tokens)?)
Some(parse_unop(tokens, cli, prog)?)
} else
if let Some(_) = utils::check_from_many(tokens, &[
TokenType::string("", false),
TokenType::number(0, 0, false),
TokenType::char('\0'),
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)) {
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)) {
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)) {
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)) {
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)) {
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)));
} 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)));
} 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 {
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)?;
}
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() {
res = parse_cast(tokens, res)?;
}
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) {
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 {
if consume_semi {
_ = 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));
}
}
if consume_semi {
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
}
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 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))))
}
@@ -127,18 +143,20 @@ fn parse_cast(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr
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 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");
bail!("")
};
let _ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
let block = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
if let Some(_) = utils::check_2_last(tokens, TokenType::Delim(Delimiter::CurlyR)) {
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR));
Block(Vec::new())
} else {
parse_block(tokens)?
parse_block(tokens, cli, prog)?
}
} else {
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(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 {
test: Box::new(test),
body: block,
else_if: Some(branch)
})
} else {
let branch = IfBranchExpr::Else(parse_block(tokens)?);
let branch = IfBranchExpr::Else(parse_block(tokens, cli, prog)?);
Ok(IfExpr {
test: Box::new(test),
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 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");
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 {
test: Box::new(test),
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 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");
bail!("")
};
// 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");
bail!("")
};
_ = 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");
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 {
init: Box::new(pre),
@@ -205,12 +227,21 @@ fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
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 block = parse_block(tokens)?;
let block = parse_block(tokens, cli, prog)?;
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 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)) {
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);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
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), "");
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 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.");
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 {
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 {
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)) {
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
if let Some(tkn) = utils::check_consume(tokens, TokenType::number(0, 0, false)) {
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
if let Some(tkn) = utils::check_consume(tokens, TokenType::char('\0')) {
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
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) {
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) {
let first = parse_expr(tokens, 0, false)?;
let Some(first) = first else { unreachable!() };
/*if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
} 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();
values.push(first);
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);
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), "")?;
return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::Array(values))));
} 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 {
return Ok(LocBox::new(start.loc(), Expr::Literal(String::new(), Literal::Array { items: values, item_size: None })));
/*
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());
} else {
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found nothing");
}
bail!("")
*/
}
}
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 mut fields = HashMap::new();
let mut fields = BTreeMap::new();
while !tokens.is_empty() {
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) {
break;
}
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
let typ = parse_expr(tokens, 0, false)?.unwrap();
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "wtf?")?;
let typ = parse_expr(tokens, 0, false, cli, prog)?.unwrap();
fields.insert(name.tt().unwrap_ident(), typ);
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?;
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 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");
bail!("")
};
@@ -381,18 +457,18 @@ fn parse_path(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
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, &[
TokenType::Punct(Punctuation::Not),
TokenType::Punct(Punctuation::Plus),
TokenType::Punct(Punctuation::Minus),
TokenType::Punct(Punctuation::Plus), // Make number positive
TokenType::Punct(Punctuation::Minus), // make number negative
TokenType::Punct(Punctuation::Ampersand),
TokenType::Punct(Punctuation::Star),
], "")?;
let loc = typ.loc().clone();
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");
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
loop {
let op_loc;
let op = match tokens.last() {
let mut op = match tokens.last() {
Some(op) if BINOP_LIST.contains(&op.tt()) => {
op_loc = op.loc().clone();
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(_) |
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:?}");
let (lp, rp) = op.precedence().unwrap();
if lp < precedence {
break
}
_ = tokens.pop();
let Some(rhs) = parse_expr(tokens, rp, false)? else {break;};
let Some(rhs) = parse_expr(tokens, rp, false, cli, prog)? else {break;};
lhs = LocBox::new(&op_loc, Expr::BinOp {
typ: op,
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), "")?;
let mut items = Vec::new();
while !tokens.is_empty() {
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
break;
}
if let Some(item) = parse_item(tokens)? {
if let Some(item) = parse_item(tokens, cli, prog)? {
items.push(item);
} else {
break;

View File

@@ -2,45 +2,51 @@ use std::collections::HashMap;
use ast::{expr::Block, Ast, Program};
use crate::tokeniser::{Token, tokentype::*};
use crate::{cli::CliArgs, tokeniser::{Token, tokentype::*}};
pub mod ast;
mod expr;
mod stat;
mod utils;
pub mod expr;
pub mod stat;
pub mod utils;
pub mod typ;
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();
while !tokens.is_empty() {
if let Some(item) = parse_item(&mut tokens)? {
prog_body.push(item);
} else {
break
}
}
Ok(Program {
ast: Block(prog_body),
let mut prog = Program {
ast: Block(prog_body.clone()),
enums: HashMap::new(),
functions: HashMap::new(),
member_functions: HashMap::new(),
types: HashMap::new(),
structs: 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>> {
if let Some(stat) = stat::parse_statement(tokens)? {
fn parse_item(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Option<Ast>> {
if let Some(stat) = stat::parse_statement(tokens, cli, prog)? {
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)));
}
Ok(None)

View File

@@ -1,8 +1,10 @@
use anyhow::bail;
use crate::cli::CliArgs;
use crate::common::loc::LocBox;
use crate::lerror;
use crate::parser::ast::TokenType;
use crate::{error, lerror};
use crate::parser::ast::{Program, TString, TokenType};
use crate::parser::ast::statement::Let;
use crate::parser::expr::parse_expr;
use crate::parser::{Delimiter, Ident, Keyword, Punctuation};
use crate::tokeniser::Token;
@@ -14,18 +16,18 @@ use super::ast::statement::{ConstVar, Enum, Function, Statement, StaticVar, Stru
type Result<T> = anyhow::Result<T>;
pub fn parse_statement(tokens: &mut Vec<Token>) -> Result<Option<LocBox<Statement>>> {
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
Ok(Some(parse_fn(tokens)?))
pub fn parse_statement(tokens: &mut Vec<Token>, cli: &CliArgs, prog: &mut Program) -> Result<Option<LocBox<Statement>>> {
if let Some(_) = utils::check_from_many(tokens, &[TokenType::Keyword(Keyword::Fn), TokenType::Keyword(Keyword::Extern)]) {
Ok(Some(parse_fn(tokens, cli, prog)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Type)) {
Ok(Some(parse_type_alias(tokens)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Const)) {
Ok(Some(parse_constant(tokens)?))
Ok(Some(parse_constant(tokens, cli, prog)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Static)) {
Ok(Some(parse_static(tokens)?))
Ok(Some(parse_static(tokens, cli, prog)?))
} else
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Struct)) {
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)?))
} else
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 {
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 name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
_ = 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 })))
}
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 name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
_ = 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 })))
}
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 name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
let typ = parse_type(tokens)?;
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");
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 })))
}
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 name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
let mut typ = None;
@@ -114,16 +153,16 @@ fn parse_let(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
typ = Some(parse_type(tokens)?);
}
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");
bail!("")
};
val = Some(_val);
}
_ = 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), "")?;
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), "")?;
let typ = parse_type(tokens)?;
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");
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 })))
}
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 name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
_ = 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 })))
}
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
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?;
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
@@ -162,44 +212,66 @@ fn parse_fn(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
struct_name = Some(name);
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
let mut ret_type = None;
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;
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
body = Some(parse_block(tokens)?);
body = Some(parse_block(tokens, cli, prog)?);
} else {
// Check if its just a declaration
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
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{
struct_name,
name,
name: Some(name),
params,
ret_type,
qual_const: false,
qual_extern: None,
qual_extern,
body,
})))
}
fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, LocBox<Type>)>> {
// usize is: 0 = no self, static; 1 = self, ref; 2 = self, mut ref
pub fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<(usize, Vec<(Ident, LocBox<Type>)>)> {
let mut args = Vec::new();
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() {
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
break;
}
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);
let typ = parse_type(tokens)?;
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), "")?;
Ok(args)
Ok((dis, args))
}

View File

@@ -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};
@@ -19,16 +19,30 @@ pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
}
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 None = loc {
loc = Some(start.loc().clone());
}
let itm_typ = parse_type(tokens)?;
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 {
inner: Box::new(itm_typ.inner().clone()),
count
count: count
};
} else {
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), "")?;
} else {
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 {
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))
}
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,
}))
}

View File

@@ -1,31 +1,32 @@
use std::{fs::File, path::{Path, PathBuf}};
use crate::{cli::CliArgs, parser::ast::Program};
mod x86_64;
mod none;
pub mod x86_64;
//mod none;
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> {
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::llvmgen::linux::LlvmGen::get_target_triple(),
none::luagen::cct::LuaGen::get_target_triple(),
x86_64::cgen::linux::CGen::get_target_triple(),
//none::luagen::cct::LuaGen::get_target_triple(),
// x86_64::cgen::linux::CGen::get_target_triple(),
]
}
fn get_target_from_triple(triple: &str) -> Box<dyn Target> {
match triple {
// _ if triple == x86_64::asmgen::linux::AsmGen::get_target_triple() => {
// 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::asmgen::linux::AsmGen::get_target_triple() => {
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::qbegen::linux::QbeGen::get_target_triple() => {
// 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);
}
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(())
}
@@ -70,5 +74,5 @@ pub trait Target {
}
fn write_code(&mut self, program: &Program, f: &mut File) -> 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<()>;
}

View File

@@ -1,7 +1,9 @@
use crate::targets::Target;
use std::io::Write;
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::{collections::HashMap, fs::File, io::Write, process};
pub struct AsmGen;
const RUNTIME_CODE: &'static str = include_str!("./runtime.s");
impl Target for AsmGen {
fn new() -> Self {
Self {}
@@ -12,20 +14,770 @@ impl Target for AsmGen {
fn get_int_ext(&self) -> &'static str {
"s"
}
fn write_code(&mut self, program: &crate::parser::ast::Program, path: &std::path::Path) -> anyhow::Result<()> {
let mut f = std::fs::File::open(path)?;
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
writeln!(f, "bits 64")?;
writeln!(f, "global _start")?;
self.write_extern_func_decls(program, f)?;
writeln!(f, "section .text")?;
writeln!(f, "{}", RUNTIME_CODE)?;
writeln!(f, "section .rodata")?;
for (name, constant) in &program.const_vars {
writeln!(f, "{name}:")?;
for item in &program.ast.0 {
match item {
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(())
}
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(())
}
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(())
}
}
// 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(&param.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(&lt.name, typ.size_of(program)?, typ.clone());
if let Some(value) = &lt.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(())
}
}

View 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

View File

@@ -1,6 +1,6 @@
use std::{fs::File, ops::Deref};
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");
@@ -19,8 +19,8 @@ impl Target for CGen {
fn write_code(&mut self, program: &crate::parser::ast::Program, f: &mut File) -> anyhow::Result<()> {
self.write_fat_comment(f, "Internal number types");
for (name, typ) in &program.types {
if let TypeType::Builtin { size: _, signed: _ } = typ {
self.write_internal_type(f, program, name, typ)?;
if let Type::Builtin { name: _, size: _, signed: _ } = typ.inner() {
self.write_internal_type(f, program, name, typ.inner())?;
}
}
@@ -34,9 +34,7 @@ impl Target for CGen {
self.write_fat_comment(f, "Type aliases");
for (name, typ) in &program.types {
if let TypeType::Normal(t) = typ {
self.write_typedef(f, program, t.inner(), name)?;
}
self.write_typedef(f, program, typ.inner(), name)?;
}
self.write_fat_comment(f, "Struct definitions");
@@ -45,7 +43,7 @@ impl Target for CGen {
}
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())?;
if let Some(rett) = &func.ret_type {
slf.write_type(f, program, rett.inner())?;
@@ -121,7 +119,7 @@ impl CGen {
Expr::If(_ifexpr) => {
}
Expr::BinOp { typ, left, right } => {
Expr::BinOp { typ, .. } => {
match typ {
Punctuation::Eq => {
@@ -182,6 +180,7 @@ impl CGen {
pf.push_str(&part(f, program, inner)?);
pf.push_str("[]");
}
Type::Builtin { .. } => ()
}
Ok(pf)
}
@@ -223,14 +222,14 @@ impl CGen {
write!(f, "[]")?;
}
}
Type::Builtin { .. } => ()
}
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 {
TypeType::Normal(typ) => unreachable!(),
TypeType::Builtin{ size, signed } => {
Type::Builtin{ name, size, signed } => {
if *size == 0 {
return Ok(());
}
@@ -249,7 +248,7 @@ impl CGen {
8 => write!(f, "long long ")?,
3 | 5 | 6 | 7 => unreachable!(),
}
writeln!(f, "{};", name.0)?;
writeln!(f, "{};", name)?;
}
_ => (),
}
@@ -267,7 +266,7 @@ impl CGen {
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())?;
if let Some(rett) = &func.ret_type {
self.write_type(f, program, rett.inner())?;
@@ -295,7 +294,7 @@ impl CGen {
Ast::Expr(expr) => {
self.write_expr(f, program, expr.inner(), expr.loc())?
}
Ast::Statement(stat) => {
Ast::Statement(_) => {
//todo!()
}
}

View File

@@ -1,4 +1,4 @@
// pub mod asmgen;
pub mod cgen;
pub mod asmgen;
// pub mod cgen;
// pub mod qbegen;
// pub mod llvmgen;

View File

@@ -1,7 +1,7 @@
use std::{collections::HashMap, fmt::Display};
use anyhow::bail;
use parse_int::parse;
use crate::{common::{loc::LocIncr, Loc}, error, lerror};
use crate::{common::{loc::LocIncr, Loc},lerror};
pub mod tokentype;
use tokentype::*;
@@ -30,6 +30,12 @@ impl Token {
pub fn tt(&self) -> &TokenType {
&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);
last = *c;
}
let buf = buf
.replace("\\n", "\n")
.replace("\\r", "\r");
tokens.push(Token::new(TokenType::string(&buf, false), &loc));
}
'\'' => {
@@ -251,6 +260,7 @@ lazy_static::lazy_static!(
("return", TokenType::Keyword(Keyword::Return)),
("loop", TokenType::Keyword(Keyword::Loop)),
("as", TokenType::Keyword(Keyword::As)),
("self", TokenType::Keyword(Keyword::Self_)),
("{", TokenType::Delim(Delimiter::CurlyL)),
("}", TokenType::Delim(Delimiter::CurlyR)),
("[", TokenType::Delim(Delimiter::SquareL)),
@@ -272,7 +282,6 @@ lazy_static::lazy_static!(
("%", TokenType::Punct(Punctuation::Mod)),
("<<", TokenType::Punct(Punctuation::Shl)),
(">>", TokenType::Punct(Punctuation::Shr)),
("&&", TokenType::Punct(Punctuation::AndAnd)),
("||", TokenType::Punct(Punctuation::OrOr)),
("|", TokenType::Punct(Punctuation::Or)),
(">", TokenType::Punct(Punctuation::Gt)),

View File

@@ -1,6 +1,8 @@
use core::panic;
use std::fmt::Display;
use crate::parser::ast::expr::Path;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
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)]
pub struct Number {
@@ -63,7 +74,7 @@ pub enum Keyword {
Type, While, For, Break, Continue,
Let, Const, Mut, Static,
True, False, Include, Extern, Return,
As, Loop
As, Loop, Self_
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -162,7 +173,7 @@ impl TokenType {
_ => 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()))
}
pub fn number(val: usize, base: u8, signed: bool) -> Self {

View File

@@ -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;
struct Scope {
}
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);
collect_types_and_constants(prog, &items);
collect_types_and_constants(prog, &mut items);
check_that_types_exist_for_items(prog, &items)?;
//dbg!(&prog.types);
//dbg!(&prog.structs);
//dbg!(&prog.enums);
//dbg!(&prog.member_functions);
//dbg!(&prog.functions);
for item in items {
for item in items.iter_mut() {
match item {
Ast::Statement(stat) => {
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::Statement(stat) => validate_stat(prog, stat, CurrentState::Outside)?,
Ast::Expr(_) => unreachable!()
}
}
prog.ast.0 = items;
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, &param.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<()> {
let mut errored = false;
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 {
if let Err(_) = validate_type(prog, t) {
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 {
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;
}
}
if let Some(ret_typ) = &func.ret_type {
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;
}
}
},
Statement::Include |
Statement::Let { .. } => (),
Statement::ConstVar(var) => {
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::Struct(strct) => {
for (name, t) in &strct.fields {
for (name, t) in strct.fields.iter() {
if let Err(_) = validate_type(prog, t) {
lerror!(t.loc(), "Type '{}', of field, '{}.{name}' does not exist", t.inner(), strct.name);
errored = true;
@@ -104,57 +673,66 @@ fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec<Ast>) -> any
Ok(())
}
fn validate_type(prog: &mut Program, typ: &LocBox<Type>) -> anyhow::Result<()> {
fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> 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<Type> {
match typ {
Type::SizedArray { inner, .. } |
Type::UnsizedArray { inner, .. } |
Type::Ref { inner, .. } => f(prog, inner, loc),
Type::Owned(typ) => {
if prog.enums.get(typ).is_some() ||
prog.types.get(typ).is_some() ||
prog.structs.get(typ).is_some() {
Ok(())
Type::SizedArray { .. } |
Type::UnsizedArray { .. } |
Type::FnPtr(_) |
Type::Ref { .. } => Ok(typ.clone()),
Type::Owned(ident) => {
if let Some(builtin) = get_builtin_from_name(&ident.0) {
Ok(builtin)
} else
if prog.enums.get(ident).is_some() ||
prog.types.get(ident).is_some() ||
prog.structs.get(ident).is_some() {
Ok(typ.clone())
} else {
// lerror!(loc, "Could not find type '{}'", typ.0);
lerror!(loc, "Could not find type '{}'", ident.0);
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>) {
for item in items {
fn collect_types_and_constants(prog: &mut Program, items: &mut Vec<Ast>) {
for item in items.iter_mut() {
match item {
Ast::Statement(stat) => {
match stat.inner() {
let loc = stat.loc().clone();
match stat.inner_mut() {
Statement::Fn(func)=> {
if let Some(struct_name) = &func.struct_name {
if let Some(v) = prog.member_functions.get_mut(&struct_name) {
v.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone()));
} else {
let mut v = HashMap::new();
v.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone()));
prog.member_functions.insert(struct_name.clone(), v);
if let Some(name) = &func.name {
if let Some(struct_name) = &func.struct_name {
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 {
prog.functions.insert(name.clone(), LocBox::new(&loc, func.clone()));
}
} else {
prog.functions.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone()));
}
}
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) => {
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) => {
let typ = alias.clone().typ.inner().clone();
prog.types.insert(alias.name.clone(), predefined::TypeType::Normal(LocBox::new(stat.loc(), typ)));
let typ = alias.clone().typ.inner_mut().clone();
prog.types.insert(alias.name.clone(), LocBox::new(stat.loc(), typ));
}
Statement::Include |
Statement::Let { .. } => (),
Statement::ConstVar(var) => {
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());
}
_ => ()
}
}
}
}

View File

@@ -1,68 +1,157 @@
use std::collections::HashMap;
use lazy_static::lazy_static;
use crate::common::Loc;
use crate::parser::ast::expr::Path;
use crate::parser::typ::parse_type;
use crate::{common::loc::LocBox, parser::ast::{statement::Function, typ::Type, Ident, Program}};
#[cfg(target_arch="x86_64")]
const SIZE: usize = 8;
pub const SIZE: usize = 8;
#[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!(
pub static ref TYPES_RAW: HashMap<&'static str, (usize, bool)> = [
("void", (0, false)),
("usize", (SIZE, false)),
("isize", (SIZE, true)),
("u8", (1, false)),
("u16", (2, false)),
("u32", (4, false)),
("u64", (8, false)),
("i8", (1, true)),
("i16", (2, true)),
("i32", (4, true)),
("i64", (8, true)),
].into();
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, &'static str)>, &'static str)> = [
("syscall", (vec![
("arg_count", "u8"),
("sc_num", "u8"),
("args", "&[&void]")
], "usize")),
pub static ref TYPES_RAW: Vec<Type> = [
BuiltinType::void(),
BuiltinType::usize(),
BuiltinType::isize(),
BuiltinType::u8(),
BuiltinType::u16(),
BuiltinType::u32(),
BuiltinType::u64(),
BuiltinType::i8(),
BuiltinType::i16(),
BuiltinType::i32(),
BuiltinType::i64(),
BuiltinType::bool(),
BuiltinType::char()
].to_vec();
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, Type)>, Type)> = [
("__INTERNAL_syscall0", (vec![
("sc_num", BuiltinType::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();
);
#[derive(Debug, Clone)]
pub enum TypeType {
Normal(LocBox<Type>),
Builtin {
size: usize,
signed: bool,
pub fn get_builtin_from_name(type_name: &str) -> Option<Type> {
for t in TYPES_RAW.iter() {
match t {
Type::Builtin { name, .. } if name.as_str() == type_name => {
return Some(t.clone());
}
Type::Builtin { .. } => (),
_ => unreachable!()
}
}
None
}
pub fn load_builtin(prog: &mut Program) {
for (name, (size, signed)) in TYPES_RAW.iter() {
prog.types.insert(Ident(name.to_string()), TypeType::Builtin{ size: *size, signed: *signed });
let loc = Loc::new("(internal)", 0, 0);
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() {
let mut params = Vec::new();
let mut ret_type = None;
if ret_typ.len() > 0 {
let mut ret_t_tokens = crate::tokeniser::tokenise(&ret_typ, "(internal)").unwrap();
let typ = parse_type(&mut ret_t_tokens).unwrap();
ret_type = Some(LocBox::new(&Loc::default(), typ.inner().clone()));
if !ret_typ.is_void() {
ret_type = Some(Box::new(LocBox::new(&Loc::default(), ret_typ.clone())));
}
for (name, typ) in args {
let mut tokens = crate::tokeniser::tokenise(&typ, "(internal)").unwrap();
let typ = parse_type(&mut tokens).unwrap();
params.push((Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), typ.inner().clone())));
params.push((Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), typ.clone())));
}
let f = Function {
struct_name: None,
name: Ident(name.to_string()),
name: Some(Ident(name.to_string())),
params,
ret_type,
qual_const: false,

2
std/core.mcl Normal file
View File

@@ -0,0 +1,2 @@
include "std/str.mcl";
include "std/io/mod.mcl";

12
std/io/mod.mcl Normal file
View 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
View 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
View File

@@ -0,0 +1,14 @@
type cstr = [u8];
struct str {
len: usize,
inner: &cstr
}
fn str.len(&self) -> usize {
return self.len;
}

View File

@@ -1,21 +1,47 @@
type str = [u8];
include "std.mcl";
struct Foo {
a: usize,
b: &str
}
fn Foo.new(a: usize, b: &str) -> Foo {
return Foo {
a: a,
fn Foo.new(a1: usize, b: &str) -> &Foo {
return &Foo {
a: a1,
b: b
};
}
fn main() -> i32 {
let obj = Foo::new();
fn print(s: &str) {
// do nothign for now
}
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;

BIN
test2 Executable file

Binary file not shown.

18
test2.mcl Normal file
View 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
View File

@@ -0,0 +1 @@
mod parser;

3
tests/parser/expr.rs Normal file
View File

@@ -0,0 +1,3 @@

3
tests/parser/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
mod expr;
mod stat;
mod typ;

View 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
View 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
}

View 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");
}

View 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
View 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");
}

View File

@@ -1 +0,0 @@
[]