├── .gitignore
├── macro
├── .gitignore
├── Cargo.toml
├── Cargo.lock
└── src
│ └── lib.rs
├── notebooks
└── .gitignore
├── monoteny
├── core
│ ├── bool.monoteny
│ ├── run.monoteny
│ ├── transpilation.monoteny
│ ├── strings.monoteny
│ └── debug.monoteny
├── common.monoteny
├── core.monoteny
└── common
│ ├── precedence.monoteny
│ ├── debug.monoteny
│ └── math.monoteny
├── src
├── util
│ ├── graphs.rs
│ ├── hash.rs
│ ├── strings.rs
│ ├── file_writer.rs
│ ├── multimap.rs
│ ├── iter.rs
│ ├── position.rs
│ ├── vec.rs
│ ├── fmt.rs
│ └── graphs
│ │ ├── change_graph.rs
│ │ ├── task_dependency_graph.rs
│ │ └── node_tree.rs
├── interpreter
│ ├── builtins.rs
│ ├── compile.rs
│ ├── vm
│ │ ├── util.rs
│ │ └── call_frame.rs
│ ├── opcode.rs
│ ├── data.rs
│ ├── data_layout.rs
│ ├── builtins
│ │ └── modules.rs
│ ├── disassembler.rs
│ ├── chunks.rs
│ ├── run.rs
│ └── compile
│ │ └── function_descriptor_compiler.rs
├── parser
│ ├── strings.rs
│ ├── expressions
│ │ └── token.rs
│ ├── lexer
│ │ └── token.rs
│ ├── tests.rs
│ ├── error.rs
│ └── grammar.rs
├── static_analysis
│ └── implicit_clones.rs
├── util.rs
├── program.rs
├── static_analysis.rs
├── interpreter.rs
├── resolver
│ ├── structs.rs
│ ├── ambiguous.rs
│ ├── interpreter_mock.rs
│ ├── fields.rs
│ ├── precedence_order.rs
│ ├── decorations.rs
│ ├── ambiguous
│ │ └── abstract_call.rs
│ ├── imports.rs
│ ├── referencible.rs
│ ├── imperative_builder.rs
│ ├── conformance.rs
│ └── type_factory.rs
├── ast
│ ├── trait_.rs
│ ├── string.rs
│ ├── block.rs
│ ├── conformance.rs
│ ├── function.rs
│ ├── struct_.rs
│ ├── array.rs
│ ├── expression.rs
│ ├── decorated.rs
│ ├── term.rs
│ └── statement.rs
├── transpiler
│ ├── python
│ │ ├── strings.rs
│ │ ├── types.rs
│ │ ├── class.rs
│ │ └── representations.rs
│ ├── structs.rs
│ ├── namespaces.rs
│ └── tests.rs
├── resolver.rs
├── program
│ ├── traits.rs
│ ├── functions.rs
│ ├── functions
│ │ ├── implementation.rs
│ │ ├── overload.rs
│ │ ├── function_binding.rs
│ │ ├── representation.rs
│ │ ├── logic.rs
│ │ └── head.rs
│ ├── traits
│ │ ├── structs.rs
│ │ ├── binding.rs
│ │ ├── trait_.rs
│ │ └── conformance.rs
│ ├── primitives.rs
│ ├── debug.rs
│ ├── expression_tree.rs
│ ├── allocation.rs
│ ├── module.rs
│ └── types.rs
├── main.rs
├── repository.rs
├── ast.rs
├── cli
│ ├── run.rs
│ ├── expression.rs
│ ├── logging.rs
│ ├── check.rs
│ └── transpile.rs
├── refactor
│ ├── analyze.rs
│ ├── call_graph.rs
│ ├── locals.rs
│ └── simplify.rs
├── parser.rs
├── cli.rs
├── source.rs
└── transpiler.rs
├── resources
├── Monoteny.afdesign
├── MonotenyLogo.png
├── ExampleInheritance.afdesign
└── Monoteny.tmbundle
│ └── info.plist
├── .idea
├── codeStyles
│ ├── codeStyleConfig.xml
│ └── Project.xml
├── vcs.xml
├── other.xml
├── .gitignore
├── modules.xml
├── misc.xml
└── runConfigurations
│ ├── Transpile.xml
│ ├── Run.xml
│ ├── Test.xml
│ └── Check.xml
├── test-code
├── hello_world.monoteny
├── requirements
│ ├── eq0.monoteny
│ ├── eq1.monoteny
│ └── eq2.monoteny
├── debug
│ └── assert.monoteny
├── control_flow
│ ├── if_then_else.monoteny
│ └── and_or.monoteny
├── grammar
│ ├── string_interpolation.monoteny
│ └── custom_grammar.monoteny
├── monomorphization
│ └── branch.monoteny
└── traits
│ ├── simple.monoteny
│ ├── fields.monoteny
│ └── conformance.monoteny
├── python
├── pyproject.toml
└── poetry.lock
├── CONTRIBUTING.md
├── .github
└── workflows
│ ├── check-rust.yml
│ └── build.yml
├── Cargo.toml
└── monoteny.iml
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/macro/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/notebooks/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/monoteny/core/bool.monoteny:
--------------------------------------------------------------------------------
1 | def true -> Bool;
2 | def false -> Bool;
3 |
--------------------------------------------------------------------------------
/src/util/graphs.rs:
--------------------------------------------------------------------------------
1 | pub mod task_dependency_graph;
2 | pub mod change_graph;
3 | pub mod node_tree;
4 |
--------------------------------------------------------------------------------
/resources/Monoteny.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ivorforce/Monoteny/HEAD/resources/Monoteny.afdesign
--------------------------------------------------------------------------------
/resources/MonotenyLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ivorforce/Monoteny/HEAD/resources/MonotenyLogo.png
--------------------------------------------------------------------------------
/src/interpreter/builtins.rs:
--------------------------------------------------------------------------------
1 | pub mod primitives;
2 | pub mod traits;
3 | pub mod vm;
4 | pub mod modules;
5 |
--------------------------------------------------------------------------------
/resources/ExampleInheritance.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ivorforce/Monoteny/HEAD/resources/ExampleInheritance.afdesign
--------------------------------------------------------------------------------
/src/interpreter/compile.rs:
--------------------------------------------------------------------------------
1 | pub mod function_compiler;
2 | pub mod compile_server;
3 | pub mod function_descriptor_compiler;
4 |
--------------------------------------------------------------------------------
/monoteny/common.monoteny:
--------------------------------------------------------------------------------
1 | include!(
2 | module!(".precedence"),
3 | module!(".math"),
4 | module!(".debug"),
5 | );
6 |
--------------------------------------------------------------------------------
/macro/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "monoteny-macro"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [lib]
7 | crate_type = ["proc-macro"]
8 |
--------------------------------------------------------------------------------
/monoteny/core/run.monoteny:
--------------------------------------------------------------------------------
1 | use!(
2 | module!("core.bool"),
3 | module!("core.strings"),
4 | module!("core.debug"),
5 | );
6 |
7 | def main();
8 |
--------------------------------------------------------------------------------
/monoteny/core.monoteny:
--------------------------------------------------------------------------------
1 | include!(
2 | module!(".bool"),
3 | module!(".strings"),
4 | module!(".debug"),
5 | module!(".run"),
6 | module!(".transpilation"),
7 | );
8 |
--------------------------------------------------------------------------------
/src/parser/strings.rs:
--------------------------------------------------------------------------------
1 | use lazy_static::lazy_static;
2 | use regex::Regex;
3 |
4 | lazy_static! {
5 | pub static ref REGEX_UNESCAPE: Regex = Regex::new(r"\\(.)").unwrap();
6 | }
7 |
--------------------------------------------------------------------------------
/src/util/hash.rs:
--------------------------------------------------------------------------------
1 | use std::hash::{Hash, Hasher};
2 |
3 | pub fn one(object: impl Hash, mut hasher: impl Hasher) -> u64 {
4 | object.hash(&mut hasher);
5 | hasher.finish()
6 | }
7 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/macro/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "monoteny-macro"
7 | version = "0.1.0"
8 |
--------------------------------------------------------------------------------
/src/static_analysis/implicit_clones.rs:
--------------------------------------------------------------------------------
1 | use crate::program::functions::FunctionImplementation;
2 |
3 | pub fn explicitize_clones(implementation: &mut FunctionImplementation) {
4 | todo!()
5 | }
6 |
--------------------------------------------------------------------------------
/src/util.rs:
--------------------------------------------------------------------------------
1 | pub mod fmt;
2 | pub mod multimap;
3 | pub mod hash;
4 | pub mod position;
5 | pub mod iter;
6 | pub mod vec;
7 | pub mod strings;
8 | pub mod file_writer;
9 | pub mod graphs;
10 |
--------------------------------------------------------------------------------
/test-code/hello_world.monoteny:
--------------------------------------------------------------------------------
1 | use!(module!("common"));
2 |
3 | def main! :: {
4 | write_line("Hello World!");
5 | };
6 |
7 | def transpile! :: {
8 | transpiler.add(main);
9 | };
10 |
--------------------------------------------------------------------------------
/src/interpreter/vm/util.rs:
--------------------------------------------------------------------------------
1 | use crate::interpreter::data::Value;
2 |
3 | #[inline(always)]
4 | pub unsafe fn pop_stack(sp: &mut *mut Value) -> Value {
5 | *sp = (*sp).offset(-8);
6 | **sp
7 | }
8 |
--------------------------------------------------------------------------------
/.idea/other.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/src/program.rs:
--------------------------------------------------------------------------------
1 | pub mod allocation;
2 | pub mod expression_tree;
3 | pub mod functions;
4 | pub mod generics;
5 | pub mod primitives;
6 | pub mod traits;
7 | pub mod types;
8 | pub mod module;
9 | pub mod debug;
10 |
--------------------------------------------------------------------------------
/test-code/requirements/eq0.monoteny:
--------------------------------------------------------------------------------
1 | -- Tests whether we can call is_equal statically.
2 |
3 | def main! :: {
4 | is_equal(1, 1 'Int32);
5 | };
6 |
7 | def transpile! :: {
8 | transpiler.add(main);
9 | };
10 |
--------------------------------------------------------------------------------
/test-code/debug/assert.monoteny:
--------------------------------------------------------------------------------
1 | -- Tests that assertions can fail.
2 |
3 | use!(module!("common"));
4 |
5 | def main! :: {
6 | assert(false);
7 | };
8 |
9 | def transpile! :: {
10 | transpiler.add(main);
11 | };
12 |
--------------------------------------------------------------------------------
/src/static_analysis.rs:
--------------------------------------------------------------------------------
1 | use crate::error::RResult;
2 | use crate::program::functions::FunctionImplementation;
3 |
4 | pub mod implicit_clones;
5 |
6 | pub fn check(function: &mut FunctionImplementation) -> RResult<()> {
7 | Ok(())
8 | }
9 |
--------------------------------------------------------------------------------
/src/interpreter.rs:
--------------------------------------------------------------------------------
1 | pub mod vm;
2 | pub mod run;
3 | pub mod chunks;
4 | pub mod builtins;
5 | pub mod opcode;
6 | pub mod disassembler;
7 | pub mod data;
8 | pub mod runtime;
9 | pub mod data_layout;
10 | pub mod compile;
11 | mod tests;
12 |
--------------------------------------------------------------------------------
/src/interpreter/vm/call_frame.rs:
--------------------------------------------------------------------------------
1 | use crate::interpreter::chunks::Chunk;
2 | use crate::interpreter::data::Value;
3 | use std::rc::Rc;
4 |
5 | pub struct CallFrame {
6 | pub chunk: Rc,
7 | pub ip: *const u8,
8 | pub fp: *mut Value,
9 | }
10 |
--------------------------------------------------------------------------------
/test-code/control_flow/if_then_else.monoteny:
--------------------------------------------------------------------------------
1 | def main! :: {
2 | if true :: _write_line("true")
3 | else :: if false :: _write_line("false")
4 | else :: _write_line("maybe");
5 | };
6 |
7 | def transpile! :: {
8 | transpiler.add(main);
9 | };
10 |
--------------------------------------------------------------------------------
/test-code/grammar/string_interpolation.monoteny:
--------------------------------------------------------------------------------
1 | -- This tests whether we strings can be interpolated.
2 |
3 | def main! :: {
4 | _write_line("Left: \("String"), Right: \(2 'Int32)");
5 | };
6 |
7 | def transpile! :: {
8 | transpiler.add(main);
9 | };
10 |
--------------------------------------------------------------------------------
/monoteny/core/transpilation.monoteny:
--------------------------------------------------------------------------------
1 | use!(
2 | module!("core.bool"),
3 | module!("core.strings"),
4 | module!("core.debug"),
5 | );
6 |
7 | trait Transpiler {
8 | };
9 |
10 | def (self 'Transpiler).add(object '#A);
11 |
12 | def transpile(transpiler 'Transpiler);
13 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/resolver/structs.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::Display;
2 | use std::hash::Hash;
3 |
4 | use crate::program::expression_tree::ExpressionID;
5 | use crate::program::functions::ParameterKey;
6 |
7 | #[derive(Clone)]
8 | pub struct AnonymousStruct {
9 | pub keys: Vec,
10 | pub values: Vec
11 | }
12 |
--------------------------------------------------------------------------------
/monoteny/common/precedence.monoteny:
--------------------------------------------------------------------------------
1 | precedence_order!(
2 | LeftUnaryPrecedence: LeftUnary,
3 | ExponentiationPrecedence: Right,
4 | MultiplicationPrecedence: Left,
5 | AdditionPrecedence: Left,
6 | ComparisonPrecedence: LeftConjunctivePairs,
7 | LogicalConjunctionPrecedence: Left,
8 | LogicalDisjunctionPrecedence: Left,
9 | );
10 |
--------------------------------------------------------------------------------
/test-code/requirements/eq1.monoteny:
--------------------------------------------------------------------------------
1 | -- Tests whether we can call is_equal with one layer of indirection.
2 | -- This is_equal_1 calls is_equal from its requirements.
3 |
4 | def is_equal_1(lhs '$Eq, rhs '$Eq) -> Bool :: is_equal(lhs, rhs);
5 |
6 | def main! :: {
7 | is_equal_1(1, 1 'Int32);
8 | };
9 |
10 | def transpile! :: {
11 | transpiler.add(main);
12 | };
13 |
--------------------------------------------------------------------------------
/python/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "monoteny"
3 | version = "0.1.0"
4 | description = ""
5 | authors = ["Lukas Tenbrink "]
6 | readme = "README.md"
7 |
8 | [tool.poetry.dependencies]
9 | python = "^3.9"
10 | numpy = "^1.26.4"
11 |
12 |
13 | [build-system]
14 | requires = ["poetry-core"]
15 | build-backend = "poetry.core.masonry.api"
16 |
--------------------------------------------------------------------------------
/test-code/monomorphization/branch.monoteny:
--------------------------------------------------------------------------------
1 | -- Tests whether monomorphization can yield two separate functions.
2 |
3 | def (self '$Number).square() -> $Number :: multiply(self, self);
4 |
5 | def main! :: {
6 | _write_line("\(3.square() 'Int32)");
7 | _write_line("\(3.square() 'Float32)");
8 | };
9 |
10 | def transpile! :: {
11 | transpiler.add(main);
12 | };
13 |
--------------------------------------------------------------------------------
/src/util/strings.rs:
--------------------------------------------------------------------------------
1 | pub fn map_chars(string: &str, fun: impl Fn(char) -> Option<&'static str>) -> String {
2 | let mut output = String::with_capacity(string.len());
3 | for char in string.chars() {
4 | if let Some(map) = fun(char) {
5 | output.push_str(map);
6 | }
7 | else {
8 | output.push(char);
9 | }
10 | }
11 | output
12 | }
13 |
--------------------------------------------------------------------------------
/monoteny/core/strings.monoteny:
--------------------------------------------------------------------------------
1 | use!(
2 | module!("core.bool"),
3 | );
4 |
5 | -- The easiest way to format an object: It formats itself!
6 | def format(object '$ToString) -> String :: object.to_string();
7 |
8 | declare String is ToString :: {
9 | def (self 'Self).to_string() -> String :: self;
10 | };
11 |
12 | -- Provided by the transpiler.
13 | def add(lhs 'String, rhs 'String) -> String;
14 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/ast/trait_.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Formatter};
2 |
3 | use crate::ast::Block;
4 |
5 | #[derive(Eq, PartialEq, Clone)]
6 | pub struct TraitDefinition {
7 | pub name: String,
8 | pub block: Box,
9 | }
10 |
11 | impl Display for TraitDefinition {
12 | fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
13 | write!(fmt, "trait {} {{\n{}}}", self.name, self.block)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/monoteny/core/debug.monoteny:
--------------------------------------------------------------------------------
1 | use!(
2 | module!("core.bool"),
3 | module!("core.strings"),
4 | );
5 |
6 | -- Supplied by transpiler.
7 | def _exit_with_error() -> #;
8 |
9 | -- TODO This should be attached to a Console trait.
10 | -- But that only makes sense once we can constant fold away objects without storage.
11 | -- - otherwise, we'll have ugly write_line(console, "...") calls!
12 | def _write_line(value 'String);
13 |
--------------------------------------------------------------------------------
/src/transpiler/python/strings.rs:
--------------------------------------------------------------------------------
1 | use crate::util::strings;
2 |
3 | pub fn escape_string(string: &str) -> String {
4 | strings::map_chars(string, |ch| {
5 | Some(match ch {
6 | '\\' => "\\\\",
7 | '\n' => "\\n",
8 | '\0' => "\\0",
9 | '\t' => "\\t",
10 | '\r' => "\\r",
11 | '\"' => "\\\"",
12 | _ => return None,
13 | })
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/test-code/requirements/eq2.monoteny:
--------------------------------------------------------------------------------
1 | -- Tests whether we can call is_equal with one layer of indirection.
2 | -- This is_equal_2 must pass on its requirements to is_equal_1.
3 |
4 | def is_equal_1(lhs '$Eq, rhs '$Eq) -> Bool :: is_equal(lhs, rhs);
5 | def is_equal_2(lhs '$Eq, rhs '$Eq) -> Bool :: is_equal_1(lhs, rhs);
6 |
7 | def main! :: {
8 | is_equal_2(1, 1 'Int32);
9 | };
10 |
11 | def transpile! :: {
12 | transpiler.add(main);
13 | };
14 |
--------------------------------------------------------------------------------
/src/resolver.rs:
--------------------------------------------------------------------------------
1 | pub use crate::resolver::global::resolve_file;
2 |
3 | pub mod scopes;
4 | pub mod imperative;
5 | pub mod traits;
6 | pub mod conformance;
7 | pub mod global;
8 | pub mod ambiguous;
9 | pub mod type_factory;
10 | pub mod interface;
11 | pub mod fields;
12 | pub mod imports;
13 | pub mod interpreter_mock;
14 | pub mod referencible;
15 | pub mod structs;
16 | pub mod decorations;
17 | pub mod precedence_order;
18 | pub mod function;
19 | pub mod imperative_builder;
20 |
--------------------------------------------------------------------------------
/src/program/traits.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::Debug;
2 | use std::hash::{Hash, Hasher};
3 |
4 | use itertools::Itertools;
5 |
6 | pub use binding::TraitBinding;
7 | pub use conformance::{RequirementsAssumption, RequirementsFulfillment, TraitConformance, TraitConformanceWithTail};
8 | pub use graph::{TraitConformanceRule, TraitGraph};
9 | pub use structs::StructInfo;
10 | pub use trait_::{FieldHint, Trait};
11 |
12 | mod conformance;
13 | mod binding;
14 | mod graph;
15 | mod trait_;
16 | mod structs;
17 |
--------------------------------------------------------------------------------
/src/ast/string.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Formatter};
2 |
3 | use crate::ast::Struct;
4 |
5 | #[derive(PartialEq, Eq, Clone)]
6 | pub enum StringPart {
7 | Literal(String),
8 | Object(Box),
9 | }
10 |
11 | impl Display for StringPart {
12 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
13 | match self {
14 | StringPart::Literal(s) => write!(f, "{}", s),
15 | StringPart::Object(struct_) => write!(f, "{}", struct_),
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test-code/traits/simple.monoteny:
--------------------------------------------------------------------------------
1 | -- Tests traits with fields.
2 |
3 | use!(module!("common"));
4 |
5 | trait Animal {
6 | var height_cm 'Float32;
7 | };
8 |
9 | def main! :: {
10 | -- Create a cat with pre-defined name and height.
11 | var animal = Animal(height_cm: 180);
12 |
13 | write_line("Height 1: \(animal.height_cm)cm");
14 |
15 | upd animal.height_cm = 150;
16 | write_line("Height 2: \(animal.height_cm)cm");
17 | };
18 |
19 | def transpile! :: {
20 | transpiler.add(main);
21 | };
22 |
--------------------------------------------------------------------------------
/src/ast/block.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Error, Formatter};
2 |
3 | use crate::ast::decorated::Decorated;
4 | use crate::ast::Statement;
5 | use crate::util::fmt::write_separated_display;
6 | use crate::util::position::Positioned;
7 |
8 | #[derive(Eq, PartialEq, Clone)]
9 | pub struct Block {
10 | pub statements: Vec>>>
11 | }
12 |
13 | impl Display for Block {
14 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
15 | write_separated_display(fmt, "\n", self.statements.iter())
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/ast/conformance.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Formatter};
2 |
3 | use crate::ast::expression::Expression;
4 | use crate::ast::Block;
5 |
6 | #[derive(Eq, PartialEq, Clone)]
7 | pub struct TraitConformanceDeclaration {
8 | pub declared_for: Expression,
9 | pub declared: Expression,
10 | pub block: Box,
11 | }
12 |
13 | impl Display for TraitConformanceDeclaration {
14 | fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
15 | write!(fmt, "declare {} is {} {{}} :: {{\n{}}}", self.declared_for, self.declared, self.block)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | extern crate core;
2 | #[macro_use]
3 | extern crate lalrpop_util;
4 |
5 | use std::env;
6 | use std::process::ExitCode;
7 |
8 | use itertools::Itertools;
9 |
10 | lalrpop_mod!(pub monoteny_grammar);
11 | pub mod interpreter;
12 | pub mod resolver;
13 | pub mod parser;
14 | pub mod program;
15 | pub mod transpiler;
16 | pub mod util;
17 | pub mod error;
18 | pub mod repository;
19 | pub mod refactor;
20 | pub mod source;
21 | pub mod cli;
22 | pub mod static_analysis;
23 | pub mod ast;
24 |
25 | fn main() -> ExitCode {
26 | println!("{}", env::args().join(" "));
27 | cli::run_command()
28 | }
29 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Monoteny is in its early stages of development. Its design and implementation are highly exploratory, and features are expected to stop and start working regularly as the language is expanded.
2 |
3 | I do not recommend using the language in its current form. If you want to get involved, discuss the language design or make contributions, please reach out to me.
4 |
5 | #### Socials
6 |
7 | - [Discord](https://discord.gg/zgyNx9K7mE) (for discussions)
8 | - [Other Socials](https://tenbrink.me) (if you do not use discord)
9 | - [Issues](https://github.com/Ivorforce/Monoteny/issues) (for tracking implementation issues)
10 |
--------------------------------------------------------------------------------
/src/repository.rs:
--------------------------------------------------------------------------------
1 | use crate::program::module::ModuleName;
2 | use std::collections::HashMap;
3 | use std::path::PathBuf;
4 |
5 | pub enum Loader {
6 | Path(PathBuf),
7 | Intrinsic(HashMap)
8 | }
9 |
10 | pub struct Repository {
11 | pub entries: HashMap,
12 | }
13 |
14 | impl Repository {
15 | pub fn new() -> Box {
16 | Box::new(Repository {
17 | entries: Default::default(),
18 | })
19 | }
20 |
21 | pub fn add(&mut self, name: &str, loader: Loader) {
22 | self.entries.insert(name.to_string(), loader);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/resources/Monoteny.tmbundle/info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | contactEmailRot13
6 | yhxnf.graoevax@tznvy.pbz
7 | contactName
8 | Lukas Tenbrink
9 | description
10 | Rudimentary grammar support for the Monoteny programming language.
11 | name
12 | Monoteny
13 | uuid
14 | 4A934ACC-37B3-48A2-8289-B0AEA3E0024B
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/util/file_writer.rs:
--------------------------------------------------------------------------------
1 | use std::fs::File;
2 | use std::io::Write;
3 | use std::path::{Path, PathBuf};
4 |
5 | pub fn write_file_safe(base_path: &Path, sub_path: &str, content: &str) -> PathBuf {
6 | let file_path = base_path.join(sub_path);
7 |
8 | if !file_path.starts_with(base_path) {
9 | panic!("Tried to write a file in unexpected directory: {}", file_path.as_os_str().to_string_lossy());
10 | }
11 |
12 | let mut f = File::create(file_path.clone()).expect("Unable to create file");
13 | let f: &mut (dyn Write) = &mut f;
14 | write!(f, "{}", content).expect("Error writing file");
15 |
16 | file_path
17 | }
--------------------------------------------------------------------------------
/test-code/control_flow/and_or.monoteny:
--------------------------------------------------------------------------------
1 | use!(module!("common"));
2 |
3 | def main! :: {
4 | if true and true :: _write_line("true and true");
5 | if false and true :: _write_line("false and true");
6 | if true and false :: _write_line("true and false");
7 | if false and false :: _write_line("false and false");
8 |
9 | if true or true :: _write_line("true or true");
10 | if false or true :: _write_line("false or true");
11 | if true or false :: _write_line("true or false");
12 | if false or false :: _write_line("false or false");
13 | };
14 |
15 | def transpile! :: {
16 | transpiler.add(main);
17 | };
18 |
--------------------------------------------------------------------------------
/src/ast.rs:
--------------------------------------------------------------------------------
1 | pub use array::{Array, ArrayArgument};
2 | pub use block::Block;
3 | pub use conformance::TraitConformanceDeclaration;
4 | pub use decorated::Decorated;
5 | pub use expression::Expression;
6 | pub use function::{Function, FunctionInterface};
7 | pub use statement::Statement;
8 | pub use string::StringPart;
9 | pub use struct_::{Struct, StructArgument};
10 | pub use term::{IfThenElse, Term};
11 | pub use trait_::TraitDefinition;
12 |
13 | mod array;
14 | mod block;
15 | mod struct_;
16 | mod trait_;
17 | mod conformance;
18 | mod statement;
19 | mod expression;
20 | mod term;
21 | mod string;
22 | mod decorated;
23 | mod function;
24 |
25 |
--------------------------------------------------------------------------------
/.github/workflows/check-rust.yml:
--------------------------------------------------------------------------------
1 | name: Rust Tests
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 | - run: rustup toolchain install stable --profile minimal
20 | - uses: Swatinem/rust-cache@v2
21 | with:
22 | prefix-key: "main"
23 | save-if: ${{ github.ref == 'refs/heads/main' }}
24 | - name: Build
25 | run: cargo build --verbose
26 | - name: Run tests
27 | run: cargo test --verbose
28 |
--------------------------------------------------------------------------------
/src/resolver/ambiguous.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::Display;
2 | use std::ops::Range;
3 |
4 | pub use abstract_call::AmbiguousAbstractCall;
5 | pub use function_call::{AmbiguousFunctionCall, AmbiguousFunctionCandidate};
6 |
7 | use crate::error::RResult;
8 | use crate::resolver::imperative::ImperativeResolver;
9 |
10 | pub mod function_call;
11 | pub mod abstract_call;
12 |
13 | pub enum AmbiguityResult {
14 | Ok(V),
15 | Ambiguous,
16 | }
17 |
18 | pub trait ResolverAmbiguity: Display {
19 | fn attempt_to_resolve(&mut self, expressions: &mut ImperativeResolver) -> RResult>;
20 |
21 | fn get_position(&self) -> Range;
22 | }
23 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "monoteny"
3 | version = "0.1.0"
4 | edition = "2021"
5 | build = "build.rs"
6 |
7 | [build-dependencies]
8 | lalrpop = { version = "0.20.2" }
9 |
10 | [dependencies]
11 | clap = "4.5.4"
12 | custom_error = "1.9.2"
13 | itertools = "0.12.1"
14 | lalrpop-util = { version = "0.20.2", features = ["lexer"] }
15 | regex = "1"
16 | replace_with = "0.1.7"
17 | strum = { version = "0.26.2", features = ["derive"] }
18 | try_map = "0.3.1"
19 | uuid = { version = "1.1.2", features = ["v4"] }
20 | monoteny-macro = { path = "macro" }
21 | log = "0.4.17"
22 | lazy_static = "1.4.0"
23 | colored = "2.0.4"
24 | linked_hash_set = "0.1.4"
25 | linked-hash-map = "0.5.6"
26 | display_with_options = "0.1.0"
27 | annotate-snippets = "0.11.1"
28 |
--------------------------------------------------------------------------------
/src/transpiler/python/types.rs:
--------------------------------------------------------------------------------
1 | use crate::program::types::{TypeProto, TypeUnit};
2 | use crate::transpiler::python::{ast, FunctionContext};
3 |
4 | pub fn transpile(type_def: &TypeProto, context: &FunctionContext) -> Box {
5 | match &type_def.unit {
6 | TypeUnit::Struct(s) => {
7 | let representation = &context.representations.type_ids.get(type_def).unwrap_or_else(|| panic!("Unable to find representation for type {:?}", s));
8 | Box::new(ast::Expression::NamedReference(context.names[representation].clone()))
9 | },
10 | TypeUnit::Generic(id) => panic!("Failed to transpile {:?}, generics shouldn't exist anymore at this point.", type_def),
11 | TypeUnit::Void => todo!(),
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/program/functions.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Debug, Display};
2 | use std::hash::{Hash, Hasher};
3 |
4 | use display_with_options::{DebugWithOptions, DisplayWithOptions};
5 |
6 | pub use function_binding::{resolve_binding, FunctionBinding};
7 | pub use head::{FunctionHead, FunctionType};
8 | pub use implementation::FunctionImplementation;
9 | pub use interface::{FunctionInterface, Parameter, ParameterKey};
10 | pub use logic::{FunctionLogic, FunctionLogicDescriptor, PrimitiveOperation};
11 | pub use overload::FunctionOverload;
12 | pub use representation::{FunctionCallExplicity, FunctionRepresentation, FunctionTargetType};
13 |
14 | mod interface;
15 | mod head;
16 | mod representation;
17 | mod overload;
18 | mod function_binding;
19 | mod logic;
20 | mod implementation;
21 |
--------------------------------------------------------------------------------
/src/program/functions/implementation.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use std::rc::Rc;
3 |
4 | use crate::program::allocation::ObjectReference;
5 | use crate::program::expression_tree::ExpressionTree;
6 | use crate::program::functions::FunctionInterface;
7 | use crate::program::generics::TypeForest;
8 | use crate::program::traits::RequirementsAssumption;
9 |
10 | #[derive(Clone)]
11 | pub struct FunctionImplementation {
12 | pub interface: Rc,
13 |
14 | pub requirements_assumption: Box,
15 |
16 | pub expression_tree: Box,
17 | pub type_forest: Box,
18 |
19 | pub parameter_locals: Vec>,
20 | pub locals_names: HashMap, String>,
21 | }
22 |
--------------------------------------------------------------------------------
/test-code/traits/fields.monoteny:
--------------------------------------------------------------------------------
1 | -- Tests traits with fields.
2 |
3 | use!(module!("common"));
4 |
5 | trait Animal {
6 | -- Immutable Field
7 | let species 'String;
8 | let name 'String;
9 |
10 | -- Mutable Field
11 | var height_cm 'Float32;
12 | };
13 |
14 | def main! :: {
15 | -- Create a cat with pre-defined name and height.
16 | var animal = Animal(species: "Cat", name: "Noir", height_cm: 180);
17 |
18 | -- Write current status
19 | write_line("\(animal.name) (\(animal.species)) was: \(animal.height_cm)cm");
20 |
21 | -- We can set height_cm, but not species or name.
22 | upd animal.height_cm = 25;
23 | write_line("And is now: \(animal.height_cm)cm");
24 | };
25 |
26 | def transpile! :: {
27 | transpiler.add(main);
28 | };
29 |
--------------------------------------------------------------------------------
/src/util/multimap.rs:
--------------------------------------------------------------------------------
1 | use std::collections::hash_map::Entry;
2 | use std::collections::{HashMap, HashSet};
3 | use std::hash::Hash;
4 |
5 | pub fn insert_into_multimap(multimap: &mut HashMap>, key: K, value: V) where K: Hash, V: Hash, K: Eq, V: Eq, V: Clone, K: Clone {
6 | match multimap.entry(key) {
7 | Entry::Occupied(o) => {
8 | o.into_mut().insert(value);
9 | }
10 | Entry::Vacant(v) => {
11 | v.insert(HashSet::from([value]));
12 | }
13 | }
14 | }
15 |
16 | pub fn remove_from_multimap(multimap: &mut HashMap>, key: &K, value: &V) where K: Hash, V: Hash, K: Eq, V: Eq, V: Clone, K: Clone {
17 | if let Some(map) = multimap.get_mut(key) {
18 | map.remove(value);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/util/iter.rs:
--------------------------------------------------------------------------------
1 | use std::collections::VecDeque;
2 |
3 | pub struct Omega, F: FnMut(&E) -> I> {
4 | pub deeper: F,
5 | pub next: VecDeque,
6 | }
7 |
8 | impl, F: FnMut(&E) -> I> Iterator for Omega {
9 | type Item = E;
10 |
11 | fn next(&mut self) -> Option {
12 | self.next.pop_front().map(|current| {
13 | let next = (self.deeper)(¤t);
14 | self.next.extend(next);
15 | current
16 | })
17 | }
18 | }
19 |
20 | pub fn omega, I: Iterator- , F: FnMut(&E) -> I>(start: I0, mut deeper: F) -> Omega {
21 | Omega {
22 | deeper,
23 | next: VecDeque::from_iter(start),
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/util/position.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Formatter};
2 | use std::ops::Range;
3 |
4 | #[derive(PartialEq, Eq, Clone, Debug)]
5 | pub struct Positioned {
6 | pub position: Range,
7 | pub value: V,
8 | }
9 |
10 | impl Positioned {
11 | pub fn with_value(&self, v: V1) -> Positioned {
12 | Positioned {
13 | position: self.position.clone(),
14 | value: v,
15 | }
16 | }
17 | }
18 |
19 | pub fn positioned(v: V, start: usize, end: usize) -> Positioned {
20 | Positioned {
21 | position: start..end,
22 | value: v,
23 | }
24 | }
25 |
26 | impl Display for Positioned {
27 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
28 | write!(f, "{}", self.value)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/util/vec.rs:
--------------------------------------------------------------------------------
1 | use itertools::Itertools;
2 |
3 | pub fn swizzle(vec: &mut Vec, swizzle: &Vec) -> Vec {
4 | if swizzle.is_empty() {
5 | return vec.drain(..).collect_vec()
6 | }
7 |
8 | assert!(swizzle.iter().duplicates().next().is_none());
9 |
10 | // TODO Would be nicer with unsafe, but eh.
11 | let mut tmp_array = vec.drain(..).enumerate()
12 | .map(|(idx, obj)| (swizzle.iter().position(|p| p == &idx).unwrap_or(usize::MAX), obj))
13 | .collect_vec();
14 | tmp_array.sort_by_key(|(idx, obj)| *idx);
15 | let mut removed = vec![];
16 | for (idx, obj) in tmp_array {
17 | if idx != usize::MAX {
18 | vec.push(obj)
19 | }
20 | else {
21 | removed.push(obj)
22 | }
23 | }
24 | removed
25 | }
26 |
--------------------------------------------------------------------------------
/src/parser/expressions/token.rs:
--------------------------------------------------------------------------------
1 | use crate::ast;
2 | use crate::util::position::Positioned;
3 |
4 | pub enum Value<'a, Function> {
5 | Operation(Function, Vec>>),
6 | Identifier(&'a String),
7 | MacroIdentifier(&'a String),
8 | RealLiteral(&'a String),
9 | IntLiteral(&'a String),
10 | StringLiteral(&'a Vec>>),
11 | StructLiteral(&'a ast::Struct),
12 | ArrayLiteral(&'a ast::Array),
13 | Block(&'a ast::Block),
14 | MemberAccess(Box>, &'a String),
15 | FunctionCall(Box>, &'a ast::Struct),
16 | Subscript(Box>, &'a ast::Array),
17 | IfThenElse(&'a ast::IfThenElse),
18 | }
19 |
20 | pub enum Token<'a, Function> {
21 | Keyword(Positioned<&'a String>),
22 | Value(Box>>),
23 | }
24 |
--------------------------------------------------------------------------------
/src/program/traits/structs.rs:
--------------------------------------------------------------------------------
1 | use crate::program::allocation::ObjectReference;
2 | use crate::program::functions::FunctionHead;
3 | use crate::program::traits::Trait;
4 | use std::collections::HashMap;
5 | use std::hash::{Hash, Hasher};
6 | use std::rc::Rc;
7 |
8 | #[derive(Clone, PartialEq, Eq, Debug)]
9 | pub struct StructInfo {
10 | pub trait_: Rc,
11 |
12 | pub clone: Rc,
13 | pub constructor: Rc,
14 | pub fields: Vec>,
15 |
16 | pub field_names: HashMap, String>,
17 | pub field_getters: HashMap, Rc>,
18 | pub field_setters: HashMap, Rc>,
19 | }
20 |
21 | impl Hash for StructInfo {
22 | fn hash(&self, state: &mut H) {
23 | self.trait_.hash(state)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/monoteny/common/debug.monoteny:
--------------------------------------------------------------------------------
1 | use!(
2 | module!("common.precedence"),
3 | module!("common.math"),
4 | );
5 |
6 | def exit_with_error(error '$ToString) -> # :: {
7 | write_line(error);
8 | return _exit_with_error();
9 | };
10 | -- TODO These should accept Default / varargs parameters.
11 | def panic() -> # :: exit_with_error("internal error");
12 | def panic(error '$ToString) -> # :: exit_with_error("internal error (\(error))");
13 | def todo() -> # :: panic("not yet implemented");
14 | def todo(error '$ToString) -> # :: panic("not yet implemented: \(error)");
15 | def unreachable() -> # :: panic("entered unreachable code");
16 | def unreachable(error '$ToString) -> # :: panic("entered unreachable code: \(error)");
17 |
18 | def write_line(value '$ToString) :: _write_line(value.to_string());
19 |
20 | def assert(value 'Bool) :: {
21 | if not value :: exit_with_error("Assertion failure.");
22 | };
23 |
--------------------------------------------------------------------------------
/test-code/traits/conformance.monoteny:
--------------------------------------------------------------------------------
1 | -- Tests conformance to traits.
2 |
3 | use!(module!("common"));
4 |
5 | trait Animal {
6 | def (self 'Self).talk() -> String;
7 | };
8 |
9 | trait Cat {};
10 |
11 | -- Declare every Cat subtype is Animal.
12 | declare $Cat is Animal :: {
13 | def (self 'Self).talk() -> String :: "Meow";
14 | };
15 |
16 | trait Dog {};
17 |
18 | -- Declare just the Dog struct is Animal.
19 | declare Dog is Animal :: {
20 | def (self 'Self).talk() -> String :: "Bark";
21 | };
22 |
23 | def converse(lhs '$Animal#lhs, rhs '$Animal#rhs) :: {
24 | write_line("Conversation: \n \(lhs.talk()) \n \(rhs.talk())");
25 | };
26 |
27 | def main! :: {
28 | let dog = Dog();
29 | let cat = Cat();
30 |
31 | write_line(dog.talk());
32 | write_line(cat.talk());
33 | converse(cat, dog);
34 | converse(dog, cat);
35 | };
36 |
37 | def transpile! :: {
38 | transpiler.add(main);
39 | };
40 |
--------------------------------------------------------------------------------
/test-code/grammar/custom_grammar.monoteny:
--------------------------------------------------------------------------------
1 | -- This tests whether we can define a custom grammar.
2 |
3 | precedence_order!(
4 | LeftUnaryPrecedence: LeftUnary,
5 | ExponentiationPrecedence: Right,
6 | MultiplicationPrecedence: Left,
7 | AdditionPrecedence: Left,
8 | ComparisonPrecedence: LeftConjunctivePairs,
9 | LogicalConjunctionPrecedence: Left,
10 | LogicalDisjunctionPrecedence: Left,
11 | );
12 |
13 |
14 | ![pattern(- val, LeftUnaryPrecedence)]
15 | def _neg(val '$Number) -> $Number :: negative(val);
16 |
17 | ![pattern(lhs + rhs, AdditionPrecedence)]
18 | def _add(lhs '$Number, rhs '$Number) -> $Number :: add(lhs, rhs);
19 |
20 | ![pattern(lhs * rhs, MultiplicationPrecedence)]
21 | def _multiply(lhs '$Number, rhs '$Number) -> $Number :: multiply(lhs, rhs);
22 |
23 | def main! :: {
24 | _write_line(format(1 + -2 * 2 'Int32));
25 | };
26 |
27 | def transpile! :: {
28 | transpiler.add(main);
29 | };
30 |
--------------------------------------------------------------------------------
/src/cli/run.rs:
--------------------------------------------------------------------------------
1 | use std::path::PathBuf;
2 | use std::process::ExitCode;
3 |
4 | use clap::{arg, ArgMatches, Command};
5 |
6 | use crate::error::RResult;
7 | use crate::interpreter;
8 | use crate::interpreter::runtime::Runtime;
9 | use crate::program::module::module_name;
10 |
11 | pub fn make_command() -> Command {
12 | Command::new("run")
13 | .about("Run a file using the interpreter.")
14 | .arg_required_else_help(true)
15 | .arg(arg!( "file to run").value_parser(clap::value_parser!(PathBuf)))
16 | }
17 |
18 | pub fn run(args: &ArgMatches) -> RResult {
19 | let input_path = args.get_one::("PATH").unwrap();
20 |
21 | let mut runtime = Runtime::new()?;
22 | runtime.add_common_repository();
23 |
24 | let module = runtime.load_file_as_module(input_path, module_name("main"))?;
25 |
26 | interpreter::run::main(&module, &mut runtime)?;
27 |
28 | Ok(ExitCode::SUCCESS)
29 | }
30 |
--------------------------------------------------------------------------------
/src/parser/lexer/token.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Formatter};
2 |
3 | /// Token returned by the lexers
4 | #[derive(Debug, Clone, PartialEq)]
5 | pub enum Token<'a> {
6 | Identifier(&'a str),
7 | MacroIdentifier(&'a str),
8 | OperatorIdentifier(&'a str),
9 | StringLiteral(String),
10 | IntLiteral(&'a str),
11 | RealLiteral(&'a str),
12 | Symbol(&'a str),
13 | }
14 |
15 | impl<'i> Display for Token<'i> {
16 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
17 | match self {
18 | Token::Identifier(s) => write!(f, "{}", s),
19 | Token::MacroIdentifier(s) => write!(f, "{}", s),
20 | Token::OperatorIdentifier(s) => write!(f, "{}", s),
21 | Token::IntLiteral(s) => write!(f, "{}", s),
22 | Token::RealLiteral(s) => write!(f, "{}", s),
23 | Token::Symbol(s) => write!(f, "{}", s),
24 | Token::StringLiteral(s) => write!(f, "{}", s),
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Transpile.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/interpreter/opcode.rs:
--------------------------------------------------------------------------------
1 | #[allow(non_camel_case_types)]
2 | #[repr(u8)]
3 | #[derive(Debug, Copy, Clone)]
4 | pub enum OpCode {
5 | NOOP,
6 | PANIC,
7 | LOAD0,
8 | LOAD8,
9 | LOAD16,
10 | LOAD32,
11 | LOAD64,
12 | LOAD_LOCAL_32,
13 | STORE_LOCAL_32,
14 | LOAD_CONSTANT_32,
15 | DUP64,
16 | POP64,
17 | SWAP64,
18 | JUMP,
19 | JUMP_IF_FALSE,
20 | AND,
21 | OR,
22 | NOT,
23 | NEG,
24 | ADD,
25 | SUB,
26 | MUL,
27 | DIV,
28 | MOD,
29 | EXP,
30 | LOG,
31 | EQ,
32 | NEQ,
33 | GR,
34 | GR_EQ,
35 | LE,
36 | LE_EQ,
37 |
38 | // Member
39 | ALLOC_32,
40 | SET_MEMBER_32,
41 | GET_MEMBER_32,
42 |
43 | CALL,
44 | CALL_INTRINSIC,
45 | RETURN,
46 | }
47 |
48 | #[repr(u8)]
49 | #[derive(Debug, Copy, Clone)]
50 | pub enum Primitive {
51 | BOOL,
52 | I8,
53 | I16,
54 | I32,
55 | I64,
56 | U8,
57 | U16,
58 | U32,
59 | U64,
60 | F32,
61 | F64,
62 | }
63 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Check.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/cli/expression.rs:
--------------------------------------------------------------------------------
1 | use std::process::ExitCode;
2 |
3 | use clap::{arg, ArgMatches, Command};
4 |
5 | use crate::error::RResult;
6 | use crate::interpreter::runtime::Runtime;
7 | use crate::interpreter;
8 |
9 | pub fn make_command() -> Command {
10 | Command::new("expression")
11 | .about("Run an expression using the interpreter.")
12 | .arg_required_else_help(true)
13 | .arg(arg!( "expression to run").value_parser(clap::value_parser!(String)))
14 | }
15 |
16 | pub fn run(args: &ArgMatches) -> RResult {
17 | let expression = args.get_one::("EXPRESSION").unwrap();
18 | let full_expression = format!("use!(module!(\"common\")); def main! :: write_line({});", expression);
19 |
20 | let mut runtime = Runtime::new()?;
21 | runtime.add_common_repository();
22 |
23 | let module = runtime.load_text_as_module(&full_expression, vec!["dynamic".to_string()])?;
24 |
25 | interpreter::run::main(&module, &mut runtime)?;
26 |
27 | Ok(ExitCode::SUCCESS)
28 | }
29 |
--------------------------------------------------------------------------------
/src/interpreter/data.rs:
--------------------------------------------------------------------------------
1 | use std::alloc::{alloc, Layout};
2 | use std::intrinsics::transmute;
3 | use uuid::Uuid;
4 |
5 | #[derive(Copy, Clone)]
6 | pub union Value {
7 | pub bool: bool,
8 | pub u8: u8,
9 | pub u16: u16,
10 | pub u32: u32,
11 | pub u64: u64,
12 | pub i8: i8,
13 | pub i16: i16,
14 | pub i32: i32,
15 | pub i64: i64,
16 | pub f32: f32,
17 | pub f64: f64,
18 | pub ptr: *mut (),
19 | }
20 |
21 | impl Value {
22 | pub fn alloc() -> Value {
23 | Value { u8: 0 }
24 | }
25 | }
26 |
27 | // TODO The constants should probably be alloced in the chunk's constants, not 'anywhere'.
28 | pub unsafe fn string_to_ptr(string: &String) -> *mut () {
29 | let data = alloc(Layout::new::());
30 | std::ptr::write(data as *mut String, string.clone());
31 | transmute(data)
32 | }
33 |
34 | pub unsafe fn uuid_to_ptr(uuid: Uuid) -> *mut () {
35 | let data = alloc(Layout::new::());
36 | std::ptr::write(data as *mut Uuid, uuid.clone());
37 | transmute(data)
38 | }
39 |
--------------------------------------------------------------------------------
/src/refactor/analyze.rs:
--------------------------------------------------------------------------------
1 | use std::rc::Rc;
2 |
3 | use linked_hash_set::LinkedHashSet;
4 |
5 | use crate::program::expression_tree::ExpressionOperation;
6 | use crate::program::functions::{resolve_binding, FunctionBinding, FunctionImplementation};
7 |
8 | pub fn gather_callees(implementation: &FunctionImplementation) -> LinkedHashSet> {
9 | let mut callees = LinkedHashSet::new();
10 |
11 | // TODO Generic function calls would break this logic
12 | for expression_id in implementation.expression_tree.deep_children(implementation.expression_tree.root) {
13 | let expression_op = &implementation.expression_tree.values[&expression_id];
14 | match expression_op {
15 | ExpressionOperation::FunctionCall(f) => {
16 | callees.insert(resolve_binding(f, &implementation.type_forest));
17 | }
18 | ExpressionOperation::PairwiseOperations { .. } => {
19 | todo!()
20 | }
21 | _ => {}
22 | }
23 | }
24 |
25 | callees
26 | }
27 |
--------------------------------------------------------------------------------
/src/ast/function.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Error, Formatter};
2 |
3 | use crate::ast::expression::Expression;
4 |
5 | #[derive(Eq, PartialEq, Clone)]
6 | pub struct Function {
7 | pub interface: FunctionInterface,
8 | pub body: Option,
9 | }
10 |
11 | impl Display for Function {
12 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
13 | write!(fmt, "def {}", self.interface)?;
14 |
15 | if let Some(body) = &self.body {
16 | write!(fmt, " :: {}", body)?;
17 | }
18 | return Ok(())
19 | }
20 | }
21 |
22 | #[derive(Eq, PartialEq, Clone)]
23 | pub struct FunctionInterface {
24 | pub expression: Expression,
25 | pub return_type: Option,
26 | }
27 |
28 | impl Display for FunctionInterface {
29 | fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
30 | write!(fmt, "{}", &self.expression)?;
31 |
32 | if let Some(return_type) = &self.return_type {
33 | write!(fmt, " -> {}", return_type)?;
34 | }
35 |
36 | Ok(())
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/monoteny.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/ast/struct_.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Error, Formatter};
2 |
3 | use crate::ast::Expression;
4 | use crate::program::functions::ParameterKey;
5 | use crate::util::fmt::write_separated_display;
6 | use crate::util::position::Positioned;
7 |
8 | #[derive(Eq, PartialEq, Clone)]
9 | pub struct Struct { pub arguments: Vec>> }
10 |
11 | impl Struct {
12 | pub fn empty() -> Struct {
13 | Struct { arguments: vec![] }
14 | }
15 | }
16 |
17 | impl Display for Struct {
18 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
19 | write!(f, "(")?;
20 | write_separated_display(f, ", ", self.arguments.iter().map(|f| &f.value))?;
21 | write!(f, ")")
22 | }
23 | }
24 |
25 | #[derive(Eq, PartialEq, Clone)]
26 | pub struct StructArgument {
27 | pub key: ParameterKey,
28 | pub value: Expression,
29 | pub type_declaration: Option,
30 | }
31 |
32 | impl Display for StructArgument {
33 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
34 | write!(fmt, "{}{}", self.key, self.value)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/cli/logging.rs:
--------------------------------------------------------------------------------
1 | use std::process::ExitCode;
2 | use std::time::Instant;
3 |
4 | use colored::Colorize;
5 |
6 | use crate::error::{print_errors, RResult, RuntimeError};
7 |
8 | pub fn dump_start(name: &str) -> Instant {
9 | println!("{} {}", "Running".green().bold(), name);
10 | Instant::now()
11 | }
12 |
13 | pub fn dump_result(start: Instant, result: RResult) -> ExitCode {
14 | match result {
15 | Ok(_) => dump_success(start,),
16 | Err(e) => dump_failure(e),
17 | }
18 | }
19 |
20 | pub fn dump_named_failure(name: &str, err: Vec) -> ExitCode {
21 | print_errors(&err);
22 | println!("\n{} on {}: {} error(s)", "Failure".red().bold(), name, err.len());
23 | ExitCode::FAILURE
24 | }
25 |
26 | pub fn dump_failure(err: Vec) -> ExitCode {
27 | print_errors(&err);
28 | println!("\n{}: {} error(s)", "Failure".red().bold(), err.len());
29 | ExitCode::FAILURE
30 | }
31 |
32 | pub fn dump_success(start: Instant) -> ExitCode {
33 | println!("{} in {:.2}s", "Finished".green().bold(), start.elapsed().as_secs_f32());
34 | ExitCode::SUCCESS
35 | }
36 |
--------------------------------------------------------------------------------
/src/parser.rs:
--------------------------------------------------------------------------------
1 | use itertools::Itertools;
2 | use lalrpop_util::ErrorRecovery;
3 |
4 | use crate::error::RResult;
5 | use crate::{ast, monoteny_grammar};
6 |
7 | pub mod strings;
8 | pub mod lexer;
9 | pub mod error;
10 | pub mod grammar;
11 | pub mod expressions;
12 | mod tests;
13 |
14 | pub fn parse_program(content: &str) -> RResult<(ast::Block, Vec, error::Error>>)> {
15 | let lexer = lexer::Lexer::new(content);
16 | let mut errors = vec![];
17 | let ast = monoteny_grammar::FileParser::new()
18 | .parse(&mut errors, content, lexer)
19 | .map_err(|e| { error::map_parse_error(&e).to_array() })?;
20 |
21 | Ok((ast, errors))
22 | }
23 |
24 | pub fn parse_expression(content: &str) -> RResult<(ast::Expression, Vec, error::Error>>)> {
25 | let lexer = lexer::Lexer::new(content);
26 | let mut errors = vec![];
27 | let ast = monoteny_grammar::ExpressionParser::new()
28 | .parse(&mut errors, content, lexer)
29 | .map_err(|e| { error::map_parse_error(&e).to_array() })?;
30 |
31 | Ok((ast, errors))
32 | }
33 |
--------------------------------------------------------------------------------
/src/ast/array.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Error, Formatter};
2 |
3 | use crate::ast::Expression;
4 | use crate::util::fmt::write_separated_display;
5 | use crate::util::position::Positioned;
6 |
7 | #[derive(Eq, PartialEq, Clone)]
8 | pub struct Array { pub arguments: Vec>> }
9 |
10 | impl Array {
11 | pub fn empty() -> Array {
12 | Array { arguments: vec![] }
13 | }
14 | }
15 |
16 | impl Display for Array {
17 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
18 | write!(f, "[")?;
19 | write_separated_display(f, ", ", self.arguments.iter().map(|f| &f.value))?;
20 | write!(f, "]")
21 | }
22 | }
23 |
24 | #[derive(Eq, PartialEq, Clone)]
25 | pub struct ArrayArgument {
26 | pub key: Option,
27 | pub value: Expression,
28 | pub type_declaration: Option,
29 | }
30 |
31 | impl Display for ArrayArgument {
32 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
33 | if let Some(key) = &self.key {
34 | write!(fmt, "{}: ", key)?;
35 | }
36 | write!(fmt, "{}", self.value)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/interpreter/data_layout.rs:
--------------------------------------------------------------------------------
1 | use std::rc::Rc;
2 |
3 | use itertools::Itertools;
4 |
5 | use crate::program::allocation::ObjectReference;
6 | use crate::program::traits::StructInfo;
7 | use crate::program::types::TypeUnit;
8 |
9 | pub struct DataLayout {
10 | pub struct_info: Rc,
11 | pub fields: Vec>,
12 | }
13 |
14 | pub fn create_data_layout(struct_info: Rc) -> Rc {
15 | let mut fields = vec![];
16 | let mut todo = struct_info.fields.iter().rev().collect_vec();
17 |
18 | while let Some(next) = todo.pop() {
19 | match &next.type_.unit {
20 | TypeUnit::Void => unreachable!(),
21 | TypeUnit::Generic(_) => todo!(),
22 | TypeUnit::Struct(s) => {
23 | // TODO In the future, we probably want to merge nested structs into each
24 | // other to avoid indirection. For now, it's safer and easier to just accept the
25 | // indirection though.
26 | fields.push(Rc::clone(next));
27 | }
28 | }
29 | }
30 |
31 | Rc::new(DataLayout {
32 | struct_info,
33 | fields
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/src/cli.rs:
--------------------------------------------------------------------------------
1 | use crate::cli::logging::dump_failure;
2 | use clap::Command;
3 | use std::process::ExitCode;
4 |
5 | pub mod run;
6 | pub mod expression;
7 | pub mod check;
8 | pub mod transpile;
9 | pub mod logging;
10 |
11 | pub fn make_command() -> Command {
12 | Command::new("monoteny")
13 | .about("A cli implementation for the monoteny language.")
14 | .subcommand_required(true)
15 | .arg_required_else_help(true)
16 | .allow_external_subcommands(true)
17 | .subcommand(run::make_command())
18 | .subcommand(expression::make_command())
19 | .subcommand(check::make_command())
20 | .subcommand(transpile::make_command())
21 | }
22 |
23 | pub fn run_command() -> ExitCode {
24 | let matches = make_command().get_matches();
25 |
26 | let result = match matches.subcommand() {
27 | Some(("run", sub_matches)) => run::run(sub_matches),
28 | Some(("expression", sub_matches)) => expression::run(sub_matches),
29 | Some(("check", sub_matches)) => check::run(sub_matches),
30 | Some(("transpile", sub_matches)) => transpile::run(sub_matches),
31 | _ => panic!("Unsupported action."),
32 | };
33 |
34 | result.unwrap_or_else(|e| dump_failure(e))
35 | }
36 |
--------------------------------------------------------------------------------
/src/program/functions/overload.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashSet;
2 | use std::rc::Rc;
3 |
4 | use crate::error::RResult;
5 | use crate::program::functions::{FunctionHead, FunctionRepresentation};
6 |
7 | /// Reference to a multiplicity of functions, usually resolved when attempting to call
8 | #[derive(Clone, PartialEq, Eq)]
9 | pub struct FunctionOverload {
10 | pub functions: HashSet>,
11 | // Note: If representation is NOT an implicit, the functions within are getters.
12 | pub representation: FunctionRepresentation,
13 | }
14 |
15 | impl FunctionOverload {
16 | pub fn from(function: &Rc, representation: FunctionRepresentation) -> Rc {
17 | Rc::new(FunctionOverload {
18 | functions: HashSet::from([Rc::clone(function)]),
19 | representation,
20 | })
21 | }
22 |
23 | pub fn adding_function(&self, function: &Rc) -> RResult> {
24 | Ok(Rc::new(FunctionOverload {
25 | functions: self.functions.iter()
26 | .chain([function])
27 | .map(Rc::clone)
28 | .collect(),
29 | representation: self.representation.clone(),
30 | }))
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/program/primitives.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::Debug;
2 |
3 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
4 | pub enum Type {
5 | Bool,
6 | Int(usize),
7 | UInt(usize),
8 | Float(usize),
9 | }
10 |
11 | impl Type {
12 | pub fn is_number(&self) -> bool {
13 | match self {
14 | Type::Bool => false,
15 | _ => true,
16 | }
17 | }
18 |
19 | pub fn is_float(&self) -> bool {
20 | match self {
21 | Type::Float(_) => true,
22 | _ => false,
23 | }
24 | }
25 |
26 | pub fn is_int(&self) -> bool {
27 | match self {
28 | Type::Bool => false,
29 | Type::Float(_) => false,
30 | _ => true,
31 | }
32 | }
33 |
34 | pub fn is_signed_number(&self) -> bool {
35 | match self {
36 | Type::Bool => false,
37 | Type::UInt(_) => false,
38 | _ => true,
39 | }
40 | }
41 |
42 | pub fn identifier_string(&self) -> String {
43 | match self {
44 | Type::Bool => "Bool".to_string(),
45 | Type::Int(bits) => format!("Int{}", bits),
46 | Type::UInt(bits) => format!("UInt{}", bits),
47 | Type::Float(bits) => format!("Float{}", bits),
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/ast/expression.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Formatter};
2 | use std::ops::{Deref, DerefMut};
3 |
4 | use crate::ast::term::Term;
5 | use crate::error::{RResult, TryCollectMany};
6 | use crate::util::fmt::write_separated_display;
7 | use crate::util::position::Positioned;
8 |
9 | #[derive(Eq, PartialEq, Clone)]
10 | pub struct Expression(Vec>>);
11 |
12 | impl Expression {
13 | pub fn no_errors(&self) -> RResult<()> {
14 | self.iter()
15 | .map(|t| match &t.value {
16 | Term::Error(e) => Err(e.clone().to_array()),
17 | _ => Ok(())
18 | })
19 | .try_collect_many()
20 | }
21 | }
22 |
23 | impl Deref for Expression {
24 | type Target = Vec>>;
25 |
26 | fn deref(&self) -> &Self::Target {
27 | &self.0
28 | }
29 | }
30 |
31 | impl DerefMut for Expression {
32 | fn deref_mut(&mut self) -> &mut Self::Target {
33 | &mut self.0
34 | }
35 | }
36 |
37 | impl From>>> for Expression {
38 | fn from(value: Vec>>) -> Self {
39 | Expression(value)
40 | }
41 | }
42 |
43 | impl Display for Expression {
44 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45 | write_separated_display(f, " ", self.0.iter().map(|b| &b.value))
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/program/debug.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Formatter};
2 |
3 | use display_with_options::with_options;
4 | use itertools::Itertools;
5 | use uuid::Uuid;
6 |
7 | use crate::program::expression_tree::ExpressionID;
8 | use crate::program::functions::{FunctionInterface, FunctionRepresentation, Parameter, ParameterKey};
9 | use crate::program::generics::TypeForest;
10 | use crate::program::types::{TypeProto, TypeUnit};
11 |
12 | pub struct MockFunctionInterface<'a> {
13 | pub representation: FunctionRepresentation,
14 | pub argument_keys: Vec,
15 | pub arguments: Vec,
16 | pub types: &'a TypeForest,
17 | }
18 |
19 | impl<'a> Display for MockFunctionInterface<'a> {
20 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
21 | let signature = FunctionInterface {
22 | parameters: self.argument_keys.iter().zip(&self.arguments).map(|(key, expression_id)| Parameter {
23 | external_key: (*key).clone(),
24 | type_: self.types.prototype_binding_alias(expression_id),
25 | }).collect_vec(),
26 | return_type: TypeProto::unit(TypeUnit::Generic(Uuid::new_v4())),
27 | requirements: Default::default(),
28 | generics: Default::default(),
29 | };
30 | write!(f, "{:?}", with_options(&signature, &self.representation))
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/resolver/interpreter_mock.rs:
--------------------------------------------------------------------------------
1 | use itertools::Itertools;
2 |
3 | use crate::ast;
4 | use crate::error::{ErrInRange, RResult, RuntimeError};
5 | use crate::program::functions::ParameterKey;
6 | use crate::util::position::Positioned;
7 |
8 | pub fn plain_parameter<'a>(cause: &str, struct_: &'a ast::Struct) -> RResult<&'a ast::Expression> {
9 | let body = struct_.arguments.iter().exactly_one()
10 | .map_err(|_| RuntimeError::error(format!("{} needs exactly one parameter.", cause).as_str()).to_array())?;
11 |
12 | if body.value.key != ParameterKey::Positional {
13 | return Err(RuntimeError::error(format!("{} needs exactly one parameter.", cause).as_str()).to_array());
14 | }
15 |
16 | if body.value.type_declaration.is_some() {
17 | return Err(RuntimeError::error(format!("{} needs exactly one parameter.", cause).as_str()).to_array());
18 | }
19 |
20 | Ok(&body.value.value)
21 | }
22 |
23 | pub fn plain_string_literal<'a>(cause: &str, literal: &'a Vec>>) -> RResult<&'a str> {
24 | let [part] = &literal[..] else {
25 | return Err(RuntimeError::error(format!("{} needs a plain string literal.", cause).as_str()).to_array());
26 | };
27 |
28 | let ast::StringPart::Literal(literal) = &part.value else {
29 | return Err(RuntimeError::error(format!("{} needs a plain string literal.", cause).as_str()).to_array()).err_in_range(&part.position);
30 | };
31 |
32 | Ok(literal)
33 | }
34 |
--------------------------------------------------------------------------------
/src/cli/check.rs:
--------------------------------------------------------------------------------
1 | use std::path::PathBuf;
2 | use std::process::ExitCode;
3 |
4 | use crate::cli::logging::{dump_named_failure, dump_start, dump_success};
5 | use clap::{arg, ArgMatches, Command};
6 |
7 | use crate::error::RResult;
8 | use crate::interpreter::runtime::Runtime;
9 | use crate::program::module::module_name;
10 |
11 | pub fn make_command() -> Command {
12 | Command::new("check")
13 | .about("Parse files to check for validity.")
14 | .arg_required_else_help(true)
15 | .arg(arg!( ... "files to check").value_parser(clap::value_parser!(PathBuf)))
16 | }
17 |
18 | pub fn run(args: &ArgMatches) -> RResult {
19 | let paths = args
20 | .get_many::("PATH")
21 | .into_iter()
22 | .flatten()
23 | .collect::>();
24 |
25 | let start = dump_start(format!("check for {} file(s)", paths.len()).as_str());
26 |
27 | let mut runtime = Runtime::new()?;
28 | runtime.add_common_repository();
29 |
30 | let mut error_count = 0;
31 | for path in paths {
32 | match runtime.load_file_as_module(path, module_name("main")) {
33 | Ok(_) => {},
34 | Err(e) => {
35 | dump_named_failure(format!("import({})", path.as_os_str().to_string_lossy()).as_str(), e);
36 | error_count += 1;
37 | },
38 | };
39 | }
40 |
41 | if error_count == 0 {
42 | dump_success(start);
43 | }
44 |
45 | Ok(ExitCode::from(error_count))
46 | }
47 |
--------------------------------------------------------------------------------
/src/program/expression_tree.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::Debug;
2 | use std::rc::Rc;
3 |
4 | use crate::program::allocation::ObjectReference;
5 | use crate::program::functions::FunctionBinding;
6 | use crate::program::generics::GenericAlias;
7 | use crate::util::graphs::node_tree::NodeTree;
8 |
9 | pub type ExpressionID = GenericAlias;
10 |
11 | #[derive(Clone, PartialEq, Eq, Debug)]
12 | pub enum ExpressionOperation {
13 | // TODO Blocks are a tough one to transpile as no language supports yields.
14 | // They will probably have to be inlined as a variable, like e.g.:
15 | // var x: Int;
16 | // for i in 0 ..< 1 {
17 | // ...
18 | // // yield 5;
19 | // x = 5;
20 | // break;
21 | // }
22 | // This syntax, while stupid, is at least supported in pretty much every language.
23 | Block,
24 | IfThenElse,
25 |
26 | GetLocal(Rc),
27 | SetLocal(Rc),
28 |
29 | // 0 arguments if no return type is set, otherwise 1
30 | Return,
31 |
32 | FunctionCall(Rc),
33 | PairwiseOperations { calls: Vec> },
34 |
35 | // TODO This is required because it has a variable number of arguments (its elements).
36 | // This is not supported in functions otherwise, and we'd have to make an exception.
37 | // Which might be fair in the future, but for now it's not a pressing concern.
38 | ArrayLiteral,
39 | StringLiteral(String),
40 | }
41 |
42 | pub type ExpressionTree = NodeTree;
43 |
--------------------------------------------------------------------------------
/src/interpreter/builtins/modules.rs:
--------------------------------------------------------------------------------
1 | use crate::repository;
2 | use std::collections::HashMap;
3 |
4 | pub fn create_core_loader() -> repository::Loader {
5 | repository::Loader::Intrinsic(
6 | HashMap::from([
7 | (vec!["core".to_string()], include_str!("../../../monoteny/core.monoteny")),
8 | (vec!["core".to_string(), "bool".to_string()], include_str!("../../../monoteny/core/bool.monoteny")),
9 | (vec!["core".to_string(), "debug".to_string()], include_str!("../../../monoteny/core/debug.monoteny")),
10 | (vec!["core".to_string(), "run".to_string()], include_str!("../../../monoteny/core/run.monoteny")),
11 | (vec!["core".to_string(), "strings".to_string()], include_str!("../../../monoteny/core/strings.monoteny")),
12 | (vec!["core".to_string(), "transpilation".to_string()], include_str!("../../../monoteny/core/transpilation.monoteny")),
13 | ]),
14 | )
15 | }
16 |
17 | pub fn create_common_loader() -> repository::Loader {
18 | repository::Loader::Intrinsic(
19 | HashMap::from([
20 | (vec!["common".to_string()], include_str!("../../../monoteny/common.monoteny")),
21 | (vec!["common".to_string(), "debug".to_string()], include_str!("../../../monoteny/common/debug.monoteny")),
22 | (vec!["common".to_string(), "math".to_string()], include_str!("../../../monoteny/common/math.monoteny")),
23 | (vec!["common".to_string(), "precedence".to_string()], include_str!("../../../monoteny/common/precedence.monoteny")),
24 | ]),
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/src/util/fmt.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use std::fmt::{Debug, Display, Error, Formatter};
3 |
4 | pub fn fmta std::fmt::Result>(fun: F) -> String {
5 | struct Mock std::fmt::Result> {
6 | fun: F,
7 | }
8 |
9 | impl std::fmt::Result> Display for Mock {
10 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
11 | (&self.fun)(f)
12 | }
13 | }
14 |
15 | format!("{}", Mock { fun })
16 | }
17 |
18 | pub fn write_separated_display(fmt: &mut Formatter, separator: &str, mut list: impl Iterator
- ) -> Result<(), Error> where E: Display {
19 | if let Some(first) = list.next() {
20 | write!(fmt, "{}", first)?
21 | }
22 | for item in list { write!(fmt, "{}{}", separator, item)? }
23 | Ok(())
24 | }
25 |
26 | pub fn write_separated_debug(fmt: &mut Formatter, separator: &str, mut list: impl Iterator
- ) -> Result<(), Error> where E: Debug {
27 | if let Some(first) = list.next() {
28 | write!(fmt, "{:?}", first)?
29 | }
30 | for item in list { write!(fmt, "{}{:?}", separator, item)? }
31 | Ok(())
32 | }
33 |
34 | pub fn write_keyval(fmt: &mut Formatter, mapping: &HashMap) -> Result<(), Error> where K: Debug, V: Debug {
35 | let mut iterator = mapping.iter();
36 |
37 | if let Some((key, val)) = iterator.next() {
38 | write!(fmt, "{:?}: {:?}", key, val)?
39 | }
40 | for (key, val) in iterator.skip(1) {
41 | write!(fmt, ", {:?}: {:?}", key, val)?
42 | }
43 |
44 | Ok(())
45 | }
46 |
--------------------------------------------------------------------------------
/src/program/allocation.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Debug, Formatter};
2 | use std::hash::{Hash, Hasher};
3 | use std::rc::Rc;
4 |
5 | use uuid::Uuid;
6 |
7 | use crate::program::types::TypeProto;
8 |
9 | #[derive(Copy, Clone, PartialEq, Eq)]
10 | pub enum Mutability {
11 | Immutable,
12 | Mutable,
13 | }
14 |
15 | impl Mutability {
16 | pub fn variable_declaration_keyword(&self) -> &str {
17 | match *self {
18 | Mutability::Mutable => "var",
19 | Mutability::Immutable => "let",
20 | }
21 | }
22 | }
23 |
24 | #[derive(Clone, Eq)]
25 | pub struct ObjectReference {
26 | pub id: Uuid,
27 | pub type_: Rc,
28 | pub mutability: Mutability,
29 | }
30 |
31 | impl ObjectReference {
32 | pub fn new_immutable(type_: Rc) -> Rc {
33 | Rc::new(ObjectReference {
34 | id: Uuid::new_v4(),
35 | type_,
36 | mutability: Mutability::Immutable
37 | })
38 | }
39 | }
40 |
41 | impl PartialEq for ObjectReference {
42 | fn eq(&self, other: &Self) -> bool {
43 | self.id == other.id
44 | }
45 | }
46 |
47 | impl Hash for ObjectReference {
48 | fn hash(&self, state: &mut H) {
49 | self.id.hash(state);
50 | }
51 | }
52 |
53 | impl Debug for ObjectReference {
54 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
55 | let mut_keyword = match self.mutability {
56 | Mutability::Immutable => "let",
57 | Mutability::Mutable => "var",
58 | };
59 | write!(f, "{} <{}> '{:?}", mut_keyword, self.id, self.type_)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/util/graphs/change_graph.rs:
--------------------------------------------------------------------------------
1 | use std::collections::{HashMap, HashSet};
2 | use std::hash::Hash;
3 |
4 | use linked_hash_set::LinkedHashSet;
5 |
6 | use crate::util::multimap::insert_into_multimap;
7 |
8 | pub struct ChangeGraph {
9 | pub next: LinkedHashSet,
10 | pub dependents: HashMap>,
11 | }
12 |
13 | impl ChangeGraph {
14 | pub fn new() -> ChangeGraph {
15 | ChangeGraph {
16 | next: LinkedHashSet::new(),
17 | dependents: Default::default(),
18 | }
19 | }
20 |
21 | pub fn pop(&mut self) -> Option {
22 | self.next.pop_front()
23 | }
24 |
25 | pub fn mark_change(&mut self, object: I) {
26 | if self.next.insert(object.clone()) {
27 | // Could use a LinkedHashSet here too, but that's not really worth it speed wise.
28 | let mut next = vec![object];
29 |
30 | while let Some(object) = next.pop() {
31 | if self.next.insert(object.clone()) {
32 | next.push(object);
33 | }
34 | }
35 | }
36 | }
37 |
38 | pub fn remove_dependency(&mut self, dependent: &I, dependency: &I) {
39 | self.dependents.get_mut(dependency).map(|s| s.remove(dependent));
40 | }
41 |
42 | pub fn add_dependency(&mut self, dependent: I, dependency: I) {
43 | insert_into_multimap(&mut self.dependents, dependent, dependency);
44 | }
45 |
46 | pub fn add_dependencies(&mut self, dependent: &I, dependencies: impl Iterator
- ) {
47 | for dependency in dependencies {
48 | self.add_dependency(dependent.clone(), dependency)
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/ast/decorated.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Formatter};
2 |
3 | use crate::ast::{Array, Expression};
4 | use crate::error::{RResult, RuntimeError, TryCollectMany};
5 |
6 | #[derive(PartialEq, Eq, Clone)]
7 | pub struct Decorated {
8 | pub decorations: Array,
9 | pub value: T,
10 | }
11 |
12 | impl Decorated {
13 | pub fn with_value(&self, n: N) -> Decorated {
14 | Decorated {
15 | decorations: self.decorations.clone(),
16 | value: n,
17 | }
18 | }
19 |
20 | pub fn undecorated(t: V) -> Decorated {
21 | Decorated {
22 | decorations: Array { arguments: vec![] },
23 | value: t,
24 | }
25 | }
26 |
27 | pub fn decorations_as_vec(&self) -> RResult> {
28 | return self.decorations.arguments.iter().map(|d| {
29 | if d.value.key.is_some() {
30 | return Err(RuntimeError::error("Decorations cannot have keys.").to_array())
31 | }
32 | if d.value.type_declaration.is_some() {
33 | return Err(RuntimeError::error("Decorations cannot have type declarations.").to_array())
34 | }
35 |
36 | Ok(&d.value.value)
37 | }).try_collect_many()
38 | }
39 |
40 | pub fn no_decorations(&self) -> RResult<()> {
41 | if !self.decorations.arguments.is_empty() {
42 | return Err(RuntimeError::error("Decorations are not supported in this context.").to_array())
43 | }
44 |
45 | return Ok(())
46 | }
47 | }
48 |
49 | impl Display for Decorated {
50 | fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
51 | if self.decorations.arguments.is_empty() {
52 | return write!(fmt, "{}", self.value)
53 | }
54 | write!(fmt, "!{}\n{}", self.decorations, self.value)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/source.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use std::rc::Rc;
3 |
4 | use uuid::Uuid;
5 |
6 | use crate::program::functions::{FunctionHead, FunctionLogic};
7 | use crate::program::module::{Module, ModuleName};
8 | use crate::program::traits::{StructInfo, Trait};
9 |
10 | pub struct Source {
11 | pub module_by_name: HashMap>,
12 |
13 | // Cache of aggregated module_by_name fields for quick reference.
14 |
15 | /// For every getter, which trait it provides.
16 | pub trait_heads: HashMap>,
17 | /// For referencible functions, the trait for it as an object.
18 | /// For every getter, which trait it provides.
19 | pub trait_references: HashMap, Rc>,
20 | /// For referencible functions, the trait for it as an object.
21 | pub function_traits: HashMap, Rc>,
22 | /// For instantiatable traits, their struct info
23 | pub struct_by_trait: HashMap, Rc>,
24 |
25 | /// For each function_id, its head.
26 | pub fn_heads: HashMap>,
27 | /// For referencible functions, a way to load it. The getter itself does not get a getter.
28 | pub fn_getters: HashMap, Rc>,
29 |
30 | /// For all functions, their logic.
31 | pub fn_logic: HashMap, FunctionLogic>,
32 | }
33 |
34 | impl Source {
35 | pub fn new() -> Source {
36 | Source {
37 | module_by_name: Default::default(),
38 | trait_heads: Default::default(),
39 | trait_references: Default::default(),
40 | function_traits: Default::default(),
41 | struct_by_trait: Default::default(),
42 | fn_heads: Default::default(),
43 | fn_getters: Default::default(),
44 | fn_logic: Default::default(),
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/transpiler/python/class.rs:
--------------------------------------------------------------------------------
1 | use std::collections::{HashMap, HashSet};
2 | use std::rc::Rc;
3 |
4 | use uuid::Uuid;
5 |
6 | use crate::program::types::{TypeProto, TypeUnit};
7 | use crate::transpiler::python::ast;
8 | use crate::transpiler::python::ast::Block;
9 | use crate::transpiler::python::representations::Representations;
10 |
11 | pub struct ClassContext<'a> {
12 | pub names: &'a HashMap,
13 | pub representations: &'a Representations,
14 | pub unestablished_structs: &'a HashSet>,
15 | }
16 |
17 | pub fn transpile_class(type_def: &TypeProto, context: &ClassContext) -> Box {
18 | // TODO If the type has no variables, we can fold it away from the program entirely
19 | let struct_id = context.representations.type_ids[type_def];
20 | let mut statements = vec![];
21 |
22 | // TODO Need to account for bindings
23 | match &type_def.unit {
24 | TypeUnit::Struct(struct_) => {
25 | for hint in &struct_.field_hints {
26 | let is_established = !context.unestablished_structs.contains(&hint.type_);
27 | let type_string = context.names[&context.representations.type_ids[&hint.type_]].clone();
28 |
29 | statements.push(Box::new(ast::Statement::VariableAssignment {
30 | target: Box::new(ast::Expression::NamedReference(hint.name.clone())),
31 | value: None,
32 | type_annotation: Some(Box::new(match is_established {
33 | true => ast::Expression::NamedReference(type_string),
34 | false => ast::Expression::StringLiteral(type_string),
35 | })),
36 | }))
37 | }
38 | }
39 | _ => panic!()
40 | }
41 |
42 | Box::new(ast::Class {
43 | name: context.names[&struct_id].clone(),
44 | block: Block { statements },
45 | })
46 | }
47 |
--------------------------------------------------------------------------------
/src/parser/tests.rs:
--------------------------------------------------------------------------------
1 | #[cfg(test)]
2 | mod tests {
3 | use std::fs;
4 |
5 | use itertools::Itertools;
6 |
7 | use crate::error::RResult;
8 | use crate::parser;
9 | use crate::parser::ast::*;
10 |
11 | #[test]
12 | fn hello_world() -> RResult<()> {
13 | let file_contents = fs::read_to_string("test-code/hello_world.monoteny").unwrap();
14 | let (parsed, errors) = parser::parse_program(
15 | file_contents.as_str()
16 | )?;
17 | assert!(errors.is_empty());
18 |
19 | assert_eq!(parsed.statements.len(), 3);
20 |
21 | let Statement::FunctionDeclaration(function) = &parsed.statements[1].as_ref().value.value else {
22 | panic!();
23 | };
24 |
25 | assert!(function.interface.return_type.is_none());
26 | assert_eq!(function.interface.expression.len(), 1);
27 | assert!(function.interface.expression[0].value == Term::MacroIdentifier("main".to_string()));
28 |
29 | Ok(())
30 | }
31 |
32 | #[test]
33 | fn custom_grammar() -> RResult<()> {
34 | let file_contents = fs::read_to_string("test-code/grammar/custom_grammar.monoteny").unwrap();
35 | let (parsed, errors) = parser::parse_program(file_contents.as_str())?;
36 | assert!(errors.is_empty());
37 |
38 | assert_eq!(parsed.statements.len(), 6);
39 |
40 | let Statement::FunctionDeclaration(floor_div) = &parsed.statements[2].as_ref().value.value else {
41 | panic!();
42 | };
43 |
44 | match floor_div.interface.expression.iter().map(|t| &t.value).collect_vec()[..] {
45 | [Term::Identifier(i), Term::Struct(s)] => {
46 | assert_eq!(i, "_add");
47 | assert_eq!(s.arguments.len(), 2);
48 | }
49 | _ => panic!()
50 | }
51 | assert_eq!(parsed.statements[1].decorations.arguments.len(), 1);
52 |
53 | Ok(())
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | workflow_call:
5 | workflow_dispatch:
6 |
7 | env:
8 | CARGO_TERM_COLOR: always
9 |
10 | jobs:
11 | build:
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | target:
16 | # TODO Fails due to linker error
17 | # - { name: "linux-x86", target: "i686-unknown-linux-gnu", executable-name: "monoteny", os: ubuntu-latest }
18 | - { name: "linux-x86_64", target: "x86_64-unknown-linux-gnu", executable-name: "monoteny", os: ubuntu-latest, }
19 | # TODO Fails due to linker error
20 | # - { name: "linux-aarch64", target: "aarch64-unknown-linux-gnu", executable-name: "monoteny", os: ubuntu-latest, }
21 | - { name: "windows-x86", target: "i686-pc-windows-msvc", os: windows-latest, executable-name: "monoteny.exe" }
22 | - { name: "windows-x86_64", target: "x86_64-pc-windows-msvc", os: windows-latest, executable-name: "monoteny.exe" }
23 | - { name: "macos-x86", target: "x86_64-apple-darwin", executable-name: "monoteny", os: macos-latest }
24 | - { name: "macos-aarch64", target: "aarch64-apple-darwin", executable-name: "monoteny", os: macos-latest }
25 |
26 | runs-on: ${{ matrix.target.os }}
27 |
28 | steps:
29 | - uses: actions/checkout@v4
30 | - run: rustup toolchain install stable --profile minimal && rustup target add ${{ matrix.target.target }}
31 | - uses: Swatinem/rust-cache@v2
32 | with:
33 | prefix-key: "main"
34 | save-if: ${{ github.ref == 'refs/heads/main' }}
35 | - name: Build
36 | run: cargo build --release --verbose --target ${{ matrix.target.target }}
37 | - name: Upload Artifact
38 | uses: actions/upload-artifact@v4
39 | with:
40 | name: Monoteny-${{ matrix.target.name }}
41 | path: |
42 | ${{ github.workspace }}/target/${{ matrix.target.target }}/release/${{ matrix.target.executable-name }}
43 |
--------------------------------------------------------------------------------
/src/transpiler/structs.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use std::rc::Rc;
3 |
4 | use linked_hash_map::{Entry, LinkedHashMap};
5 |
6 | use crate::program::expression_tree::ExpressionOperation;
7 | use crate::program::functions::{FunctionHead, FunctionImplementation, FunctionLogicDescriptor};
8 | use crate::program::traits::StructInfo;
9 | use crate::program::types::TypeProto;
10 |
11 | pub fn find_in_interfaces(heads: impl Iterator
- >, map: &mut LinkedHashMap, Rc>) {
12 | for head in heads {
13 | for type_ in head.interface.parameters.iter().map(|p| &p.type_).chain([&head.interface.return_type].into_iter()) {
14 | todo!("From the type we SHOULD be able to deduce the struct info, but we can't for now.")
15 | }
16 | }
17 | }
18 |
19 | pub fn find_in_implementations<'a>(implementations: impl Iterator
- , logic: &HashMap, FunctionLogicDescriptor>, map: &mut LinkedHashMap, Rc>) {
20 | for implementation in implementations {
21 | for expression_id in implementation.expression_tree.deep_children(implementation.expression_tree.root) {
22 | let operation = &implementation.expression_tree.values[&expression_id];
23 |
24 | if let ExpressionOperation::FunctionCall(binding) = operation {
25 | let Some(FunctionLogicDescriptor::Constructor(struct_info)) = logic.get(&binding.function) else {
26 | continue;
27 | };
28 |
29 | let type_ = &binding.function.interface.return_type; // Fulfillment for Self
30 | if let Entry::Vacant(entry) = map.entry(type_.clone()) {
31 | // If it's already present and we insert, we shuffle it to the end, which is unnecessary
32 | entry.insert(Rc::clone(struct_info));
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/program/functions/function_binding.rs:
--------------------------------------------------------------------------------
1 | use std::rc::Rc;
2 |
3 | use crate::program::functions::FunctionHead;
4 | use crate::program::generics::TypeForest;
5 | use crate::program::traits::{RequirementsFulfillment, TraitConformance, TraitConformanceWithTail};
6 |
7 | #[derive(Clone, Eq, PartialEq, Hash, Debug)]
8 | pub struct FunctionBinding {
9 | pub function: Rc,
10 | pub requirements_fulfillment: Rc,
11 | }
12 |
13 | impl FunctionBinding {
14 | pub fn pure(function: Rc) -> Rc {
15 | Rc::new(FunctionBinding {
16 | function,
17 | requirements_fulfillment: RequirementsFulfillment::empty(),
18 | })
19 | }
20 | }
21 |
22 | pub fn resolve_binding(binding: &FunctionBinding, type_forest: &TypeForest) -> Rc {
23 | Rc::new(FunctionBinding {
24 | function: Rc::clone(&binding.function),
25 | requirements_fulfillment: resolve_fulfillment(&binding.requirements_fulfillment, type_forest),
26 | })
27 | }
28 |
29 | pub fn resolve_fulfillment(fulfillment: &RequirementsFulfillment, type_forest: &TypeForest) -> Rc {
30 | Rc::new(RequirementsFulfillment {
31 | conformance: fulfillment.conformance.iter().map(|(b, f)| {
32 | let binding = b.mapping_types(&|t| type_forest.resolve_type(t).unwrap());
33 | (Rc::clone(&binding), Rc::new(TraitConformanceWithTail {
34 | conformance: Rc::new(TraitConformance {
35 | binding,
36 | function_mapping: f.conformance.function_mapping.clone(),
37 | }),
38 | tail: resolve_fulfillment(&f.tail, type_forest),
39 | }))
40 | }).collect(),
41 | generic_mapping: fulfillment.generic_mapping.iter()
42 | .map(|(generic, type_)| (Rc::clone(generic), type_forest.resolve_type(type_).unwrap()))
43 | .collect(),
44 | })
45 | }
46 |
--------------------------------------------------------------------------------
/src/ast/term.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Error, Formatter};
2 |
3 | use crate::ast::{Array, Block, Expression, StringPart, Struct};
4 | use crate::error::RuntimeError;
5 | use crate::util::position::Positioned;
6 |
7 | #[derive(Eq, PartialEq, Clone)]
8 | pub enum Term {
9 | Error(RuntimeError),
10 | Identifier(String),
11 | MacroIdentifier(String),
12 | Dot,
13 | IntLiteral(String),
14 | RealLiteral(String),
15 | Struct(Box),
16 | Array(Box),
17 | StringLiteral(Vec>>),
18 | Block(Box),
19 | IfThenElse(Box),
20 | }
21 |
22 | impl Display for Term {
23 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
24 | match self {
25 | Term::Error(err) => write!(fmt, "ERR"),
26 | Term::Identifier(s) => write!(fmt, "{}", s),
27 | Term::MacroIdentifier(s) => write!(fmt, "{}!", s),
28 | Term::IntLiteral(s) => write!(fmt, "{}", s),
29 | Term::RealLiteral(s) => write!(fmt, "{}", s),
30 | Term::StringLiteral(parts) => {
31 | write!(fmt, "\"")?;
32 | for part in parts {
33 | write!(fmt, "{}", part)?;
34 | }
35 | write!(fmt, "\"")
36 | },
37 | Term::Struct(struct_) => write!(fmt, "{}", struct_),
38 | Term::Array(array) => write!(fmt, "{}", array),
39 | Term::Block(block) => write!(fmt, "{{\n{}}}", block),
40 | Term::Dot => write!(fmt, "."),
41 | Term::IfThenElse(if_then_else) => {
42 | write!(fmt, "if {} :: {}", if_then_else.condition, if_then_else.consequent)?;
43 | if let Some(alternative) = &if_then_else.alternative {
44 | write!(fmt, "else :: {}", alternative)?;
45 | }
46 | Ok(())
47 | }
48 | }
49 | }
50 | }
51 |
52 | #[derive(Eq, PartialEq, Clone)]
53 | pub struct IfThenElse {
54 | pub condition: Expression,
55 | pub consequent: Expression,
56 | pub alternative: Option,
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/src/parser/error.rs:
--------------------------------------------------------------------------------
1 | use itertools::Itertools;
2 | use lalrpop_util::{ErrorRecovery, ParseError};
3 | use std::fmt::{Display, Formatter};
4 |
5 | use crate::error::RuntimeError;
6 | use crate::parser::lexer::Token;
7 | use crate::util::position::Positioned;
8 |
9 | #[derive(Debug, PartialEq, Clone)]
10 | pub struct Error(pub String);
11 |
12 | pub fn derive_error(error: &Positioned, Error>>) -> RuntimeError {
13 | map_parse_error(&error.value.error)
14 | .in_range(error.position.clone())
15 | }
16 |
17 | pub fn map_parse_error(e: &ParseError) -> RuntimeError {
18 | match e {
19 | ParseError::InvalidToken { location } => {
20 | RuntimeError::error("Invalid token.").in_range(*location..*location)
21 | },
22 | ParseError::UnrecognizedEof { location, expected } => {
23 | add_expected_note(RuntimeError::error("File ended unexpectedly.").in_range(*location..*location), expected)
24 | }
25 | ParseError::UnrecognizedToken { token: (start, token, end), expected } => {
26 | add_expected_note(RuntimeError::error("Unexpected token.").in_range(*start..*end), expected)
27 | }
28 | ParseError::ExtraToken { token: (start, token, end) } => {
29 | RuntimeError::error("Extraneous token.").in_range(*start..*end)
30 | }
31 | ParseError::User { error } => {
32 | panic!()
33 | }
34 | }
35 | }
36 |
37 | fn unquote(value: &str) -> &str {
38 | if !value.starts_with('\"') {
39 | return value
40 | }
41 |
42 | let mut chars = value.chars();
43 | chars.next();
44 | chars.next_back();
45 | chars.as_str()
46 | }
47 |
48 | fn add_expected_note(error: RuntimeError, expected: &Vec) -> RuntimeError {
49 | match &expected[..] {
50 | [] => error,
51 | [one] => error.with_note(RuntimeError::note(format!("Expected: {}", unquote(one)).as_str())),
52 | expected => error.with_note(RuntimeError::note(format!("Expected one of: {}", expected.iter().map(|s| unquote(s)).join(" ")).as_str())),
53 | }
54 | }
55 |
56 | impl Display for Error {
57 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
58 | write!(f, "{}", self.0)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/ast/statement.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::{Display, Error, Formatter};
2 |
3 | use crate::ast::conformance::TraitConformanceDeclaration;
4 | use crate::ast::expression::Expression;
5 | use crate::ast::function::Function;
6 | use crate::ast::trait_::TraitDefinition;
7 | use crate::program::allocation::Mutability;
8 |
9 | #[derive(Eq, PartialEq, Clone)]
10 | pub enum Statement {
11 | VariableDeclaration {
12 | mutability: Mutability,
13 | identifier: String,
14 | type_declaration: Option>,
15 | assignment: Option>
16 | },
17 | VariableUpdate { target: Box, new_value: Box },
18 | Expression(Box),
19 | Return(Option>),
20 | FunctionDeclaration(Box),
21 | Trait(Box),
22 | Conformance(Box),
23 | }
24 |
25 | impl Display for Statement {
26 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
27 | match self {
28 | Statement::VariableDeclaration { mutability, identifier, type_declaration, assignment} => {
29 | let mutability_string = mutability.variable_declaration_keyword();
30 | write!(fmt, "{} {}", mutability_string, identifier)?;
31 | if let Some(type_declaration) = type_declaration {
32 | write!(fmt, " '{}", type_declaration)?;
33 | }
34 | if let Some(assignment) = assignment {
35 | write!(fmt, " = {}", assignment)?;
36 | }
37 | Ok(())
38 | },
39 | Statement::VariableUpdate { target, new_value } => {
40 | write!(fmt, "upd {} = {}", target, new_value)
41 | },
42 | Statement::Return(Some(expression)) => write!(fmt, "return {}", expression),
43 | Statement::Return(None) => write!(fmt, "return"),
44 | Statement::Expression(ref expression) => write!(fmt, "{}", expression),
45 | Statement::FunctionDeclaration(function) => write!(fmt, "{}", function),
46 | Statement::Trait(trait_) => write!(fmt, "{}", trait_),
47 | Statement::Conformance(conformance) => write!(fmt, "{}", conformance),
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/transpiler/python/representations.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use std::rc::Rc;
3 |
4 | use uuid::Uuid;
5 |
6 | use crate::program::expression_tree::ExpressionOperation;
7 | use crate::program::functions::{FunctionHead, FunctionImplementation};
8 | use crate::program::types::TypeProto;
9 | use crate::transpiler::namespaces;
10 |
11 | #[derive(Clone)]
12 | pub struct Representations {
13 | pub function_forms: HashMap, FunctionForm>,
14 | pub type_ids: HashMap, Uuid>,
15 | }
16 |
17 | impl Representations {
18 | pub fn new() -> Representations {
19 | Representations {
20 | function_forms: Default::default(),
21 | type_ids: Default::default(),
22 | }
23 | }
24 | }
25 |
26 | // The IDs are attached per object because theoretically it's possible for a representation to use
27 | // 0 names (direct keyword use) or 2 (using multiple keywords). They just 'happen' to all use one.
28 | #[derive(PartialEq, Eq, Clone, Debug)]
29 | pub enum FunctionForm {
30 | Identity,
31 | CallAsFunction,
32 | Constant(Uuid),
33 | FunctionCall(Uuid),
34 | SetMemberField(Uuid),
35 | GetMemberField(Uuid),
36 | MemberCall(Uuid),
37 | Unary(Uuid),
38 | Binary(Uuid),
39 | }
40 |
41 | pub fn find_for_function(forms: &mut HashMap, FunctionForm>, global_namespace: &mut namespaces::Level, function_head: &Rc, implementation: &FunctionImplementation) {
42 | if implementation.parameter_locals.is_empty() {
43 | // TODO We could make a helper function and still use a constant even if we use blocks.
44 | let has_blocks = implementation.expression_tree.values.values().any(|op| matches!(op, ExpressionOperation::Block));
45 | if !has_blocks {
46 | global_namespace.insert_name(function_head.function_id, function_head.declared_representation.name.as_str());
47 | forms.insert(Rc::clone(&function_head), FunctionForm::Constant(function_head.function_id));
48 | return
49 | }
50 | }
51 |
52 | global_namespace.insert_name(function_head.function_id, function_head.declared_representation.name.as_str());
53 | forms.insert(Rc::clone(&function_head), FunctionForm::FunctionCall(function_head.function_id));
54 | }
55 |
--------------------------------------------------------------------------------
/src/program/traits/binding.rs:
--------------------------------------------------------------------------------
1 | use std::collections::{HashMap, HashSet};
2 | use std::fmt::{Debug, Formatter};
3 | use std::hash::{Hash, Hasher};
4 | use std::rc::Rc;
5 |
6 | use itertools::Itertools;
7 |
8 | use crate::program::generics::GenericAlias;
9 | use crate::program::traits::Trait;
10 | use crate::program::types::TypeProto;
11 | use crate::util::fmt::write_keyval;
12 |
13 | /// Some application of a trait with specific types.
14 | #[derive(Clone, Eq, PartialEq)]
15 | pub struct TraitBinding {
16 | /// The trait that is bound.
17 | pub trait_: Rc,
18 |
19 | /// A mapping from each of the trait's generics to some type.
20 | pub generic_to_type: HashMap, Rc>,
21 | }
22 |
23 | impl TraitBinding {
24 | pub fn mapping_types(&self, map: &dyn Fn(&Rc) -> Rc) -> Rc {
25 | Rc::new(TraitBinding {
26 | trait_: Rc::clone(&self.trait_),
27 | generic_to_type: self.generic_to_type.iter().map(|(generic, type_) | (Rc::clone(generic), map(type_))).collect()
28 | })
29 | }
30 |
31 | pub fn try_mapping_types(&self, map: &dyn Fn(&Rc) -> Result, B>) -> Result, B> {
32 | Ok(Rc::new(TraitBinding {
33 | trait_: Rc::clone(&self.trait_),
34 | generic_to_type: self.generic_to_type.iter().map(|(generic, type_) | Ok((Rc::clone(generic), map(type_)?))).try_collect()?
35 | }))
36 | }
37 |
38 | pub fn collect_generics(&self) -> HashSet {
39 | TypeProto::collect_generics(self.generic_to_type.values())
40 | }
41 | }
42 |
43 | impl Hash for TraitBinding {
44 | fn hash(&self, state: &mut H) {
45 | self.trait_.hash(state);
46 |
47 | for keyval in self.generic_to_type.iter().sorted_by_key(|(trait_, type_)| trait_.id) {
48 | keyval.hash(state);
49 | }
50 | }
51 | }
52 |
53 | impl Debug for TraitBinding {
54 | fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
55 | write!(fmt, "{}", self.trait_.name)?;
56 | if !self.generic_to_type.is_empty() {
57 | write!(fmt, "<")?;
58 | write_keyval(fmt, &self.generic_to_type)?;
59 | write!(fmt, ">")?;
60 | }
61 | Ok(())
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/program/functions/representation.rs:
--------------------------------------------------------------------------------
1 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
2 | pub enum FunctionCallExplicity {
3 | Explicit,
4 | Implicit,
5 | }
6 |
7 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
8 | pub enum FunctionTargetType {
9 | Global,
10 | Member
11 | }
12 |
13 | #[derive(PartialEq, Eq, Hash, Clone, Debug)]
14 | pub struct FunctionRepresentation {
15 | /// Name of the function.
16 | pub name: String,
17 | pub target_type: FunctionTargetType,
18 | pub call_explicity: FunctionCallExplicity,
19 | }
20 |
21 |
22 | impl FunctionRepresentation {
23 | pub fn dummy() -> FunctionRepresentation {
24 | FunctionRepresentation {
25 | name: "fn".into(),
26 | target_type: FunctionTargetType::Global,
27 | call_explicity: FunctionCallExplicity::Explicit,
28 | }
29 | }
30 |
31 | pub fn new(name: &str, target_type: FunctionTargetType, explicity: FunctionCallExplicity) -> FunctionRepresentation {
32 | FunctionRepresentation {
33 | name: name.into(),
34 | target_type,
35 | call_explicity: explicity,
36 | }
37 | }
38 |
39 | pub fn new_global_function(name: &str) -> FunctionRepresentation {
40 | FunctionRepresentation {
41 | name: name.into(),
42 | target_type: FunctionTargetType::Global,
43 | call_explicity: FunctionCallExplicity::Explicit,
44 | }
45 | }
46 |
47 | pub fn new_global_implicit(name: &str) -> FunctionRepresentation {
48 | FunctionRepresentation {
49 | name: name.into(),
50 | target_type: FunctionTargetType::Global,
51 | call_explicity: FunctionCallExplicity::Implicit,
52 | }
53 | }
54 | pub fn new_member_function(name: &str) -> FunctionRepresentation {
55 | FunctionRepresentation {
56 | name: name.into(),
57 | target_type: FunctionTargetType::Member,
58 | call_explicity: FunctionCallExplicity::Explicit,
59 | }
60 | }
61 |
62 | pub fn new_member_implicit(name: &str) -> FunctionRepresentation {
63 | FunctionRepresentation {
64 | name: name.into(),
65 | target_type: FunctionTargetType::Member,
66 | call_explicity: FunctionCallExplicity::Implicit,
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/resolver/fields.rs:
--------------------------------------------------------------------------------
1 | use std::rc::Rc;
2 |
3 | use crate::program::functions::{FunctionHead, FunctionInterface, FunctionRepresentation, Parameter, ParameterKey};
4 | use crate::program::traits::{FieldHint, Trait};
5 | use crate::program::types::TypeProto;
6 |
7 | pub fn make(name: &str, self_type: &Rc, field_type: &Rc, add_getter: bool, add_setter: bool) -> FieldHint {
8 | let getter = add_getter.then_some({
9 | let head = FunctionHead::new_static(
10 | vec!["self".into()],
11 | FunctionRepresentation::new_member_implicit(name),
12 | Rc::new(FunctionInterface {
13 | parameters: vec![
14 | Parameter {
15 | external_key: ParameterKey::Positional,
16 | type_: self_type.clone(),
17 | }],
18 | return_type: field_type.clone(),
19 | requirements: Default::default(),
20 | generics: Default::default(),
21 | })
22 | );
23 | head
24 | });
25 |
26 | let setter = add_setter.then_some({
27 | let head = FunctionHead::new_static(
28 | vec!["self".into(), "new_value".into()],
29 | FunctionRepresentation::new_member_implicit(name),
30 | Rc::new(FunctionInterface {
31 | parameters: vec![Parameter {
32 | external_key: ParameterKey::Positional,
33 | type_: self_type.clone(),
34 | }, Parameter {
35 | external_key: ParameterKey::Positional,
36 | type_: field_type.clone(),
37 | }],
38 | return_type: TypeProto::void(),
39 | requirements: Default::default(),
40 | generics: Default::default(),
41 | })
42 | );
43 | head
44 | });
45 |
46 | FieldHint {
47 | name: name.to_string(),
48 | type_: field_type.clone(),
49 | setter,
50 | getter,
51 | }
52 | }
53 |
54 | pub fn add_to_trait(trait_: &mut Trait, field: FieldHint) {
55 | if let Some(getter) = &field.getter {
56 | trait_.abstract_functions.insert(Rc::clone(getter));
57 | }
58 | if let Some(setter) = &field.setter {
59 | trait_.abstract_functions.insert(Rc::clone(setter));
60 | }
61 | trait_.field_hints.push(field)
62 | }
63 |
--------------------------------------------------------------------------------
/src/resolver/precedence_order.rs:
--------------------------------------------------------------------------------
1 | use std::rc::Rc;
2 |
3 | use itertools::Itertools;
4 | use strum::IntoEnumIterator;
5 | use uuid::Uuid;
6 |
7 | use crate::ast;
8 | use crate::error::{RResult, RuntimeError, TryCollectMany};
9 | use crate::parser::expressions;
10 | use crate::parser::grammar::{OperatorAssociativity, PrecedenceGroup};
11 | use crate::program::functions::ParameterKey;
12 | use crate::resolver::scopes;
13 |
14 | pub fn resolve_precedence_order(call_struct: &ast::Struct, scope: &scopes::Scope) -> RResult>> {
15 | let order: Vec> = call_struct.arguments.iter().map(|arg| {
16 | let ParameterKey::Name(name) = &arg.value.key else {
17 | return Err(RuntimeError::error("Not a named argument.").in_range(arg.position.clone()).to_array())
18 | };
19 | if arg.value.type_declaration.is_some() {
20 | return Err(RuntimeError::error("Unexpected type declaration.").in_range(arg.position.clone()).to_array())
21 | }
22 |
23 | let associativity = resolve_associativity(&arg.value.value, scope)?;
24 |
25 | Ok(Rc::new(PrecedenceGroup {
26 | trait_id: Uuid::new_v4(),
27 | name: name.to_string(),
28 | associativity,
29 | }))
30 | }).try_collect_many()?;
31 |
32 | order.iter().filter(|x| x.associativity == OperatorAssociativity::LeftUnary).at_most_one()
33 | .map_err(|_| RuntimeError::error("Cannot declare two LeftUnary associativities.").to_array())?;
34 | order.iter().filter(|x| x.associativity == OperatorAssociativity::RightUnary).at_most_one()
35 | .map_err(|_| RuntimeError::error("Cannot declare two RightUnary associativities.").to_array())?;
36 |
37 | Ok(order)
38 | }
39 |
40 | pub fn resolve_associativity(body: &ast::Expression, scope: &scopes::Scope) -> RResult {
41 | let error = RuntimeError::error(
42 | format!("Operator associativity needs to be one of {:?}.", OperatorAssociativity::iter().collect_vec()).as_str()
43 | ).to_array();
44 |
45 | let parsed = expressions::parse(body, &scope.grammar)?;
46 |
47 | let expressions::Value::Identifier(identifier) = &parsed.value else {
48 | return Err(error);
49 | };
50 |
51 | let Ok(associativity) = OperatorAssociativity::iter().filter(|a| &a.to_string() == identifier.as_str()).exactly_one() else {
52 | return Err(error)
53 | };
54 |
55 | Ok(associativity)
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/src/program/functions/logic.rs:
--------------------------------------------------------------------------------
1 | use std::rc::Rc;
2 |
3 | use crate::error::{RResult, RuntimeError};
4 | use crate::program::allocation::ObjectReference;
5 | use crate::program::functions::{FunctionHead, FunctionImplementation};
6 | use crate::program::primitives;
7 | use crate::program::traits::{StructInfo, Trait};
8 | use crate::program::types::TypeProto;
9 |
10 | #[derive(Clone)]
11 | pub enum FunctionLogic {
12 | Implementation(Box),
13 | Descriptor(FunctionLogicDescriptor),
14 | }
15 |
16 |
17 | #[derive(Clone, PartialEq, Eq, Debug)]
18 | pub enum FunctionLogicDescriptor {
19 | /// This function was not described by the implementer and is expected not to be called,
20 | /// or to be injected by a transpiler.
21 | Stub,
22 | TraitProvider(Rc),
23 | FunctionProvider(Rc),
24 | PrimitiveOperation { operation: PrimitiveOperation, type_: primitives::Type },
25 | Clone(Rc),
26 | Constructor(Rc),
27 | GetMemberField(Rc, Rc),
28 | SetMemberField(Rc, Rc),
29 | }
30 |
31 | #[derive(Clone, Copy, PartialEq, Eq, Debug)]
32 | pub enum PrimitiveOperation {
33 | And, Or, Not,
34 | Negative,
35 | Add, Subtract,
36 | Multiply, Divide,
37 | Modulo,
38 | Exp, Log,
39 | EqualTo, NotEqualTo,
40 | GreaterThan, LesserThan,
41 | GreaterThanOrEqual, LesserThanOrEqual,
42 | ParseIntString,
43 | ParseRealString,
44 | ToString,
45 | Clone,
46 | }
47 |
48 | impl FunctionLogic {
49 | pub fn is_implementation(&self) -> bool {
50 | match self {
51 | FunctionLogic::Implementation(_) => true,
52 | FunctionLogic::Descriptor(_) => false,
53 | }
54 | }
55 |
56 | pub fn to_implementation(self) -> RResult> {
57 | match self {
58 | FunctionLogic::Implementation(i) => Ok(i),
59 | FunctionLogic::Descriptor(_) => Err(RuntimeError::error("Cannot use a function with described logic as an implementation.").to_array()),
60 | }
61 | }
62 |
63 | pub fn as_implementation(&self) -> RResult<&FunctionImplementation> {
64 | match self {
65 | FunctionLogic::Implementation(i) => Ok(i),
66 | FunctionLogic::Descriptor(_) => Err(RuntimeError::error("Cannot use a function with described logic as an implementation.").to_array()),
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/program/module.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashSet;
2 | use std::rc::Rc;
3 |
4 | use crate::parser::grammar::{Pattern, PrecedenceGroup};
5 | use itertools::Itertools;
6 | use uuid::Uuid;
7 |
8 | use crate::program::functions::FunctionHead;
9 | use crate::program::traits::{TraitConformanceRule, TraitGraph};
10 | use crate::resolver::scopes;
11 | use crate::source::Source;
12 |
13 | pub type ModuleName = Vec;
14 |
15 | pub fn module_name(name: &str) -> ModuleName {
16 | name.split(".").map(ToString::to_string).collect_vec()
17 | }
18 |
19 | pub struct Module {
20 | pub id: Uuid,
21 | pub name: ModuleName,
22 |
23 | /// For each trait, its metatype getter function.
24 | pub included_modules: Vec>,
25 |
26 | pub precedence_order: Option>>,
27 | pub patterns: HashSet>>>,
28 | pub trait_conformance: Box,
29 |
30 | /// Functions that are directly referencible.
31 | /// Usually, these are just getters for traits, function objects etc.
32 | pub exposed_functions: HashSet>,
33 |
34 | /// These come from decorators.
35 | /// Collecting all decorated functions allows us to fail late - the rest of the code is still
36 | /// valid even if multiple main! functions are declared! We just cannot run them as 'main'.
37 | pub main_functions: Vec>,
38 | pub transpile_functions: Vec>,
39 | }
40 |
41 | impl Module {
42 | pub fn new(name: ModuleName) -> Module {
43 | Module {
44 | id: Default::default(),
45 | name,
46 | included_modules: vec![],
47 | precedence_order: None,
48 | patterns: Default::default(),
49 | trait_conformance: Box::new(TraitGraph::new()),
50 | exposed_functions: Default::default(),
51 | main_functions: vec![],
52 | transpile_functions: vec![],
53 | }
54 | }
55 | }
56 |
57 | impl Module {
58 | pub fn explicit_functions<'a>(&'a self, source: &'a Source) -> Vec<&'a Rc> {
59 | self.exposed_functions.iter().collect_vec()
60 | }
61 |
62 | /// Add the rule to our conformance, but also the scope.
63 | pub fn add_conformance_rule(&mut self, rule: Rc, scope: &mut scopes::Scope) {
64 | self.trait_conformance.add_conformance_rule(rule.clone());
65 | scope.trait_conformance.add_conformance_rule(rule);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/util/graphs/task_dependency_graph.rs:
--------------------------------------------------------------------------------
1 | use std::collections::hash_map::Entry;
2 | use std::collections::{HashMap, HashSet};
3 | use std::hash::Hash;
4 |
5 | pub struct TaskDependencyGraph {
6 | pub done: HashSet,
7 | pub next: Vec,
8 | pub changed: HashSet,
9 | pub dependencies: HashMap>,
10 | pub dependents: HashMap>,
11 | }
12 |
13 | impl TaskDependencyGraph {
14 | pub fn new() -> TaskDependencyGraph {
15 | TaskDependencyGraph {
16 | done: Default::default(),
17 | next: vec![],
18 | changed: Default::default(),
19 | dependencies: Default::default(),
20 | dependents: Default::default(),
21 | }
22 | }
23 |
24 | pub fn mark_as_done(&mut self, object: I) {
25 | let mut next = vec![object];
26 |
27 | while let Some(object) = next.pop() {
28 | self.dependencies.remove(&object);
29 | self.changed.remove(&object);
30 |
31 | for dependent in self.dependents.remove(&object).into_iter().flatten() {
32 | let dependencies = self.dependencies.get_mut(&dependent).unwrap();
33 | dependencies.remove(&object);
34 | if dependencies.is_empty() {
35 | self.changed.insert(dependent.clone());
36 | next.push(dependent);
37 | }
38 | }
39 |
40 | self.done.insert(object);
41 | }
42 | }
43 |
44 | pub fn add_task(&mut self, object: I, dependencies: HashSet) {
45 | let remaining: HashSet = dependencies.difference(&self.done).cloned().collect();
46 |
47 | match remaining.is_empty() {
48 | true => {
49 | self.next.push(object.clone());
50 | self.mark_as_done(object);
51 | }
52 | false => {
53 | for dependency in remaining.iter() {
54 | match self.dependents.entry(dependency.clone()) {
55 | Entry::Occupied(mut o) => _ = o.get_mut().insert(object.clone()),
56 | Entry::Vacant(v) => _ = v.insert(HashSet::from([object.clone()]))
57 | }
58 | }
59 | if remaining.len() < dependencies.len() {
60 | self.changed.insert(object.clone());
61 | }
62 | self.dependencies.insert(object, remaining);
63 | }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/macro/src/lib.rs:
--------------------------------------------------------------------------------
1 | extern crate proc_macro;
2 | use proc_macro::TokenStream;
3 |
4 | #[proc_macro]
5 | pub fn pop_ip(_item: TokenStream) -> TokenStream {
6 | let args_str = _item.to_string();
7 | let mut args = args_str.split(" ");
8 |
9 | let type_ = args.next().unwrap();
10 | assert!(args.next().is_none());
11 |
12 | // TODO When it's stable, these should be replaced by quote!()
13 | format!("
14 | {{
15 | let val = read_unaligned(ip as *mut {type_});
16 | ip = transmute((ip as *mut {type_}).add(1));
17 | val
18 | }}
19 | ", type_=type_).parse().unwrap()
20 | }
21 |
22 | #[proc_macro]
23 | pub fn un_expr(_item: TokenStream) -> TokenStream {
24 | let args_str = _item.to_string();
25 | let mut args = args_str.split(",");
26 |
27 | let type_ = args.next().unwrap();
28 | let result_type = args.next().unwrap();
29 | let fun = args.collect::>().join(",");
30 |
31 | // TODO When it's stable, these should be replaced by quote!()
32 | format!("
33 | {{
34 | let sp_last = sp.offset(-8);
35 | let val = (*sp_last).{type_};
36 | (*sp_last).{result_type} = {fun};
37 | }}
38 | ", type_=type_, result_type=result_type, fun=fun).parse().unwrap()
39 | }
40 |
41 | #[proc_macro]
42 | pub fn un_expr_try(_item: TokenStream) -> TokenStream {
43 | let args_str = _item.to_string();
44 | let mut args = args_str.split(",");
45 |
46 | let type_ = args.next().unwrap();
47 | let result_type = args.next().unwrap();
48 | let fun = args.collect::>().join(",");
49 |
50 | // TODO When it's stable, these should be replaced by quote!()
51 | format!("
52 | {{
53 | let sp_last = sp.offset(-8);
54 | let val = (*sp_last).{type_};
55 | (*sp_last).{result_type} = {fun}?;
56 | }}
57 | ", type_=type_, result_type=result_type, fun=fun).parse().unwrap()
58 | }
59 |
60 | #[proc_macro]
61 | pub fn bin_expr(_item: TokenStream) -> TokenStream {
62 | let args_str = _item.to_string();
63 | let mut args = args_str.split(",");
64 |
65 | let type_ = args.next().unwrap();
66 | let result_type = args.next().unwrap();
67 | let fun = args.collect::>().join(",");
68 |
69 | // TODO When it's stable, these should be replaced by quote!()
70 | format!("
71 | {{
72 | sp = sp.offset(-8);
73 | let rhs = (*sp).{type_};
74 |
75 | let sp_last = sp.offset(-8);
76 | let lhs = (*sp_last).{type_};
77 | (*sp_last).{result_type} = {fun};
78 | }}
79 | ", type_=type_, fun=fun, result_type=result_type).parse().unwrap()
80 | }
81 |
--------------------------------------------------------------------------------
/src/interpreter/disassembler.rs:
--------------------------------------------------------------------------------
1 | use crate::interpreter::chunks::Chunk;
2 | use crate::interpreter::opcode::{OpCode, Primitive};
3 | use std::mem::transmute;
4 | use uuid::Uuid;
5 |
6 | pub fn disassemble(chunk: &Chunk) {
7 | unsafe {
8 | let mut idx = 0;
9 |
10 | while idx < chunk.code.len() {
11 | print!("{:04}\t", idx);
12 | idx += disassemble_one(transmute(&chunk.code[idx]));
13 | print!("\n");
14 | }
15 | }
16 | }
17 |
18 | pub fn disassemble_one(ip: *const u8) -> usize {
19 | unsafe {
20 | let code = transmute::(*ip);
21 | // TODO Somehow, {:<20?} doesn't pad correctly.
22 | print!("{:<20}", format!("{:?}", code));
23 |
24 | match code {
25 | OpCode::NEG | OpCode::ADD | OpCode::SUB | OpCode::MUL | OpCode::DIV |
26 | OpCode::EQ | OpCode::NEQ | OpCode::GR | OpCode::GR_EQ | OpCode::LE | OpCode::LE_EQ |
27 | OpCode::MOD | OpCode::EXP | OpCode::LOG => {
28 | print!("\t{:?}", transmute::(*ip.add(1)));
29 | return 1 + 1;
30 | },
31 | OpCode::LOAD8 => {
32 | print!("\t{:?}", *ip.add(1));
33 | return 1 + 1;
34 | }
35 | OpCode::LOAD16 => {
36 | print!("\t{:?}", *(ip.add(1) as *mut u16));
37 | return 1 + 2;
38 | }
39 | OpCode::LOAD32 | OpCode::LOAD_CONSTANT_32 | OpCode::GET_MEMBER_32 | OpCode::SET_MEMBER_32 | OpCode::ALLOC_32 => {
40 | print!("\t{:?}", *(ip.add(1) as *mut u32));
41 | return 1 + 4;
42 | }
43 | OpCode::LOAD64 => {
44 | print!("\t{:?}", *(ip.add(1) as *mut u64));
45 | return 1 + 8;
46 | }
47 | OpCode::CALL_INTRINSIC => {
48 | print!("\t{:?}", *(ip.add(1) as *mut usize));
49 | return 1 + size_of::();
50 | }
51 | OpCode::JUMP | OpCode::JUMP_IF_FALSE | OpCode::LOAD_LOCAL_32 | OpCode::STORE_LOCAL_32 => {
52 | print!("\t{:?}", *(ip.add(1) as *mut i32));
53 | return 1 + 4;
54 | }
55 | OpCode::CALL => {
56 | print!("\t{}", Uuid::from_u128(*(ip.add(1) as *mut u128)));
57 | return 1 + 16;
58 | }
59 | OpCode::NOOP | OpCode::PANIC | OpCode::RETURN | OpCode::AND |
60 | OpCode::OR | OpCode::POP64 | OpCode::NOT |
61 | OpCode::DUP64 | OpCode::LOAD0 | OpCode::SWAP64 => {
62 | return 1;
63 | },
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/resolver/decorations.rs:
--------------------------------------------------------------------------------
1 | use std::rc::Rc;
2 |
3 | use itertools::Itertools;
4 | use uuid::Uuid;
5 |
6 | use crate::ast;
7 | use crate::error::{RResult, RuntimeError, TryCollectMany};
8 | use crate::parser::expressions;
9 | use crate::parser::grammar::{Pattern, PatternPart};
10 | use crate::program::functions::{FunctionHead, ParameterKey};
11 | use crate::resolver::scopes;
12 | use crate::util::position::Positioned;
13 |
14 | pub fn try_parse_pattern(decoration: &ast::Expression, function: Rc, scope: &scopes::Scope) -> RResult>>> {
15 | let parsed = expressions::parse(decoration, &scope.grammar)?;
16 |
17 | let expressions::Value::FunctionCall(target, call_struct) = &parsed.value else {
18 | return Err(RuntimeError::error("Unrecognized decoration.").to_array());
19 | };
20 |
21 | let expressions::Value::Identifier(decoration_name) = &target.value else {
22 | return Err(RuntimeError::error("Unrecognized decoration.").to_array());
23 | };
24 |
25 | if decoration_name.as_str() != "pattern" {
26 | return Err(RuntimeError::error("Unrecognized decoration.").to_array());
27 | }
28 |
29 | let [a, b] = &call_struct.arguments[..] else {
30 | return Err(RuntimeError::error("pattern decoration needs two arguments.").to_array())
31 | };
32 |
33 | if a.value.key != ParameterKey::Positional || a.value.type_declaration.is_some() ||
34 | b.value.key != ParameterKey::Positional || b.value.type_declaration.is_some() {
35 | return Err(RuntimeError::error("pattern decoration arguments are faulty.").to_array())
36 | }
37 |
38 | let precedence_group = match &b.value.value.iter().map(|p| p.as_ref()).collect_vec()[..] {
39 | [Positioned { position, value: ast::Term::Identifier(precedence) }] =>
40 | scope.resolve_precedence_group(&precedence)?,
41 | _ => return Err(RuntimeError::error("Second argument to pattern needs to be a precedence name.").to_array())
42 | };
43 |
44 | let parts: Vec> = a.value.value.iter()
45 | .map(|pterm| {
46 | match &pterm.value {
47 | ast::Term::Identifier(i) => {
48 | Ok(Box::new(function.declared_internal_parameter_names.iter()
49 | .position(|p| p == i)
50 | .map(|p| PatternPart::Parameter(p))
51 | .unwrap_or(PatternPart::Keyword(i.clone()))))
52 | },
53 | _ => Err(RuntimeError::error("Bad pattern.").to_array()),
54 | }
55 | })
56 | .try_collect_many()?;
57 |
58 | Ok(Rc::new(Pattern {
59 | id: Uuid::new_v4(),
60 | precedence_group,
61 | parts,
62 | function,
63 | }))
64 | }
65 |
--------------------------------------------------------------------------------
/src/resolver/ambiguous/abstract_call.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use std::fmt::{Display, Formatter};
3 | use std::ops::Range;
4 | use std::rc::Rc;
5 |
6 | use crate::error::{ErrInRange, RResult};
7 | use crate::program::expression_tree::{ExpressionID, ExpressionOperation};
8 | use crate::program::functions::{FunctionBinding, FunctionHead};
9 | use crate::program::traits::{RequirementsFulfillment, Trait, TraitGraph};
10 | use crate::resolver::ambiguous::{AmbiguityResult, ResolverAmbiguity};
11 | use crate::resolver::imperative::ImperativeResolver;
12 |
13 | pub struct AmbiguousAbstractCall {
14 | pub expression_id: ExpressionID,
15 | pub arguments: Vec,
16 | pub traits: TraitGraph,
17 |
18 | pub range: Range,
19 |
20 | pub trait_: Rc,
21 | pub abstract_function: Rc,
22 | }
23 |
24 | impl Display for AmbiguousAbstractCall {
25 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
26 | write!(f, "Ambiguous abstract function call.")
27 | }
28 | }
29 |
30 | impl ResolverAmbiguity for AmbiguousAbstractCall {
31 | fn attempt_to_resolve(&mut self, resolver: &mut ImperativeResolver) -> RResult> {
32 | let type_ = resolver.builder.types.resolve_binding_alias(&self.expression_id)?;
33 |
34 | let requirement = self.trait_.create_generic_binding(vec![("Self", type_.clone())]);
35 | let trait_conformance = self.traits.satisfy_requirement(&requirement, &resolver.builder.types)
36 | .err_in_range(&self.range)?;
37 | Ok(match trait_conformance {
38 | AmbiguityResult::Ambiguous => {
39 | AmbiguityResult::Ambiguous
40 | }
41 | AmbiguityResult::Ok(trait_conformance) => {
42 | let used_function = &trait_conformance.conformance.function_mapping[&self.abstract_function];
43 |
44 | resolver.builder.expression_tree.values.insert(
45 | self.expression_id.clone(),
46 | ExpressionOperation::FunctionCall(Rc::new(FunctionBinding {
47 | function: Rc::clone(used_function),
48 | requirements_fulfillment: Rc::new(RequirementsFulfillment {
49 | conformance: HashMap::from([(requirement, trait_conformance)]),
50 | generic_mapping: HashMap::from([(Rc::clone(&self.trait_.generics["Self"]), type_.clone())])
51 | }),
52 | }))
53 | );
54 | resolver.builder.types.bind(self.expression_id.clone(), type_.as_ref())
55 | .err_in_range(&self.range)?;
56 |
57 | AmbiguityResult::Ok(())
58 | }
59 | })
60 | }
61 |
62 | fn get_position(&self) -> Range {
63 | self.range.clone()
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/interpreter/chunks.rs:
--------------------------------------------------------------------------------
1 | use crate::interpreter::data::Value;
2 | use crate::interpreter::opcode::OpCode;
3 | use std::ptr::write_unaligned;
4 |
5 | pub struct Chunk {
6 | pub code: Vec,
7 | pub constants: Vec