├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── crates ├── c11-tree │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs ├── psl-codegen-c11 │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs ├── psl-diagnostics │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs └── psl │ ├── Cargo.toml │ └── src │ ├── ast │ ├── declarations.rs │ ├── expressions.rs │ ├── fragments.rs │ ├── items.rs │ ├── mod.rs │ ├── node_id.rs │ ├── program.rs │ ├── statements.rs │ └── token.rs │ ├── codegen │ ├── construct │ │ ├── mod.rs │ │ ├── scope.rs │ │ └── type.rs │ ├── context.rs │ ├── impls │ │ ├── declarations │ │ │ ├── function_declaration.rs │ │ │ ├── mod.rs │ │ │ └── variable_declaration.rs │ │ ├── expressions │ │ │ ├── binary_operator.rs │ │ │ ├── if.rs │ │ │ ├── literal.rs │ │ │ ├── mod.rs │ │ │ ├── name.rs │ │ │ ├── postfix_operator.rs │ │ │ ├── read.rs │ │ │ └── return.rs │ │ ├── fragments │ │ │ ├── expression_or_block.rs │ │ │ └── mod.rs │ │ ├── item.rs │ │ ├── mod.rs │ │ ├── program.rs │ │ └── statements │ │ │ ├── assignment.rs │ │ │ ├── mod.rs │ │ │ ├── while.rs │ │ │ └── write.rs │ ├── mod.rs │ ├── pass │ │ ├── mod.rs │ │ └── name_resolution.rs │ ├── rt │ │ ├── header.h │ │ ├── panic.h │ │ ├── read.h │ │ ├── typedef.h │ │ └── write.h │ ├── scope.rs │ └── visitor.rs │ ├── diagnostics │ ├── diagnostic_code.rs │ ├── mod.rs │ └── transform │ │ ├── mod.rs │ │ └── parse_error.rs │ ├── lib.rs │ ├── main.rs │ └── syntax │ ├── declarations │ ├── function_declaration.rs │ ├── mod.rs │ └── variable_declaration.rs │ ├── expressions │ ├── binary_operator.rs │ ├── if.rs │ ├── literal.rs │ ├── mod.rs │ ├── name.rs │ ├── postfix_operator.rs │ ├── read.rs │ ├── return.rs │ └── simple.rs │ ├── fragments │ ├── block.rs │ ├── format_specifier.rs │ ├── mod.rs │ ├── separator.rs │ └── type.rs │ ├── item.rs │ ├── mod.rs │ ├── program.rs │ ├── statements │ ├── assignment.rs │ ├── mod.rs │ ├── while.rs │ └── write.rs │ └── tokens │ ├── identifier.rs │ ├── keyword.rs │ ├── literal.rs │ ├── mod.rs │ ├── punctuations.rs │ └── whitespaces.rs └── snippets ├── 10xx └── p1000.psl ├── 24xx └── p2420.psl ├── 25xx └── p2557.psl ├── 26xx └── p2609.psl └── 27xx ├── p2747.psl └── p2753.psl /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | types: [opened, synchronize, reopened] 4 | 5 | name: CI 6 | 7 | jobs: 8 | clippy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: dtolnay/rust-toolchain@stable 13 | - uses: reviewdog/action-setup@v1 14 | - name: check clippy 15 | env: 16 | REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | run: | 18 | cargo clippy \ 19 | -q --message-format short \ 20 | --workspace \ 21 | -- -Dwarnings \ 22 | 2>&1 \ 23 | | reviewdog \ 24 | -f=clippy \ 25 | -name=clippy \ 26 | -reporter=github-pr-review \ 27 | -filter-mode=nofilter \ 28 | -fail-on-error=true 29 | rustfmt: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: dtolnay/rust-toolchain@stable 34 | with: 35 | toolchain: nightly 36 | components: cargo, rustfmt 37 | - uses: reviewdog/action-setup@v1 38 | - name: check rustfmt 39 | env: 40 | REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | run: | 42 | cargo +nightly fmt \ 43 | -- --emit checkstyle \ 44 | | reviewdog \ 45 | -f=checkstyle \ 46 | -name=rustfmt \ 47 | -reporter=github-pr-review \ 48 | -filter-mode=nofilter \ 49 | -fail-on-error=true 50 | test-coverage: 51 | runs-on: ubuntu-latest 52 | steps: 53 | - uses: actions/checkout@v4 54 | - uses: dtolnay/rust-toolchain@stable 55 | with: 56 | components: cargo,llvm-tools 57 | - run: curl -L https://github.com/mozilla/grcov/releases/latest/download/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf - 58 | - run: LLVM_PROFILE_FILE="grcov-%p-%m.profraw" RUSTFLAGS="-Cinstrument-coverage" cargo test 59 | - run: ./grcov --ignore-not-existing --binary-path ./target/debug -o lcov.info -s . . 60 | - uses: codecov/codecov-action@v3 61 | env: 62 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | snippets/**/*.c 4 | snippets/**/*.o -------------------------------------------------------------------------------- /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 = "ariadne" 7 | version = "0.4.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "44055e597c674aef7cb903b2b9f6e4cba1277ed0d2d61dae7cd52d7ffa81f8e2" 10 | dependencies = [ 11 | "unicode-width", 12 | "yansi 1.0.1", 13 | ] 14 | 15 | [[package]] 16 | name = "c11-tree" 17 | version = "0.1.0" 18 | 19 | [[package]] 20 | name = "diff" 21 | version = "0.1.13" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" 24 | 25 | [[package]] 26 | name = "memchr" 27 | version = "2.6.4" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 30 | 31 | [[package]] 32 | name = "pretty_assertions" 33 | version = "1.4.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" 36 | dependencies = [ 37 | "diff", 38 | "yansi 0.5.1", 39 | ] 40 | 41 | [[package]] 42 | name = "psl" 43 | version = "0.1.3" 44 | dependencies = [ 45 | "ariadne", 46 | "pretty_assertions", 47 | "unicode-ident", 48 | "winnow", 49 | ] 50 | 51 | [[package]] 52 | name = "psl-codegen-c11" 53 | version = "0.1.0" 54 | 55 | [[package]] 56 | name = "psl-diagnostics" 57 | version = "0.1.0" 58 | 59 | [[package]] 60 | name = "unicode-ident" 61 | version = "1.0.12" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 64 | 65 | [[package]] 66 | name = "unicode-width" 67 | version = "0.1.11" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 70 | 71 | [[package]] 72 | name = "winnow" 73 | version = "0.6.13" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" 76 | dependencies = [ 77 | "memchr", 78 | ] 79 | 80 | [[package]] 81 | name = "yansi" 82 | version = "0.5.1" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" 85 | 86 | [[package]] 87 | name = "yansi" 88 | version = "1.0.1" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" 91 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "crates/c11-tree", 5 | "crates/psl", 6 | "crates/psl-codegen-c11", 7 | "crates/psl-diagnostics", 8 | ] 9 | 10 | [workspace.dependencies] 11 | c11-tree = { path = "crates/c11-tree" } 12 | psl-codegen-c11 = { path = "crates/psl-codegen-c11" } 13 | psl-diagnostics = { path = "crates/psl-diagnostics" } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 psl-lang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

🎈psl

3 |

4 | A Modern Programming Language Empowering Algorithmic Thinking 5 |

6 |
7 | 8 | > [!WARNING] 9 | > We're currently in an early alpha stage, the product may be very incomplete and unstable. 10 | 11 | psl is a programming language specifically made for intuitive algorithm code writing. 12 | 13 | ## Installation 14 | 15 | ### From the release 16 | 17 | Not available now, sorry! 18 | 19 | ### From Git with Cargo 20 | 21 | ```sh 22 | cargo install \ 23 | --git https://github.com/psl-lang/psl \ 24 | --tag v0.1.3 25 | ``` 26 | 27 | > [!NOTE] 28 | > The `psl` executable may collide with libpsl (which curl depends on) executable. 29 | > See [psl(1)](https://man.archlinux.org/man/core/libpsl/psl.1.en) for more explanation. We may rename the package not to collide with that, the potent alternative is `pslc`.` 30 | -------------------------------------------------------------------------------- /crates/c11-tree/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "c11-tree" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /crates/c11-tree/README.md: -------------------------------------------------------------------------------- 1 | # c11-tree 2 | 3 | A library for constructing/modifying C11 abstract syntax tree. 4 | -------------------------------------------------------------------------------- /crates/c11-tree/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crates/psl-codegen-c11/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "psl-codegen-c11" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /crates/psl-codegen-c11/README.md: -------------------------------------------------------------------------------- 1 | # psl-codegen-c11 2 | 3 | The psl codegen emitting C11 codes. 4 | -------------------------------------------------------------------------------- /crates/psl-codegen-c11/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crates/psl-diagnostics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "psl-diagnostics" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /crates/psl-diagnostics/README.md: -------------------------------------------------------------------------------- 1 | # psl-diagnostics 2 | 3 | The error code, localizable messages, and the diagnostic itself and its reporter for the psl. 4 | -------------------------------------------------------------------------------- /crates/psl-diagnostics/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crates/psl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "psl" 3 | version = "0.1.3" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ariadne = "0.4.1" 10 | unicode-ident = "1.0.12" 11 | winnow = "0.6.13" 12 | 13 | [dev-dependencies] 14 | pretty_assertions = "1.4.0" 15 | -------------------------------------------------------------------------------- /crates/psl/src/ast/declarations.rs: -------------------------------------------------------------------------------- 1 | use super::{Expression, ExpressionOrBlock, Token, Type}; 2 | 3 | #[derive(Clone, Debug, Hash, PartialEq)] 4 | pub enum Declaration { 5 | Variable(VariableDeclaration), 6 | Function(FunctionDeclaration), 7 | } 8 | 9 | #[derive(Clone, Debug, Hash, PartialEq)] 10 | pub struct VariableDeclaration { 11 | pub ty: Type, 12 | pub name: Token, 13 | pub value: Option, 14 | } 15 | 16 | #[derive(Clone, Debug, Hash, PartialEq)] 17 | pub struct FunctionDeclaration { 18 | pub name: Token, 19 | pub parameters: Vec<(Type, Token)>, 20 | pub return_type: Type, 21 | pub body: ExpressionOrBlock, 22 | } 23 | -------------------------------------------------------------------------------- /crates/psl/src/ast/expressions.rs: -------------------------------------------------------------------------------- 1 | use super::{ExpressionOrBlock, Token, Type}; 2 | 3 | #[derive(Clone, Debug, Hash, PartialEq)] 4 | pub enum Expression { 5 | Literal(LiteralExpression), 6 | Read(ReadExpression), 7 | Return(ReturnExpression), 8 | Name(NameExpression), 9 | If(IfExpression), 10 | BinaryOperator(BinaryOperatorExpression), 11 | PostfixOperator(PostfixOperatorExpression), 12 | } 13 | 14 | #[derive(Clone, Debug, Hash, PartialEq)] 15 | pub struct LiteralExpression { 16 | pub value: Token, 17 | } 18 | 19 | #[derive(Clone, Debug, Hash, PartialEq)] 20 | pub enum ReadExpression { 21 | Type(Type), 22 | } 23 | #[derive(Clone, Debug, Hash, PartialEq)] 24 | pub struct ReturnExpression { 25 | pub value: Box, 26 | } 27 | 28 | #[derive(Clone, Debug, Hash, PartialEq)] 29 | pub struct NameExpression { 30 | pub name: Token, 31 | } 32 | 33 | #[derive(Clone, Debug, Hash, PartialEq)] 34 | pub struct IfExpression { 35 | pub condition: Box, 36 | pub positive: Box, 37 | pub negative: Option>, 38 | } 39 | 40 | #[derive(Clone, Debug, Hash, PartialEq)] 41 | pub struct BinaryOperatorExpression { 42 | pub lhs: Box, 43 | pub operator: BinaryOperator, 44 | pub rhs: Box, 45 | } 46 | 47 | #[derive(Clone, Debug, Hash, PartialEq)] 48 | pub enum BinaryOperator { 49 | Add, 50 | Subtract, 51 | Multiply, 52 | Divide, 53 | Modulus, 54 | Equal, 55 | NotEqual, 56 | Less, 57 | Greater, 58 | LessEqual, 59 | GreaterEqual, 60 | LogiacalAnd, 61 | LogicalOr, 62 | } 63 | 64 | #[derive(Clone, Debug, Hash, PartialEq)] 65 | pub struct PostfixOperatorExpression { 66 | pub expr: Box, 67 | pub operator: PostfixOperator, 68 | } 69 | #[derive(Clone, Debug, Hash, PartialEq)] 70 | pub enum PostfixOperator { 71 | Invoke(Vec), 72 | Index(Vec), 73 | } 74 | -------------------------------------------------------------------------------- /crates/psl/src/ast/fragments.rs: -------------------------------------------------------------------------------- 1 | use super::{Expression, Statement, Token}; 2 | 3 | #[derive(Clone, Debug, Hash, PartialEq)] 4 | pub enum Type { 5 | Simple(Token), 6 | } 7 | 8 | #[derive(Clone, Debug, Hash, PartialEq)] 9 | pub struct Block { 10 | pub statements: Vec, 11 | pub last_expression: Option>, 12 | } 13 | 14 | #[derive(Clone, Debug, Hash, PartialEq)] 15 | pub enum ExpressionOrBlock { 16 | Expression(Expression), 17 | Block(Block), 18 | } 19 | -------------------------------------------------------------------------------- /crates/psl/src/ast/items.rs: -------------------------------------------------------------------------------- 1 | use super::{Declaration, Statement}; 2 | 3 | #[derive(Clone, Debug, Hash, PartialEq)] 4 | pub enum Item { 5 | Declaration(Declaration), 6 | Statement(Statement), 7 | } 8 | -------------------------------------------------------------------------------- /crates/psl/src/ast/mod.rs: -------------------------------------------------------------------------------- 1 | pub use declarations::*; 2 | pub use expressions::*; 3 | pub use fragments::*; 4 | pub use items::*; 5 | pub use program::*; 6 | pub use statements::*; 7 | pub use token::*; 8 | 9 | mod declarations; 10 | mod expressions; 11 | mod fragments; 12 | mod items; 13 | mod program; 14 | mod statements; 15 | mod token; 16 | -------------------------------------------------------------------------------- /crates/psl/src/ast/node_id.rs: -------------------------------------------------------------------------------- 1 | #[derive(Hash, PartialEq, Eq)] 2 | pub struct NodeId(usize); 3 | 4 | impl NodeId { 5 | const DUMMY: NodeId = NodeId(usize::MAX); 6 | 7 | pub fn is_dummy(&self) -> bool { 8 | self == &NodeId::DUMMY 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /crates/psl/src/ast/program.rs: -------------------------------------------------------------------------------- 1 | use super::Item; 2 | 3 | #[derive(Clone, Debug, Hash, PartialEq)] 4 | pub struct Program { 5 | pub items: Vec, 6 | } 7 | -------------------------------------------------------------------------------- /crates/psl/src/ast/statements.rs: -------------------------------------------------------------------------------- 1 | use super::{Block, Declaration, Expression, FormatSpecifierFragment, Token}; 2 | 3 | #[derive(Clone, Debug, Hash, PartialEq)] 4 | pub enum Statement { 5 | Declaration(Declaration), 6 | Write(WriteStatement), 7 | While(WhileStatement), 8 | CompoundAssignment(CompoundAssignmentStatement), 9 | Expression(Expression), 10 | } 11 | 12 | #[derive(Clone, Debug, Hash, PartialEq)] 13 | pub struct WriteStatement { 14 | pub fragments: Vec, 15 | } 16 | 17 | #[derive(Clone, Debug, Hash, PartialEq)] 18 | pub struct WhileStatement { 19 | pub condition: Expression, 20 | pub block: Block, 21 | } 22 | 23 | #[derive(Clone, Debug, Hash, PartialEq)] 24 | pub enum CompoundAssignmentStatement { 25 | Simple(Assignment), 26 | } 27 | 28 | #[derive(Clone, Debug, Hash, PartialEq)] 29 | pub struct Assignment { 30 | pub name: Token, 31 | pub expr: Expression, 32 | } 33 | -------------------------------------------------------------------------------- /crates/psl/src/ast/token.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt::{self, Write}, 3 | ops::Range, 4 | }; 5 | 6 | #[derive(Clone, Debug, Hash, PartialEq)] 7 | pub enum TokenKind { 8 | WhitespaceHorizontal, 9 | WhitespaceVertical, 10 | 11 | IdentifierIdentifier, 12 | 13 | LiteralIntegerDecimal, 14 | LiteralIntegerHexadecimal, 15 | LiteralIntegerBinary, 16 | LiteralFormatSpecifier(FormatSpecifier), 17 | 18 | // please sort keyword alphabetically 19 | KeywordElse, 20 | KeywordFn, 21 | KeywordIf, 22 | KeywordRead, 23 | KeywordReturn, 24 | KeywordWhile, 25 | KeywordWrite, 26 | 27 | // please sort punctuation by its ascii code 28 | // please name enum variant Punctuation (PascalCase-d unicode name[1]) 29 | // [1]: https://www.unicode.org/charts/PDF/U0000.pdf 30 | PunctuationExclamationMark, 31 | PunctuationNumberSign, 32 | PunctuationDollarSign, 33 | PunctuationPercentSign, 34 | PunctuationAmpersand, 35 | PunctuationLeftParenthesis, 36 | PunctuationRightParenthesis, 37 | PunctuationAsterisk, 38 | PunctuationPlusSign, 39 | PunctuationComma, 40 | PunctuationHyphenMinus, 41 | PunctuationFullStop, 42 | PunctuationSolidus, 43 | PunctuationColon, 44 | PunctuationSemicolon, 45 | PunctuationLessThanSign, 46 | PunctuationEqualsSign, 47 | PunctuationGreaterThanSign, 48 | PunctuationQuestionMark, 49 | PunctuationCommercialAt, 50 | PunctuationLeftSquareBracket, 51 | PunctuationReverseSolidus, 52 | PunctuationRightSquareBracket, 53 | PunctuationCircumflexAccent, 54 | PunctuationLowLine, 55 | PunctuationLeftCurlyBracket, 56 | PunctuationVerticalLine, 57 | PunctuationRightCurlyBracket, 58 | PunctuationTilde, 59 | 60 | Error, 61 | Eof, 62 | } 63 | 64 | #[derive(Clone, Debug, Hash, PartialEq)] 65 | pub struct Token { 66 | pub kind: TokenKind, 67 | pub content: String, 68 | pub span: Range, 69 | } 70 | 71 | #[derive(Clone, Debug, Hash, PartialEq)] 72 | pub struct FormatSpecifier(pub Vec); 73 | 74 | #[derive(Clone, Debug, Hash, PartialEq)] 75 | pub enum FormatSpecifierFragment { 76 | Text(String), 77 | Whitespace(String), 78 | Variable(String), 79 | } 80 | 81 | impl fmt::Display for FormatSpecifierFragment { 82 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 83 | match self { 84 | FormatSpecifierFragment::Text(s) => s.fmt(f), 85 | FormatSpecifierFragment::Whitespace(s) => s.fmt(f), 86 | FormatSpecifierFragment::Variable(s) => { 87 | f.write_char('{')?; 88 | s.fmt(f)?; 89 | f.write_char('}')?; 90 | Ok(()) 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/construct/mod.rs: -------------------------------------------------------------------------------- 1 | pub use r#type::*; 2 | pub use scope::*; 3 | 4 | mod scope; 5 | mod r#type; 6 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/construct/scope.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, collections::HashMap, rc::Rc}; 2 | 3 | use crate::codegen::pass::NamesResolved; 4 | 5 | use super::Type; 6 | 7 | pub struct Scope { 8 | root: Rc>, 9 | 10 | parent: Option, 11 | 12 | variable_names: HashMap, 13 | } 14 | 15 | impl Scope { 16 | pub fn new(root: Rc>) -> Scope { 17 | Scope { 18 | root, 19 | parent: None, 20 | variable_names: HashMap::new(), 21 | } 22 | } 23 | 24 | pub fn with_parentt(root: Rc>, parent: usize) -> Scope { 25 | Scope { 26 | root, 27 | parent: Some(parent), 28 | 29 | variable_names: HashMap::new(), 30 | } 31 | } 32 | 33 | pub fn put_variable(&mut self, name: String, ty: Type) { 34 | self.variable_names.insert(name, ty); 35 | } 36 | 37 | pub fn get_variable_type(&self, name: &String) -> Option { 38 | self.variable_names.get(name).cloned().or_else(|| { 39 | self.parent 40 | .and_then(|parent| self.root.borrow().vec[parent].get_variable_type(name)) 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/construct/type.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Write}; 2 | 3 | use crate::ast; 4 | 5 | #[derive(Clone, Debug, PartialEq)] 6 | pub enum Type { 7 | I32, 8 | I64, 9 | Integer, 10 | Bool, 11 | Tuple(Vec), 12 | Function { 13 | parameters: Vec, 14 | returning: Box, 15 | }, 16 | Never, 17 | } 18 | 19 | impl TryFrom for Type { 20 | type Error = String; 21 | 22 | fn try_from(value: ast::Type) -> Result { 23 | match value { 24 | ast::Type::Simple(token) => match token.content.as_ref() { 25 | "i32" => Ok(Type::I32), 26 | "i64" => Ok(Type::I64), 27 | "bool" => Ok(Type::Bool), 28 | "tuple0" => Ok(Type::Tuple(Vec::new())), 29 | _ => Err(format!("THere is no type named {:?}", &token.content)), 30 | }, 31 | } 32 | } 33 | } 34 | 35 | impl fmt::Display for Type { 36 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 37 | match self { 38 | Type::I32 => write!(f, "i32"), 39 | Type::I64 => write!(f, "i64"), 40 | Type::Integer => write!(f, "{{integer}}"), 41 | Type::Bool => write!(f, "bool"), 42 | Type::Tuple(vec) => write!( 43 | f, 44 | "({})", 45 | vec.iter() 46 | .map(|ty| ty.to_string()) 47 | .collect::>() 48 | .join(", ") 49 | ), 50 | Type::Function { 51 | parameters, 52 | returning, 53 | } => { 54 | f.write_char('(')?; 55 | for (idx, param) in parameters.iter().enumerate() { 56 | if idx != 0 { 57 | f.write_str(", ")?; 58 | } 59 | param.fmt(f)?; 60 | } 61 | f.write_str(") -> ")?; 62 | returning.fmt(f)?; 63 | Ok(()) 64 | } 65 | Type::Never => write!(f, "!"), 66 | } 67 | } 68 | } 69 | 70 | impl Type { 71 | pub const UNIT: Type = Type::Tuple(Vec::new()); 72 | 73 | pub fn as_c_type(&self) -> String { 74 | match self { 75 | Type::I32 => "i32".to_string(), 76 | Type::I64 => "i64".to_string(), 77 | Type::Integer => Type::I32.as_c_type(), 78 | Type::Bool => "bool".to_string(), 79 | Type::Tuple(_) => "tuple0".to_string(), 80 | Type::Function { .. } => todo!("function type is not supported now"), 81 | Type::Never { .. } => todo!("never type is not supported now"), 82 | } 83 | } 84 | 85 | pub fn union_with(self, other: Type) -> Result { 86 | if self == other { 87 | return Ok(self); 88 | } 89 | match (&self, &other) { 90 | (Type::I32, Type::Integer) | (Type::Integer, Type::I32) => Ok(Type::I32), 91 | (Type::I64, Type::Integer) | (Type::Integer, Type::I64) => Ok(Type::I64), 92 | _ => Err(format!("Type {} is not compatible with {}", self, other)), 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/context.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::{Ref, RefCell}, 3 | fmt::Debug, 4 | hash::Hash, 5 | rc::Rc, 6 | }; 7 | 8 | use super::{construct::Scope, pass::NamesResolved}; 9 | 10 | pub struct CodegenContext { 11 | functions: Vec, 12 | name_resolution: Rc>, 13 | } 14 | 15 | pub struct Function { 16 | pub forward_decl: String, 17 | pub decl: String, 18 | } 19 | 20 | impl CodegenContext { 21 | pub fn new(resolution: Rc>) -> CodegenContext { 22 | CodegenContext { 23 | functions: Vec::new(), 24 | name_resolution: resolution, 25 | } 26 | } 27 | 28 | pub fn scope(&self, node: &T) -> Ref { 29 | Ref::map(self.name_resolution.borrow(), |root| { 30 | root.get(node).expect(&format!("{node:?} must have scope")) 31 | }) 32 | } 33 | 34 | pub fn add_function(&mut self, f: Function) { 35 | self.functions.push(f) 36 | } 37 | 38 | pub fn func_forward_decls(&self) -> impl Iterator { 39 | self.functions.iter().map(|f| &f.forward_decl) 40 | } 41 | 42 | pub fn func_decls(&self) -> impl Iterator { 43 | self.functions.iter().map(|f| &f.decl) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/declarations/function_declaration.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::{ExpressionOrBlock, FunctionDeclaration}, 3 | codegen::{ 4 | construct::Type, 5 | context::{CodegenContext, Function}, 6 | pass::{NameResolutionContext, NameResolutionPass}, 7 | visitor::CodegenNode, 8 | }, 9 | }; 10 | 11 | impl CodegenNode for FunctionDeclaration { 12 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 13 | let return_type = Type::try_from(self.return_type).unwrap(); 14 | 15 | let mut forward_decl = String::new(); 16 | let mut decl = String::new(); 17 | 18 | forward_decl.push_str(&return_type.as_c_type()); 19 | forward_decl.push(' '); 20 | forward_decl.push_str(&self.name.content); 21 | forward_decl.push('('); 22 | for (i, (param_ty, param_name)) in self.parameters.iter().enumerate() { 23 | if i != 0 { 24 | forward_decl.push_str(", "); 25 | } 26 | forward_decl.push_str(&Type::try_from(param_ty.clone()).unwrap().as_c_type()); 27 | forward_decl.push(' '); 28 | forward_decl.push_str(¶m_name.content); 29 | } 30 | forward_decl.push(')'); 31 | 32 | decl.push_str(&forward_decl); 33 | decl.push_str(" {\n"); 34 | 35 | match self.body { 36 | ExpressionOrBlock::Expression(expr) => { 37 | decl.push_str("return ("); 38 | decl.push_str(&ctx.visit(expr)); 39 | decl.push_str(");\n"); 40 | } 41 | ExpressionOrBlock::Block(block) => { 42 | for statement in block.statements { 43 | decl.push_str(&ctx.visit(statement)); 44 | } 45 | match block.last_expression { 46 | Some(last_expr) => { 47 | if !matches!(last_expr.infer_type(ctx).unwrap(), Type::Never) { 48 | decl.push_str("return "); 49 | } 50 | decl.push_str(&ctx.visit(last_expr)); 51 | decl.push_str(";\n"); 52 | } 53 | None => decl.push_str("return ({});\n"), 54 | } 55 | } 56 | } 57 | decl.push('}'); 58 | 59 | ctx.add_function(Function { 60 | forward_decl: format!("{forward_decl};"), 61 | decl, 62 | }); 63 | 64 | "".to_owned() 65 | } 66 | } 67 | 68 | impl NameResolutionPass for FunctionDeclaration { 69 | fn resolve(&self, ctx: &mut NameResolutionContext) { 70 | let ty = Type::Function { 71 | parameters: self 72 | .parameters 73 | .iter() 74 | .map(|(ty, _)| Type::try_from(ty.clone()).unwrap()) 75 | .collect(), 76 | returning: Box::new(Type::try_from(self.return_type.clone()).unwrap()), 77 | }; 78 | ctx.scope_mut().put_variable(self.name.content.clone(), ty); 79 | ctx.create_subscope().visit(&self.body); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/declarations/mod.rs: -------------------------------------------------------------------------------- 1 | mod function_declaration; 2 | mod variable_declaration; 3 | 4 | use crate::{ 5 | ast::Declaration, 6 | codegen::{ 7 | context::CodegenContext, 8 | pass::{NameResolutionContext, NameResolutionPass}, 9 | visitor::CodegenNode, 10 | }, 11 | }; 12 | 13 | impl CodegenNode for Declaration { 14 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 15 | match self { 16 | Declaration::Variable(node) => ctx.visit(node), 17 | Declaration::Function(node) => ctx.visit(node), 18 | } 19 | } 20 | } 21 | 22 | impl NameResolutionPass for Declaration { 23 | fn resolve(&self, ctx: &mut NameResolutionContext) { 24 | match self { 25 | Declaration::Variable(node) => ctx.visit(node), 26 | Declaration::Function(node) => ctx.visit(node), 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/declarations/variable_declaration.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::VariableDeclaration, 3 | codegen::{ 4 | construct::Type, 5 | context::CodegenContext, 6 | pass::{NameResolutionContext, NameResolutionPass}, 7 | visitor::CodegenNode, 8 | }, 9 | }; 10 | 11 | impl CodegenNode for VariableDeclaration { 12 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 13 | let ty = Type::try_from(self.ty).unwrap(); 14 | let mut output = String::new(); 15 | 16 | output.push_str(&ty.as_c_type()); 17 | output.push(' '); 18 | output.push_str(&self.name.content); 19 | 20 | if let Some(node) = self.value { 21 | output.push('='); 22 | output.push_str(&ctx.visit(node)) 23 | } 24 | 25 | output.push_str(";\n"); 26 | 27 | output 28 | } 29 | } 30 | 31 | impl NameResolutionPass for VariableDeclaration { 32 | fn resolve(&self, ctx: &mut NameResolutionContext) { 33 | let ty = Type::try_from(self.ty.clone()).unwrap(); 34 | ctx.scope_mut().put_variable(self.name.content.clone(), ty); 35 | ctx.visit(&self.value); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/expressions/binary_operator.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::{BinaryOperator, BinaryOperatorExpression}, 3 | codegen::{ 4 | construct::Type, 5 | context::CodegenContext, 6 | pass::{NameResolutionContext, NameResolutionPass}, 7 | visitor::CodegenNode, 8 | }, 9 | }; 10 | 11 | impl CodegenNode for BinaryOperatorExpression { 12 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 13 | let mut output = String::new(); 14 | 15 | output.push('('); 16 | output.push_str(&ctx.visit(self.lhs)); 17 | output.push(')'); 18 | 19 | /* 20 | * @TODO: 21 | * when we introduce operator overloading, we should change this. 22 | */ 23 | let operator = match self.operator { 24 | BinaryOperator::Add => "+", 25 | BinaryOperator::Subtract => "-", 26 | BinaryOperator::Multiply => "*", 27 | BinaryOperator::Divide => "/", 28 | BinaryOperator::Modulus => "%", 29 | BinaryOperator::Equal => "==", 30 | BinaryOperator::NotEqual => "!=", 31 | BinaryOperator::Less => "<", 32 | BinaryOperator::Greater => ">", 33 | BinaryOperator::LessEqual => "<=", 34 | BinaryOperator::GreaterEqual => "<=", 35 | BinaryOperator::LogiacalAnd => "&&", 36 | BinaryOperator::LogicalOr => "||", 37 | }; 38 | 39 | output.push_str(operator); 40 | 41 | output.push('('); 42 | output.push_str(&ctx.visit(self.rhs)); 43 | output.push(')'); 44 | 45 | output 46 | } 47 | } 48 | 49 | impl NameResolutionPass for BinaryOperatorExpression { 50 | fn resolve(&self, ctx: &mut NameResolutionContext) { 51 | ctx.visit(&self.lhs); 52 | ctx.visit(&self.rhs); 53 | } 54 | } 55 | 56 | impl BinaryOperatorExpression { 57 | /** 58 | * @TODO: 59 | * when we introduce operator overloading, we should change this. 60 | */ 61 | pub fn infer_type(&self, ctx: &CodegenContext) -> Result { 62 | match self.operator { 63 | BinaryOperator::Add 64 | | BinaryOperator::Subtract 65 | | BinaryOperator::Multiply 66 | | BinaryOperator::Divide 67 | | BinaryOperator::Modulus => self.lhs.infer_type(ctx), 68 | 69 | BinaryOperator::Equal 70 | | BinaryOperator::NotEqual 71 | | BinaryOperator::Less 72 | | BinaryOperator::Greater 73 | | BinaryOperator::LessEqual 74 | | BinaryOperator::GreaterEqual 75 | | BinaryOperator::LogiacalAnd 76 | | BinaryOperator::LogicalOr => Ok(Type::Bool), 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/expressions/if.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::IfExpression, 3 | codegen::{ 4 | construct::Type, 5 | context::CodegenContext, 6 | pass::{NameResolutionContext, NameResolutionPass}, 7 | visitor::CodegenNode, 8 | }, 9 | }; 10 | 11 | impl CodegenNode for IfExpression { 12 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 13 | let mut output = String::new(); 14 | 15 | output.push('('); 16 | output.push_str(&ctx.visit(self.condition)); 17 | output.push_str(") ? ("); 18 | output.push_str(&ctx.visit(self.positive)); 19 | output.push_str(") : ("); 20 | match self.negative { 21 | Some(negative) => output.push_str(&ctx.visit(negative)), 22 | None => output.push_str("{}"), 23 | }; 24 | output.push(')'); 25 | 26 | output 27 | } 28 | } 29 | 30 | impl NameResolutionPass for IfExpression { 31 | fn resolve(&self, ctx: &mut NameResolutionContext) { 32 | ctx.visit(&self.condition); 33 | 34 | ctx.create_subscope().visit(&self.positive); 35 | ctx.create_subscope().visit(&self.negative); 36 | } 37 | } 38 | 39 | impl IfExpression { 40 | pub fn infer_type(&self, ctx: &CodegenContext) -> Result { 41 | let positive = self.positive.infer_type(ctx)?; 42 | let negative = self 43 | .negative 44 | .as_ref() 45 | .map(|negative| negative.infer_type(ctx)) 46 | .transpose()? 47 | .unwrap_or(Type::UNIT); 48 | 49 | positive.union_with(negative) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/expressions/literal.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::{LiteralExpression, TokenKind}, 3 | codegen::{ 4 | construct::Type, 5 | context::CodegenContext, 6 | pass::{NameResolutionContext, NameResolutionPass}, 7 | visitor::CodegenNode, 8 | }, 9 | }; 10 | 11 | impl CodegenNode for LiteralExpression { 12 | fn produce_code(self, _ctx: &mut CodegenContext) -> String { 13 | let digit_only = self.value.content.replace('_', ""); 14 | match self.value.kind { 15 | TokenKind::LiteralIntegerDecimal => digit_only, 16 | TokenKind::LiteralIntegerHexadecimal => format!("0x{digit_only}"), 17 | TokenKind::LiteralIntegerBinary => { 18 | let mut binary = String::from("0x"); 19 | let mut buffer = 0; 20 | let mut nibble_len = digit_only.chars().count() % 4; 21 | for bit in digit_only.chars() { 22 | buffer <<= 1; 23 | if bit == '1' { 24 | buffer |= 1; 25 | } 26 | nibble_len -= 1; 27 | if nibble_len == 0 { 28 | binary.push(to_hex_digit(buffer)); 29 | } 30 | } 31 | binary 32 | } 33 | _ => unreachable!( 34 | "Invalid TokenKind for LiteralExpression: {:?}", 35 | self.value.kind 36 | ), 37 | } 38 | } 39 | } 40 | impl NameResolutionPass for LiteralExpression { 41 | fn resolve(&self, _ctx: &mut NameResolutionContext) {} 42 | } 43 | 44 | impl LiteralExpression { 45 | pub fn infer_type(&self, _ctx: &CodegenContext) -> Result { 46 | match &self.value.kind { 47 | TokenKind::LiteralIntegerDecimal 48 | | TokenKind::LiteralIntegerHexadecimal 49 | | TokenKind::LiteralIntegerBinary => Ok(Type::Integer), 50 | _ => unreachable!( 51 | "Invalid TokenKind for LiteralExpression: {:?}", 52 | self.value.kind 53 | ), 54 | } 55 | } 56 | } 57 | 58 | fn to_hex_digit(number: i32) -> char { 59 | match number { 60 | 0 => '0', 61 | 1 => '1', 62 | 2 => '2', 63 | 3 => '3', 64 | 4 => '4', 65 | 5 => '5', 66 | 6 => '6', 67 | 7 => '7', 68 | 8 => '8', 69 | 9 => '9', 70 | 10 => 'A', 71 | 11 => 'B', 72 | 12 => 'C', 73 | 13 => 'D', 74 | 14 => 'E', 75 | 15 => 'F', 76 | _ => unreachable!("No matching hex digit for {number}"), 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/expressions/mod.rs: -------------------------------------------------------------------------------- 1 | mod binary_operator; 2 | mod r#if; 3 | mod literal; 4 | mod name; 5 | mod postfix_operator; 6 | mod read; 7 | mod r#return; 8 | 9 | use crate::{ 10 | ast::Expression, 11 | codegen::{ 12 | construct::Type, 13 | context::CodegenContext, 14 | pass::{NameResolutionContext, NameResolutionPass}, 15 | visitor::CodegenNode, 16 | }, 17 | }; 18 | 19 | impl CodegenNode for Expression { 20 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 21 | match self { 22 | Expression::Literal(node) => ctx.visit(node), 23 | Expression::Read(node) => ctx.visit(node), 24 | Expression::Return(node) => ctx.visit(node), 25 | Expression::Name(node) => ctx.visit(node), 26 | Expression::If(node) => ctx.visit(node), 27 | Expression::BinaryOperator(node) => ctx.visit(node), 28 | Expression::PostfixOperator(node) => ctx.visit(node), 29 | } 30 | } 31 | } 32 | 33 | impl NameResolutionPass for Expression { 34 | fn resolve(&self, ctx: &mut NameResolutionContext) { 35 | match self { 36 | Expression::Literal(node) => ctx.visit(node), 37 | Expression::Read(node) => ctx.visit(node), 38 | Expression::Return(node) => ctx.visit(node), 39 | Expression::Name(node) => ctx.visit(node), 40 | Expression::If(node) => ctx.visit(node), 41 | Expression::BinaryOperator(node) => ctx.visit(node), 42 | Expression::PostfixOperator(node) => ctx.visit(node), 43 | } 44 | } 45 | } 46 | 47 | impl Expression { 48 | pub fn infer_type(&self, ctx: &CodegenContext) -> Result { 49 | match self { 50 | Expression::Literal(expr) => expr.infer_type(ctx), 51 | Expression::Read(expr) => expr.infer_type(ctx), 52 | Expression::Return(expr) => expr.infer_type(ctx), 53 | Expression::Name(expr) => expr.infer_type(ctx), 54 | Expression::If(expr) => expr.infer_type(ctx), 55 | Expression::BinaryOperator(expr) => expr.infer_type(ctx), 56 | Expression::PostfixOperator(expr) => expr.infer_type(ctx), 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/expressions/name.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::NameExpression, 3 | codegen::{ 4 | construct::Type, 5 | context::CodegenContext, 6 | pass::{NameResolutionContext, NameResolutionPass}, 7 | visitor::CodegenNode, 8 | }, 9 | }; 10 | 11 | impl CodegenNode for NameExpression { 12 | fn produce_code(self, _ctx: &mut CodegenContext) -> String { 13 | self.name.content 14 | } 15 | } 16 | 17 | impl NameResolutionPass for NameExpression { 18 | fn resolve(&self, _ctx: &mut NameResolutionContext) {} 19 | } 20 | 21 | impl NameExpression { 22 | pub fn infer_type(&self, ctx: &CodegenContext) -> Result { 23 | ctx.scope(self) 24 | .get_variable_type(&self.name.content) 25 | .ok_or_else(|| format!("There is no variable named {:?}", self.name.content)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/expressions/postfix_operator.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::{PostfixOperator, PostfixOperatorExpression}, 3 | codegen::{ 4 | construct::Type, 5 | context::CodegenContext, 6 | pass::{NameResolutionContext, NameResolutionPass}, 7 | visitor::CodegenNode, 8 | }, 9 | }; 10 | 11 | impl CodegenNode for PostfixOperatorExpression { 12 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 13 | match self.operator { 14 | PostfixOperator::Invoke(arguments) => { 15 | let mut output = String::new(); 16 | output.push_str(&ctx.visit(self.expr)); 17 | output.push('('); 18 | for (i, arg) in arguments.into_iter().enumerate() { 19 | if i != 0 { 20 | output.push_str(", "); 21 | } 22 | output.push_str(&ctx.visit(arg)); 23 | } 24 | output.push(')'); 25 | 26 | output 27 | } 28 | PostfixOperator::Index(_) => todo!(), 29 | } 30 | } 31 | } 32 | 33 | impl NameResolutionPass for PostfixOperatorExpression { 34 | fn resolve(&self, ctx: &mut NameResolutionContext) { 35 | ctx.visit(&self.expr); 36 | match &self.operator { 37 | PostfixOperator::Invoke(arguments) => { 38 | for arg in arguments { 39 | ctx.create_subscope().visit(arg); 40 | } 41 | } 42 | PostfixOperator::Index(_) => todo!(), 43 | } 44 | } 45 | } 46 | 47 | impl PostfixOperatorExpression { 48 | /** 49 | * @TODO: 50 | * when we introduce operator overloading, we should change this. 51 | */ 52 | pub fn infer_type(&self, ctx: &CodegenContext) -> Result { 53 | match &self.operator { 54 | PostfixOperator::Invoke(_) => { 55 | let Ok(Type::Function { returning, .. }) = self.expr.infer_type(ctx) else { 56 | Err("you can only invoke function-typed variables")? 57 | }; 58 | Ok(*returning.clone()) 59 | } 60 | PostfixOperator::Index(_) => todo!(), 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/expressions/read.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::ReadExpression, 3 | codegen::{ 4 | construct::Type, 5 | context::CodegenContext, 6 | pass::{NameResolutionContext, NameResolutionPass}, 7 | visitor::CodegenNode, 8 | }, 9 | }; 10 | 11 | impl CodegenNode for ReadExpression { 12 | fn produce_code(self, _ctx: &mut CodegenContext) -> String { 13 | match self { 14 | ReadExpression::Type(ty) => { 15 | let ty = Type::try_from(ty).unwrap(); 16 | format!("__read_{}(read_buf)", ty.as_c_type()) 17 | } 18 | } 19 | } 20 | } 21 | 22 | impl NameResolutionPass for ReadExpression { 23 | fn resolve(&self, _ctx: &mut NameResolutionContext) {} 24 | } 25 | 26 | impl ReadExpression { 27 | pub fn infer_type(&self, _ctx: &CodegenContext) -> Result { 28 | match self { 29 | ReadExpression::Type(ty) => ty.clone().try_into(), 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/expressions/return.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::ReturnExpression, 3 | codegen::{ 4 | construct::Type, 5 | context::CodegenContext, 6 | pass::{NameResolutionContext, NameResolutionPass}, 7 | visitor::CodegenNode, 8 | }, 9 | }; 10 | 11 | impl CodegenNode for ReturnExpression { 12 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 13 | if self.value.infer_type(ctx).unwrap() == Type::Never { 14 | ctx.visit(self.value) 15 | } else { 16 | format!("return ({})", ctx.visit(self.value)) 17 | } 18 | } 19 | } 20 | 21 | impl NameResolutionPass for ReturnExpression { 22 | fn resolve(&self, ctx: &mut NameResolutionContext) { 23 | ctx.visit(&self.value); 24 | } 25 | } 26 | 27 | impl ReturnExpression { 28 | pub fn infer_type(&self, _: &CodegenContext) -> Result { 29 | Ok(Type::Never) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/fragments/expression_or_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::ExpressionOrBlock, 3 | codegen::{ 4 | construct::Type, 5 | context::CodegenContext, 6 | pass::{NameResolutionContext, NameResolutionPass}, 7 | visitor::CodegenNode, 8 | }, 9 | }; 10 | 11 | impl CodegenNode for ExpressionOrBlock { 12 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 13 | match self { 14 | ExpressionOrBlock::Expression(expr) => ctx.visit(expr), 15 | ExpressionOrBlock::Block(block) => { 16 | let mut block_output = String::new(); 17 | block_output.push_str("{\n"); 18 | for statement in block.statements { 19 | block_output.push_str(&ctx.visit(statement)); 20 | } 21 | match block.last_expression { 22 | Some(last_expr) => block_output.push_str(&ctx.visit(last_expr)), 23 | None => block_output.push_str("{}"), 24 | } 25 | block_output.push_str(";\n}"); 26 | block_output 27 | } 28 | } 29 | } 30 | } 31 | 32 | impl NameResolutionPass for ExpressionOrBlock { 33 | fn resolve(&self, ctx: &mut NameResolutionContext) { 34 | match self { 35 | ExpressionOrBlock::Expression(node) => ctx.visit(node), 36 | ExpressionOrBlock::Block(block) => { 37 | for statement in &block.statements { 38 | ctx.visit(statement); 39 | } 40 | ctx.visit(&block.last_expression) 41 | } 42 | } 43 | } 44 | } 45 | 46 | impl ExpressionOrBlock { 47 | pub fn infer_type(&self, ctx: &CodegenContext) -> Result { 48 | match self { 49 | ExpressionOrBlock::Expression(expr) => expr.infer_type(ctx), 50 | ExpressionOrBlock::Block(block) => Ok(block 51 | .last_expression 52 | .as_ref() 53 | .map(|expr| expr.infer_type(ctx)) 54 | .transpose()? 55 | .unwrap_or(Type::UNIT)), 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/fragments/mod.rs: -------------------------------------------------------------------------------- 1 | mod expression_or_block; 2 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/item.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::Item, 3 | codegen::{ 4 | context::CodegenContext, 5 | pass::{NameResolutionContext, NameResolutionPass}, 6 | visitor::CodegenNode, 7 | }, 8 | }; 9 | 10 | impl CodegenNode for Item { 11 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 12 | match self { 13 | Item::Declaration(node) => ctx.visit(node), 14 | Item::Statement(node) => ctx.visit(node), 15 | } 16 | } 17 | } 18 | 19 | impl NameResolutionPass for Item { 20 | fn resolve(&self, ctx: &mut NameResolutionContext) { 21 | match self { 22 | Item::Declaration(node) => ctx.visit(node), 23 | Item::Statement(node) => ctx.visit(node), 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/mod.rs: -------------------------------------------------------------------------------- 1 | mod declarations; 2 | mod expressions; 3 | mod fragments; 4 | mod item; 5 | mod program; 6 | mod statements; 7 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/program.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::Program, 3 | codegen::{ 4 | context::CodegenContext, 5 | pass::{NameResolutionContext, NameResolutionPass}, 6 | visitor::CodegenNode, 7 | }, 8 | }; 9 | 10 | macro_rules! include_rt { 11 | ($output:expr, $file:literal) => { 12 | $output.push_str(concat!("// ", $file, "\n")); 13 | $output.push_str(&remove_c_preprocessor_codes(include_str!(concat!( 14 | "../rt/", $file 15 | )))); 16 | $output.push_str("\n\n") 17 | }; 18 | } 19 | 20 | fn remove_c_preprocessor_codes(s: impl AsRef) -> String { 21 | s.as_ref() 22 | .split('\n') 23 | .filter(|s| !s.is_empty() && &s[0..1] != "#") // do not support C preprocessor at all 24 | .collect::>() 25 | .join("\n") 26 | .trim() 27 | .to_owned() 28 | } 29 | 30 | impl CodegenNode for Program { 31 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 32 | let mut main_body = String::new(); 33 | 34 | for item in self.items { 35 | main_body.push_str(&ctx.visit(item)); 36 | } 37 | 38 | let mut output = String::new(); 39 | output.push_str( 40 | &include_str!("../rt/header.h") 41 | .replace("{{CARGO_PKG_VERSION}}", env!("CARGO_PKG_VERSION")), 42 | ); 43 | output.push_str("#include \n"); 44 | output.push_str("#include \n"); 45 | output.push_str("#include \n"); 46 | output.push_str("#include \n"); 47 | output.push('\n'); 48 | include_rt!(output, "typedef.h"); 49 | include_rt!(output, "write.h"); 50 | include_rt!(output, "panic.h"); 51 | include_rt!(output, "read.h"); 52 | 53 | for f in ctx.func_forward_decls() { 54 | output.push_str(&f); 55 | } 56 | 57 | output.push_str("int main() {}; int __libc_start_main() {\n"); 58 | output.push_str("c8 read_buf[READ_BUF_LEN];\n"); 59 | output.push_str("c8 write_buf[WRITE_BUF_LEN];\n"); 60 | 61 | output.push_str(&main_body); 62 | 63 | output.push_str("__flush(write_buf);\n"); 64 | output.push_str("_Exit(0);\n"); 65 | output.push_str("}\n"); 66 | 67 | for f in ctx.func_decls() { 68 | output.push_str(&f); 69 | } 70 | 71 | output 72 | } 73 | } 74 | 75 | impl NameResolutionPass for Program { 76 | fn resolve(&self, ctx: &mut NameResolutionContext) { 77 | for item in &self.items { 78 | ctx.visit(item) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/statements/assignment.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::CompoundAssignmentStatement, 3 | codegen::{ 4 | context::CodegenContext, 5 | pass::{NameResolutionContext, NameResolutionPass}, 6 | visitor::CodegenNode, 7 | }, 8 | }; 9 | 10 | impl CodegenNode for CompoundAssignmentStatement { 11 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 12 | let (assignment, operator) = match self { 13 | CompoundAssignmentStatement::Simple(assignment) => (assignment, "="), 14 | }; 15 | 16 | format!( 17 | "{} {} {};\n", 18 | assignment.name.content, 19 | operator, 20 | ctx.visit(assignment.expr) 21 | ) 22 | } 23 | } 24 | 25 | impl NameResolutionPass for CompoundAssignmentStatement { 26 | fn resolve(&self, _ctx: &mut NameResolutionContext) {} 27 | } 28 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/statements/mod.rs: -------------------------------------------------------------------------------- 1 | mod assignment; 2 | mod r#while; 3 | mod write; 4 | 5 | use crate::{ 6 | ast::Statement, 7 | codegen::{ 8 | pass::{NameResolutionContext, NameResolutionPass}, 9 | visitor::CodegenNode, 10 | }, 11 | }; 12 | 13 | impl CodegenNode for Statement { 14 | fn produce_code(self, ctx: &mut crate::codegen::context::CodegenContext) -> String { 15 | match self { 16 | Statement::Declaration(node) => ctx.visit(node), 17 | Statement::Write(node) => ctx.visit(node), 18 | Statement::While(node) => ctx.visit(node), 19 | Statement::CompoundAssignment(node) => ctx.visit(node), 20 | Statement::Expression(node) => format!("{};", ctx.visit(node)), 21 | } 22 | } 23 | } 24 | 25 | impl NameResolutionPass for Statement { 26 | fn resolve(&self, ctx: &mut NameResolutionContext) { 27 | match self { 28 | Statement::Declaration(node) => ctx.visit(node), 29 | Statement::Write(node) => ctx.visit(node), 30 | Statement::While(node) => ctx.visit(node), 31 | Statement::CompoundAssignment(node) => ctx.visit(node), 32 | Statement::Expression(node) => ctx.visit(node), 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/statements/while.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::WhileStatement, 3 | codegen::{ 4 | context::CodegenContext, 5 | pass::{NameResolutionContext, NameResolutionPass}, 6 | visitor::CodegenNode, 7 | }, 8 | }; 9 | 10 | impl CodegenNode for WhileStatement { 11 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 12 | let mut output = String::new(); 13 | 14 | output.push_str("while ("); 15 | output.push_str(&ctx.visit(self.condition)); 16 | output.push_str(") {\n"); 17 | for statement in self.block.statements { 18 | output.push_str(&ctx.visit(statement)); 19 | } 20 | if let Some(last_expression) = self.block.last_expression { 21 | output.push_str(&format!("{};\n", ctx.visit(last_expression))); 22 | } 23 | output.push_str("}\n"); 24 | 25 | output 26 | } 27 | } 28 | 29 | impl NameResolutionPass for WhileStatement { 30 | fn resolve(&self, ctx: &mut NameResolutionContext) { 31 | let mut scope = ctx.create_subscope(); 32 | scope.visit(&self.condition); 33 | for statement in &self.block.statements { 34 | ctx.visit(statement); 35 | } 36 | ctx.visit(&self.block.last_expression) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/impls/statements/write.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast::{FormatSpecifierFragment, WriteStatement}, 3 | codegen::{ 4 | context::CodegenContext, 5 | pass::{NameResolutionContext, NameResolutionPass}, 6 | visitor::CodegenNode, 7 | }, 8 | }; 9 | 10 | impl CodegenNode for WriteStatement { 11 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 12 | let mut output = String::new(); 13 | for frag in &self.fragments { 14 | match frag { 15 | FormatSpecifierFragment::Text(s) | FormatSpecifierFragment::Whitespace(s) => { 16 | let bytes = s.as_bytes(); 17 | output.push_str(&format!( 18 | "__sys_write(write_buf, \"{}\", {});\n", 19 | String::from_utf8( 20 | bytes 21 | .iter() 22 | .flat_map(|b| std::ascii::escape_default(*b)) 23 | .collect::>() 24 | ) 25 | .unwrap(), 26 | bytes.len() 27 | )); 28 | } 29 | FormatSpecifierFragment::Variable(var) => { 30 | let Some(ty) = ctx.scope(&self).get_variable_type(&var) else { 31 | // @TODO: migrate to diagnostics 32 | panic!("There is no variable named {:?}", var); 33 | }; 34 | 35 | output.push_str(&format!( 36 | "__write_{}(write_buf, {});\n", 37 | ty.as_c_type(), 38 | var 39 | )); 40 | } 41 | } 42 | } 43 | output 44 | } 45 | } 46 | 47 | impl NameResolutionPass for WriteStatement { 48 | fn resolve(&self, _ctx: &mut NameResolutionContext) {} 49 | } 50 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::Program; 2 | 3 | use self::{context::CodegenContext, pass::NameResolutionContext}; 4 | 5 | pub mod construct; 6 | mod context; 7 | mod impls; 8 | mod pass; 9 | mod visitor; 10 | 11 | pub fn generate_codes(ast: Program) -> String { 12 | let mut resolution_ctx = NameResolutionContext::new(); 13 | resolution_ctx.visit(&ast); 14 | 15 | let mut ctx = CodegenContext::new(resolution_ctx.finish()); 16 | ctx.visit(ast) 17 | } 18 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/pass/mod.rs: -------------------------------------------------------------------------------- 1 | pub use name_resolution::*; 2 | 3 | mod name_resolution; 4 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/pass/name_resolution.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any::{Any, TypeId}, 3 | cell::{RefCell, RefMut}, 4 | collections::{hash_map::DefaultHasher, HashMap}, 5 | hash::{Hash, Hasher}, 6 | rc::Rc, 7 | }; 8 | 9 | use crate::codegen::construct::Scope; 10 | 11 | pub trait NameResolutionPass { 12 | fn resolve(&self, ctx: &mut NameResolutionContext); 13 | } 14 | 15 | impl NameResolutionPass for Box { 16 | fn resolve(&self, ctx: &mut NameResolutionContext) { 17 | T::resolve(self, ctx) 18 | } 19 | } 20 | 21 | impl NameResolutionPass for Option { 22 | fn resolve(&self, ctx: &mut NameResolutionContext) { 23 | let Some(value) = self else { return }; 24 | T::resolve(value, ctx) 25 | } 26 | } 27 | 28 | type AstKey = (TypeId, u64); 29 | 30 | pub struct NamesResolved { 31 | pub(crate) vec: Vec, 32 | resolved: HashMap, 33 | } 34 | 35 | impl NamesResolved { 36 | fn make_keys(key: &K) -> AstKey { 37 | let ty = ::type_id(key); 38 | 39 | let mut hasher = DefaultHasher::new(); 40 | key.hash(&mut hasher); 41 | let hash = hasher.finish(); 42 | 43 | (ty, hash) 44 | } 45 | 46 | pub fn insert(&mut self, scope: Scope) -> usize { 47 | self.vec.push(scope); 48 | self.vec.len() - 1 49 | } 50 | 51 | pub fn get(&self, key: &K) -> Option<&Scope> { 52 | self.resolved 53 | .get(&Self::make_keys(key)) 54 | .and_then(|idx| self.vec.get(*idx)) 55 | } 56 | 57 | pub fn set(&mut self, key: &K, idx: usize) { 58 | self.resolved.insert(Self::make_keys(key), idx); 59 | } 60 | } 61 | 62 | pub struct NameResolutionContext { 63 | // as we use single-threaded codegen, there must be no more than 1 mutator. 64 | root: Rc>, 65 | scope: usize, 66 | } 67 | 68 | impl NameResolutionContext { 69 | pub fn new() -> Self { 70 | let root = Rc::new(RefCell::new(NamesResolved { 71 | vec: Vec::new(), 72 | resolved: HashMap::new(), 73 | })); 74 | let scope = root.borrow_mut().insert(Scope::new(Rc::clone(&root))); 75 | 76 | NameResolutionContext { root, scope } 77 | } 78 | 79 | pub fn visit(&mut self, node: &T) { 80 | self.root.borrow_mut().set(node, self.scope); 81 | 82 | node.resolve(self); 83 | } 84 | 85 | pub fn create_subscope(&mut self) -> NameResolutionContext { 86 | let new_scope = self 87 | .root 88 | .borrow_mut() 89 | .insert(Scope::with_parentt(Rc::clone(&self.root), self.scope)); 90 | NameResolutionContext { 91 | root: Rc::clone(&self.root), 92 | scope: new_scope, 93 | } 94 | } 95 | 96 | pub fn scope_mut(&mut self) -> RefMut { 97 | RefMut::map(self.root.borrow_mut(), |root| { 98 | root.vec.get_mut(self.scope).unwrap() 99 | }) 100 | } 101 | 102 | pub fn finish(self) -> Rc> { 103 | self.root 104 | } 105 | } 106 | 107 | impl Default for NameResolutionContext { 108 | fn default() -> Self { 109 | Self::new() 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/rt/header.h: -------------------------------------------------------------------------------- 1 | /* 2 | * /$$$$$$$ /$$$$$$ /$$ 3 | * | $$__ $$ /$$__ $$| $$ 4 | * | $$ \ $$| $$ \__/| $$ 5 | * | $$$$$$$/| $$$$$$ | $$ 6 | * | $$____/ \____ $$| $$ 7 | * | $$ /$$ \ $$| $$ 8 | * | $$ | $$$$$$/| $$$$$$$$ 9 | * |__/ \______/ |________/ 10 | * 11 | * psl {{CARGO_PKG_VERSION}} 12 | * Follow link for more information: 13 | * https://github.com/psl-lang/psl 14 | */ 15 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/rt/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "typedef.h" 4 | #include "write.h" 5 | 6 | i32 __sys_panic(c8 *msg, size_t len) 7 | { 8 | write(STDERR_FILENO, msg, len); 9 | _Exit(1); 10 | return 0; 11 | } 12 | 13 | i32 __sys_todo() 14 | { 15 | return __sys_panic("not implemented yet\n", 20); 16 | } 17 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/rt/read.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "typedef.h" 5 | #include "panic.h" 6 | 7 | const usize READ_BUF_LEN = 1 << 15; 8 | usize read_off = READ_BUF_LEN; 9 | usize read_len = READ_BUF_LEN; 10 | 11 | void __fill_buf(c8 *read_buf) 12 | { 13 | read_off = 0; 14 | read_len = read(STDIN_FILENO, read_buf, READ_BUF_LEN); 15 | } 16 | 17 | c8 __peek_c8(c8 *read_buf) 18 | { 19 | if (read_off >= read_len) 20 | { 21 | __fill_buf(read_buf); 22 | } 23 | return read_buf[read_off]; 24 | } 25 | 26 | c8 __consume_c8(c8 *read_buf) 27 | { 28 | c8 result = __peek_c8(read_buf); 29 | read_off++; 30 | return result; 31 | } 32 | 33 | void __skip_whitespace(c8 *read_buf) 34 | { 35 | c8 peek = __peek_c8(read_buf); 36 | while (peek == ' ' || peek == '\n') 37 | { 38 | __consume_c8(read_buf); 39 | peek = __peek_c8(read_buf); 40 | } 41 | } 42 | 43 | i32 __read_i32(c8 *read_buf) 44 | { 45 | __skip_whitespace(read_buf); 46 | c8 peek = __peek_c8(read_buf); 47 | 48 | bool is_negative = peek == '-'; 49 | if (is_negative) 50 | { 51 | __consume_c8(read_buf); 52 | peek = __peek_c8(read_buf); 53 | } 54 | 55 | if ('0' > peek || peek > '9') 56 | { 57 | __sys_panic("cannot read i32", 16); 58 | return -1; 59 | } 60 | 61 | u32 result = 0; 62 | while ('0' <= peek && peek <= '9') 63 | { 64 | __consume_c8(read_buf); 65 | result = result * 10 + peek - '0'; 66 | peek = __peek_c8(read_buf); 67 | } 68 | 69 | return is_negative ? ~result + 1 : result; 70 | } 71 | 72 | i64 __read_i64(c8 *read_buf) 73 | { 74 | __skip_whitespace(read_buf); 75 | c8 peek = __peek_c8(read_buf); 76 | 77 | bool is_negative = peek == '-'; 78 | if (is_negative) 79 | { 80 | __consume_c8(read_buf); 81 | peek = __peek_c8(read_buf); 82 | } 83 | 84 | if ('0' > peek || peek > '9') 85 | { 86 | __sys_panic("cannot read i64", 16); 87 | return -1; 88 | } 89 | 90 | u64 result = 0; 91 | while ('0' <= peek && peek <= '9') 92 | { 93 | __consume_c8(read_buf); 94 | result = result * 10 + peek - '0'; 95 | peek = __peek_c8(read_buf); 96 | } 97 | 98 | return is_negative ? ~result + 1 : result; 99 | } 100 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/rt/typedef.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | typedef char c8; 6 | typedef size_t usize; 7 | typedef int32_t i32; 8 | typedef int64_t i64; 9 | typedef uint32_t u32; 10 | typedef uint64_t u64; 11 | 12 | typedef struct {} tuple0; 13 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/rt/write.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "typedef.h" 7 | 8 | const usize WRITE_BUF_LEN = 1 << 15; 9 | usize write_off = 0; 10 | 11 | void __flush(c8 *write_buf) 12 | { 13 | write(STDOUT_FILENO, write_buf, write_off); 14 | write_off = 0; 15 | } 16 | 17 | void __memcpy(char *dest, const char *src, usize len) 18 | { 19 | for (usize i = 0; i < len; ++i) 20 | { 21 | dest[i] = src[i]; 22 | } 23 | } 24 | 25 | void __sys_write(c8 *write_buf, c8 *buf, usize len) 26 | { 27 | while (len >= WRITE_BUF_LEN - write_off) 28 | { 29 | __memcpy(write_buf + write_off, buf, WRITE_BUF_LEN - write_off); 30 | buf += WRITE_BUF_LEN - write_off; 31 | len -= WRITE_BUF_LEN - write_off; 32 | __flush(write_buf); 33 | } 34 | __memcpy(write_buf + write_off, buf, len); 35 | write_off += len; 36 | } 37 | 38 | void __write_i32(c8 *write_buf, i32 i) 39 | { 40 | bool is_negative = i < 0; 41 | if (is_negative) 42 | { 43 | i = -i; 44 | } 45 | c8 buf[20] = { 46 | 0, 47 | }; 48 | usize offset = 20; 49 | do 50 | { 51 | offset -= 1; 52 | buf[offset] = '0' + (i % 10); 53 | i /= 10; 54 | } while (i > 0); 55 | if (is_negative) 56 | { 57 | offset -= 1; 58 | buf[offset] = '-'; 59 | } 60 | __sys_write(write_buf, buf + offset, 20 - offset); 61 | } 62 | 63 | void __write_i64(c8 *write_buf, i64 i) 64 | { 65 | bool is_negative = i < 0; 66 | if (is_negative) 67 | { 68 | i = -i; 69 | } 70 | c8 buf[20] = { 71 | 0, 72 | }; 73 | usize offset = 20; 74 | do 75 | { 76 | offset -= 1; 77 | buf[offset] = '0' + (i % 10); 78 | i /= 10; 79 | } while (i > 0); 80 | if (is_negative) 81 | { 82 | offset -= 1; 83 | buf[offset] = '-'; 84 | } 85 | __sys_write(write_buf, buf + offset, 20 - offset); 86 | } 87 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/scope.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::construct::Type; 4 | 5 | #[derive(Debug)] 6 | pub struct Scope<'a> { 7 | parent: Option<&'a Scope<'a>>, 8 | variable_names: HashMap, 9 | } 10 | 11 | impl<'a> Scope<'a> { 12 | pub fn new() -> Scope<'static> { 13 | Scope { 14 | parent: None, 15 | variable_names: HashMap::new(), 16 | } 17 | } 18 | 19 | pub fn create_subscope<'b>(&'b mut self) -> Scope<'b> { 20 | Scope { 21 | parent: Some(self), 22 | variable_names: HashMap::new(), 23 | } 24 | } 25 | 26 | pub fn add_variable(&mut self, name: String, ty: Type) { 27 | self.variable_names.insert(name, ty); 28 | } 29 | 30 | pub fn get_variable_type(&self, name: &String) -> Option<&Type> { 31 | self.variable_names.get(name).or_else(|| { 32 | self.parent 33 | .as_ref() 34 | .and_then(|parent| parent.get_variable_type(name)) 35 | }) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /crates/psl/src/codegen/visitor.rs: -------------------------------------------------------------------------------- 1 | use super::context::CodegenContext; 2 | 3 | pub trait CodegenNode { 4 | fn produce_code(self, ctx: &mut CodegenContext) -> String; 5 | } 6 | 7 | impl CodegenNode for Box { 8 | fn produce_code(self, ctx: &mut CodegenContext) -> String { 9 | T::produce_code(*self, ctx) 10 | } 11 | } 12 | 13 | impl CodegenContext { 14 | pub fn visit(&mut self, node: impl CodegenNode) -> String { 15 | node.produce_code(self) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crates/psl/src/diagnostics/diagnostic_code.rs: -------------------------------------------------------------------------------- 1 | pub enum DiagnosticsCode { 2 | Error(ErrorCode), 3 | } 4 | 5 | #[repr(u32)] 6 | pub enum ErrorCode { 7 | Syntax = 1, 8 | } 9 | -------------------------------------------------------------------------------- /crates/psl/src/diagnostics/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Cow, ops::Range}; 2 | 3 | use ariadne::{Label, Report, ReportKind, Source}; 4 | 5 | use self::diagnostic_code::DiagnosticsCode; 6 | 7 | mod diagnostic_code; 8 | mod transform; 9 | 10 | pub struct Diagnostic<'a> { 11 | filename: Cow<'a, str>, 12 | file_content: &'a str, 13 | 14 | code: DiagnosticsCode, 15 | 16 | span: Range, 17 | message: Cow<'a, str>, 18 | } 19 | 20 | impl Diagnostic<'_> { 21 | pub fn write(self) { 22 | Report::build( 23 | match self.code { 24 | DiagnosticsCode::Error(_) => ReportKind::Error, 25 | }, 26 | &self.filename, 27 | self.span.start, 28 | ) 29 | .with_code(match self.code { 30 | DiagnosticsCode::Error(code) => code as u32, 31 | }) 32 | .with_message(self.message) 33 | .with_label(Label::new((&self.filename, self.span))) 34 | .finish() 35 | .eprint((&self.filename, Source::from(&self.file_content))) 36 | .unwrap(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crates/psl/src/diagnostics/transform/mod.rs: -------------------------------------------------------------------------------- 1 | mod parse_error; 2 | -------------------------------------------------------------------------------- /crates/psl/src/diagnostics/transform/parse_error.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use psl::syntax::parse_token; 4 | use winnow::{ 5 | error::{ContextError, ParseError}, 6 | Located, Parser, 7 | }; 8 | 9 | use crate::diagnostics::{ 10 | diagnostic_code::{DiagnosticsCode, ErrorCode}, 11 | Diagnostic, 12 | }; 13 | 14 | impl<'a> Diagnostic<'a> { 15 | pub fn from_parse_error( 16 | filename: Cow<'a, str>, 17 | value: ParseError, ContextError>, 18 | ) -> Self { 19 | let next_token = parse_token 20 | .parse_next(&mut Located::new(&value.input()[value.offset()..])) 21 | .unwrap(); 22 | 23 | let mut message = value.inner().to_string(); 24 | if message.is_empty() { 25 | message = format!("unexpected token {:?}", next_token.content); 26 | } 27 | 28 | Self { 29 | filename, 30 | file_content: value.input(), 31 | 32 | code: DiagnosticsCode::Error(ErrorCode::Syntax), 33 | message: Cow::Owned(message), 34 | span: value.offset()..value.offset() + next_token.content.len(), 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /crates/psl/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use codegen::generate_codes; 2 | 3 | mod ast; 4 | mod codegen; 5 | pub mod syntax; 6 | -------------------------------------------------------------------------------- /crates/psl/src/main.rs: -------------------------------------------------------------------------------- 1 | mod diagnostics; 2 | 3 | use psl::{generate_codes, syntax::parse_program}; 4 | use std::{ 5 | borrow::Cow, 6 | env::args, 7 | fs, 8 | path::PathBuf, 9 | process::{Command, ExitCode}, 10 | str::FromStr, 11 | }; 12 | use winnow::{Located, Parser}; 13 | 14 | use crate::diagnostics::Diagnostic; 15 | 16 | fn main() -> ExitCode { 17 | let Some(path) = args().nth(1) else { 18 | eprintln!("please specify file to compile"); 19 | return ExitCode::FAILURE; 20 | }; 21 | 22 | let Ok(content) = fs::read_to_string(&path) else { 23 | eprintln!("file not found: {path}"); 24 | return ExitCode::FAILURE; 25 | }; 26 | 27 | let ast = match parse_program.parse(Located::new(content.as_ref())) { 28 | Ok(ast) => ast, 29 | Err(e) => { 30 | Diagnostic::from_parse_error(Cow::Borrowed(&path), e).write(); 31 | 32 | return ExitCode::FAILURE; 33 | } 34 | }; 35 | 36 | let mut output_path = PathBuf::from_str(&path).unwrap(); 37 | let mut output_filename = output_path.file_stem().unwrap().to_owned(); 38 | output_filename.push(".c"); 39 | output_path.set_file_name(output_filename); 40 | 41 | let output = generate_codes(ast); 42 | 43 | fs::write(&output_path, output).unwrap(); 44 | 45 | eprintln!("C code generated, run gcc for compile."); 46 | 47 | let mut executable_path = PathBuf::from_str(&path).unwrap(); 48 | let mut executable_filename = executable_path.file_stem().unwrap().to_owned(); 49 | executable_filename.push(".o"); 50 | executable_path.set_file_name(executable_filename); 51 | 52 | Command::new("gcc") 53 | .arg(&output_path) 54 | .arg("-o") 55 | .arg(&executable_path) 56 | .spawn() 57 | .unwrap() 58 | .wait_with_output() 59 | .unwrap(); 60 | 61 | eprintln!("Run the program"); 62 | eprintln!("-------------------------------------------"); 63 | 64 | let o = Command::new(&executable_path) 65 | .spawn() 66 | .unwrap() 67 | .wait_with_output() 68 | .unwrap(); 69 | 70 | if o.status.success() { 71 | ExitCode::SUCCESS 72 | } else { 73 | ExitCode::FAILURE 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/declarations/function_declaration.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{alt, cut_err, opt, preceded, separated, separated_pair, terminated}, 3 | Located, PResult, Parser, 4 | }; 5 | 6 | use crate::{ 7 | ast::{ExpressionOrBlock, FunctionDeclaration, TokenKind}, 8 | syntax::{ 9 | expressions::parse_expression, 10 | fragments::{block::parse_block, r#type::parse_type, separator::parse_separator}, 11 | }, 12 | }; 13 | 14 | pub fn parse_function_declaration(s: &mut Located<&str>) -> PResult { 15 | ( 16 | TokenKind::KeywordFn, 17 | cut_err(( 18 | TokenKind::WhitespaceHorizontal, 19 | TokenKind::IdentifierIdentifier, 20 | TokenKind::PunctuationLeftParenthesis, 21 | opt(terminated( 22 | separated( 23 | 1.., 24 | separated_pair( 25 | parse_type, 26 | TokenKind::WhitespaceHorizontal, 27 | TokenKind::IdentifierIdentifier, 28 | ), 29 | (TokenKind::PunctuationComma, TokenKind::WhitespaceHorizontal), 30 | ), 31 | opt(TokenKind::PunctuationComma), 32 | )), 33 | TokenKind::PunctuationRightParenthesis, 34 | opt(TokenKind::WhitespaceHorizontal), 35 | parse_type, 36 | opt(TokenKind::WhitespaceHorizontal), 37 | alt(( 38 | parse_block.map(ExpressionOrBlock::Block), 39 | preceded( 40 | ( 41 | TokenKind::PunctuationEqualsSign, 42 | opt(TokenKind::WhitespaceHorizontal), 43 | ), 44 | cut_err(parse_expression), 45 | ) 46 | .map(ExpressionOrBlock::Expression), 47 | )), 48 | parse_separator, 49 | )), 50 | ) 51 | .map( 52 | |(_, (_, name, _, parameters, _, _, return_type, _, body, _))| FunctionDeclaration { 53 | name, 54 | parameters: parameters.unwrap_or(Vec::new()), 55 | return_type, 56 | body, 57 | }, 58 | ) 59 | .parse_next(s) 60 | } 61 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/declarations/mod.rs: -------------------------------------------------------------------------------- 1 | use function_declaration::parse_function_declaration; 2 | use winnow::{combinator::alt, Located, PResult, Parser}; 3 | 4 | use crate::ast::Declaration; 5 | 6 | use self::variable_declaration::parse_variable_declaration; 7 | 8 | mod function_declaration; 9 | mod variable_declaration; 10 | 11 | pub fn parse_declaration(s: &mut Located<&str>) -> PResult { 12 | alt(( 13 | parse_variable_declaration.map(Declaration::Variable), 14 | parse_function_declaration.map(Declaration::Function), 15 | )) 16 | .parse_next(s) 17 | } 18 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/declarations/variable_declaration.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{cut_err, opt}, 3 | error::{StrContext, StrContextValue}, 4 | Located, PResult, Parser, 5 | }; 6 | 7 | use crate::{ 8 | ast::{TokenKind, VariableDeclaration}, 9 | syntax::{ 10 | expressions::parse_expression, 11 | fragments::{r#type::parse_type, separator::parse_separator}, 12 | }, 13 | }; 14 | 15 | pub fn parse_variable_declaration(s: &mut Located<&str>) -> PResult { 16 | ( 17 | parse_type, 18 | TokenKind::WhitespaceHorizontal, 19 | TokenKind::IdentifierIdentifier, 20 | opt(( 21 | opt(TokenKind::WhitespaceHorizontal), 22 | TokenKind::PunctuationEqualsSign, 23 | opt(TokenKind::WhitespaceHorizontal), 24 | cut_err(parse_expression).context(StrContext::Expected(StrContextValue::Description( 25 | "expression", 26 | ))), 27 | )) 28 | .map(|opt| opt.map(|(_, _, _, expr)| expr)), 29 | cut_err(parse_separator), 30 | ) 31 | .map(|(ty, _, name, value, _)| VariableDeclaration { ty, name, value }) 32 | .parse_next(s) 33 | } 34 | 35 | #[cfg(test)] 36 | mod test { 37 | use pretty_assertions::assert_eq; 38 | use winnow::{Located, Parser}; 39 | 40 | use crate::{ 41 | ast::{Token, TokenKind, Type, VariableDeclaration}, 42 | syntax::declarations::variable_declaration::parse_variable_declaration, 43 | }; 44 | 45 | #[test] 46 | fn test_uninitialized() { 47 | assert_eq!( 48 | parse_variable_declaration.parse(Located::new("i32 a")), 49 | Ok(VariableDeclaration { 50 | ty: Type::Simple(Token { 51 | kind: TokenKind::IdentifierIdentifier, 52 | content: "i32".to_string(), 53 | span: 0..3 54 | }), 55 | name: Token { 56 | kind: TokenKind::IdentifierIdentifier, 57 | content: "a".to_string(), 58 | span: 4..5 59 | }, 60 | value: None, 61 | }), 62 | ) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/expressions/binary_operator.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{alt, opt, repeat}, 3 | error::ContextError, 4 | Located, PResult, Parser, 5 | }; 6 | 7 | use crate::ast::{BinaryOperator, BinaryOperatorExpression, Expression, TokenKind}; 8 | 9 | use super::simple::parse_simple_expression; 10 | 11 | pub fn parse_binary_operator(s: &mut Located<&str>) -> PResult { 12 | parse_or_operator.parse_next(s) 13 | } 14 | 15 | pub fn parse_or_operator(s: &mut Located<&str>) -> PResult { 16 | binary_operator( 17 | ( 18 | TokenKind::PunctuationVerticalLine, 19 | TokenKind::PunctuationVerticalLine, 20 | ) 21 | .map(|_| BinaryOperator::LogicalOr), 22 | parse_and_operator, 23 | ) 24 | .parse_next(s) 25 | } 26 | 27 | pub fn parse_and_operator(s: &mut Located<&str>) -> PResult { 28 | binary_operator( 29 | ( 30 | TokenKind::PunctuationAmpersand, 31 | TokenKind::PunctuationAmpersand, 32 | ) 33 | .map(|_| BinaryOperator::LogiacalAnd), 34 | parse_comparison_operator, 35 | ) 36 | .parse_next(s) 37 | } 38 | 39 | pub fn parse_comparison_operator(s: &mut Located<&str>) -> PResult { 40 | binary_operator( 41 | alt(( 42 | ( 43 | TokenKind::PunctuationLessThanSign, 44 | TokenKind::PunctuationEqualsSign, 45 | ) 46 | .map(|_| BinaryOperator::LessEqual), 47 | ( 48 | TokenKind::PunctuationGreaterThanSign, 49 | TokenKind::PunctuationEqualsSign, 50 | ) 51 | .map(|_| BinaryOperator::GreaterEqual), 52 | ( 53 | TokenKind::PunctuationExclamationMark, 54 | TokenKind::PunctuationEqualsSign, 55 | ) 56 | .map(|_| BinaryOperator::NotEqual), 57 | ( 58 | TokenKind::PunctuationEqualsSign, 59 | TokenKind::PunctuationEqualsSign, 60 | ) 61 | .map(|_| BinaryOperator::Equal), 62 | TokenKind::PunctuationLessThanSign.map(|_| BinaryOperator::Less), 63 | TokenKind::PunctuationGreaterThanSign.map(|_| BinaryOperator::Greater), 64 | )), 65 | parse_addsub_operator, 66 | ) 67 | .parse_next(s) 68 | } 69 | 70 | pub fn parse_addsub_operator(s: &mut Located<&str>) -> PResult { 71 | binary_operator( 72 | alt(( 73 | TokenKind::PunctuationPlusSign.map(|_| BinaryOperator::Add), 74 | TokenKind::PunctuationHyphenMinus.map(|_| BinaryOperator::Subtract), 75 | )), 76 | parse_muldiv_operator, 77 | ) 78 | .parse_next(s) 79 | } 80 | 81 | pub fn parse_muldiv_operator(s: &mut Located<&str>) -> PResult { 82 | binary_operator( 83 | alt(( 84 | TokenKind::PunctuationAsterisk.map(|_| BinaryOperator::Multiply), 85 | TokenKind::PunctuationSolidus.map(|_| BinaryOperator::Divide), 86 | TokenKind::PunctuationPercentSign.map(|_| BinaryOperator::Modulus), 87 | )), 88 | parse_simple_expression, 89 | ) 90 | .parse_next(s) 91 | } 92 | 93 | fn binary_operator<'a>( 94 | operator: impl Parser, BinaryOperator, ContextError>, 95 | parser: impl Parser, Expression, ContextError> + Clone, 96 | ) -> impl Parser, Expression, ContextError> { 97 | ( 98 | parser.clone(), 99 | repeat::<_, _, Vec<_>, _, _>( 100 | .., 101 | ( 102 | opt(TokenKind::WhitespaceHorizontal), 103 | operator, 104 | opt(TokenKind::WhitespaceHorizontal), 105 | parser, 106 | ) 107 | .map(|(_, operator, _, rhs)| (operator, rhs)), 108 | ), 109 | ) 110 | .map(|(mut lhs, seq)| { 111 | for (operator, rhs) in seq { 112 | lhs = Expression::BinaryOperator(BinaryOperatorExpression { 113 | lhs: Box::new(lhs), 114 | operator, 115 | rhs: Box::new(rhs), 116 | }); 117 | } 118 | 119 | lhs 120 | }) 121 | } 122 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/expressions/if.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{alt, opt, preceded}, 3 | Located, PResult, Parser, 4 | }; 5 | 6 | use crate::{ 7 | ast::{ExpressionOrBlock, IfExpression, TokenKind}, 8 | syntax::fragments::block::parse_block, 9 | }; 10 | 11 | use super::parse_expression; 12 | 13 | pub fn parse_if(s: &mut Located<&str>) -> PResult { 14 | ( 15 | TokenKind::KeywordIf, 16 | preceded(TokenKind::WhitespaceHorizontal, parse_expression).map(Box::new), 17 | preceded(TokenKind::WhitespaceHorizontal, parse_block) 18 | .map(|block| Box::new(ExpressionOrBlock::Block(block))), 19 | opt(( 20 | preceded(TokenKind::WhitespaceHorizontal, TokenKind::KeywordElse), 21 | alt((preceded(TokenKind::WhitespaceHorizontal, parse_block) 22 | .map(|block| Box::new(ExpressionOrBlock::Block(block))),)), 23 | )), 24 | ) 25 | .map(|(_, condition, positive, negative)| IfExpression { 26 | condition, 27 | positive, 28 | negative: negative.map(|(_, negative)| negative), 29 | }) 30 | .parse_next(s) 31 | } 32 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/expressions/literal.rs: -------------------------------------------------------------------------------- 1 | use winnow::{combinator::alt, Located, PResult, Parser}; 2 | 3 | use crate::ast::{LiteralExpression, TokenKind}; 4 | 5 | pub fn parse_literal(s: &mut Located<&str>) -> PResult { 6 | alt(( 7 | TokenKind::LiteralIntegerDecimal, 8 | TokenKind::LiteralIntegerHexadecimal, 9 | TokenKind::LiteralIntegerBinary, 10 | )) 11 | .map(|token| LiteralExpression { value: token }) 12 | .parse_next(s) 13 | } 14 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/expressions/mod.rs: -------------------------------------------------------------------------------- 1 | use winnow::{Located, PResult, Parser}; 2 | 3 | use crate::ast::Expression; 4 | 5 | use binary_operator::parse_binary_operator; 6 | pub use name::parse_name; 7 | 8 | mod binary_operator; 9 | mod r#if; 10 | mod literal; 11 | mod name; 12 | mod postfix_operator; 13 | mod read; 14 | mod r#return; 15 | mod simple; 16 | 17 | pub fn parse_expression(s: &mut Located<&str>) -> PResult { 18 | parse_binary_operator.parse_next(s) 19 | } 20 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/expressions/name.rs: -------------------------------------------------------------------------------- 1 | use winnow::{Located, PResult, Parser}; 2 | 3 | use crate::ast::{NameExpression, TokenKind}; 4 | 5 | pub fn parse_name(s: &mut Located<&str>) -> PResult { 6 | TokenKind::IdentifierIdentifier 7 | .map(|name| NameExpression { name }) 8 | .parse_next(s) 9 | } 10 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/expressions/postfix_operator.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{alt, delimited, opt, separated}, 3 | Located, PResult, Parser, 4 | }; 5 | 6 | use crate::ast::{PostfixOperator, TokenKind}; 7 | 8 | use super::parse_expression; 9 | 10 | pub fn parse_postfix_operator(s: &mut Located<&str>) -> PResult { 11 | alt((parse_invoke_operator, parse_index_operator)).parse_next(s) 12 | } 13 | 14 | fn parse_invoke_operator(s: &mut Located<&str>) -> PResult { 15 | delimited( 16 | TokenKind::PunctuationLeftParenthesis, 17 | opt(separated( 18 | 1.., 19 | parse_expression, 20 | ( 21 | TokenKind::PunctuationComma, 22 | opt(TokenKind::WhitespaceHorizontal), 23 | ), 24 | )), 25 | TokenKind::PunctuationRightParenthesis, 26 | ) 27 | .map(|arguments| PostfixOperator::Invoke(arguments.unwrap_or(Vec::new()))) 28 | .parse_next(s) 29 | } 30 | 31 | fn parse_index_operator(s: &mut Located<&str>) -> PResult { 32 | delimited( 33 | TokenKind::PunctuationLeftSquareBracket, 34 | opt(separated( 35 | 1.., 36 | parse_expression, 37 | ( 38 | TokenKind::PunctuationComma, 39 | opt(TokenKind::WhitespaceHorizontal), 40 | ), 41 | )), 42 | TokenKind::PunctuationRightSquareBracket, 43 | ) 44 | .map(|arguments| PostfixOperator::Invoke(arguments.unwrap_or(Vec::new()))) 45 | .parse_next(s) 46 | } 47 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/expressions/read.rs: -------------------------------------------------------------------------------- 1 | use winnow::{combinator::preceded, Located, PResult, Parser}; 2 | 3 | use crate::{ 4 | ast::{ReadExpression, TokenKind}, 5 | syntax::fragments::r#type::parse_type, 6 | }; 7 | 8 | pub fn parse_read(s: &mut Located<&str>) -> PResult { 9 | ( 10 | TokenKind::KeywordRead, 11 | preceded(TokenKind::WhitespaceHorizontal, parse_type), 12 | ) 13 | .map(|(_, ty)| ReadExpression::Type(ty)) 14 | .parse_next(s) 15 | } 16 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/expressions/return.rs: -------------------------------------------------------------------------------- 1 | use winnow::{combinator::preceded, Located, PResult, Parser}; 2 | 3 | use crate::ast::{ReturnExpression, TokenKind}; 4 | 5 | use super::parse_expression; 6 | 7 | pub fn parse_return(s: &mut Located<&str>) -> PResult { 8 | ( 9 | TokenKind::KeywordReturn, 10 | preceded(TokenKind::WhitespaceHorizontal, parse_expression), 11 | ) 12 | .map(|(_, value)| ReturnExpression { 13 | value: Box::new(value), 14 | }) 15 | .parse_next(s) 16 | } 17 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/expressions/simple.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{alt, opt}, 3 | Located, PResult, Parser, 4 | }; 5 | 6 | use crate::ast::{Expression, PostfixOperatorExpression}; 7 | 8 | use super::{ 9 | literal::parse_literal, name::parse_name, postfix_operator::parse_postfix_operator, 10 | r#if::parse_if, r#return::parse_return, read::parse_read, 11 | }; 12 | 13 | pub fn parse_simple_expression(s: &mut Located<&str>) -> PResult { 14 | ( 15 | alt(( 16 | parse_literal.map(Expression::Literal), 17 | parse_read.map(Expression::Read), 18 | parse_name.map(Expression::Name), 19 | parse_if.map(Expression::If), 20 | parse_return.map(Expression::Return), 21 | )), 22 | opt(parse_postfix_operator), 23 | ) 24 | .map(|(expr, postfix)| { 25 | if let Some(operator) = postfix { 26 | Expression::PostfixOperator(PostfixOperatorExpression { 27 | expr: Box::new(expr), 28 | operator, 29 | }) 30 | } else { 31 | expr 32 | } 33 | }) 34 | .parse_next(s) 35 | } 36 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/fragments/block.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{cut_err, opt, repeat}, 3 | Located, PResult, Parser, 4 | }; 5 | 6 | use crate::{ 7 | ast::{Block, Statement, TokenKind}, 8 | syntax::{fragments::separator::parse_separator, statements::parse_statement}, 9 | }; 10 | 11 | pub fn parse_block(s: &mut Located<&str>) -> PResult { 12 | ( 13 | TokenKind::PunctuationLeftCurlyBracket, 14 | cut_err(( 15 | opt(TokenKind::WhitespaceHorizontal), 16 | opt(parse_separator), 17 | repeat::<_, _, Vec<_>, _, _>(0.., parse_statement), 18 | opt(parse_separator), 19 | opt(TokenKind::WhitespaceHorizontal), 20 | TokenKind::PunctuationRightCurlyBracket, 21 | )), 22 | ) 23 | .map(|(_, (_, _, statements, _, _, _))| match &statements[..] { 24 | [statements @ .., Statement::Expression(expr)] => Block { 25 | statements: statements.to_vec(), 26 | last_expression: Some(Box::new(expr.clone())), 27 | }, 28 | statements => Block { 29 | statements: statements.to_vec(), 30 | last_expression: None, 31 | }, 32 | }) 33 | .parse_next(s) 34 | } 35 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/fragments/format_specifier.rs: -------------------------------------------------------------------------------- 1 | use winnow::{Located, PResult, Parser}; 2 | 3 | use crate::{ 4 | ast::{FormatSpecifier, TokenKind}, 5 | syntax::parse_token, 6 | }; 7 | 8 | pub fn parse_format_specifier(s: &mut Located<&str>) -> PResult { 9 | parse_token 10 | .verify_map(|t| { 11 | if let TokenKind::LiteralFormatSpecifier(s) = t.kind { 12 | Some(s) 13 | } else { 14 | None 15 | } 16 | }) 17 | .parse_next(s) 18 | } 19 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/fragments/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod block; 2 | pub(crate) mod format_specifier; 3 | pub(crate) mod separator; 4 | pub(crate) mod r#type; 5 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/fragments/separator.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{alt, opt, repeat}, 3 | Located, PResult, Parser, 4 | }; 5 | 6 | use crate::ast::TokenKind; 7 | 8 | pub fn parse_separator(s: &mut Located<&str>) -> PResult<()> { 9 | alt(( 10 | ( 11 | repeat::<_, _, (), _, _>( 12 | 1.., 13 | ( 14 | opt(TokenKind::WhitespaceHorizontal), 15 | TokenKind::WhitespaceVertical, 16 | ), 17 | ), 18 | opt(TokenKind::WhitespaceHorizontal), 19 | ) 20 | .void(), 21 | TokenKind::Eof.void(), 22 | )) 23 | .parse_next(s) 24 | } 25 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/fragments/type.rs: -------------------------------------------------------------------------------- 1 | use winnow::Parser; 2 | use winnow::{Located, PResult}; 3 | 4 | use crate::ast::{TokenKind, Type}; 5 | 6 | pub fn parse_type(s: &mut Located<&str>) -> PResult { 7 | TokenKind::IdentifierIdentifier 8 | .map(Type::Simple) 9 | .parse_next(s) 10 | } 11 | 12 | #[cfg(test)] 13 | mod test { 14 | use pretty_assertions::assert_eq; 15 | use winnow::{Located, Parser}; 16 | 17 | use crate::{ 18 | ast::{Token, TokenKind, Type}, 19 | syntax::fragments::r#type::parse_type, 20 | }; 21 | 22 | #[test] 23 | fn test_simple_type() { 24 | assert_eq!( 25 | parse_type.parse(Located::new("i32")), 26 | Ok(Type::Simple(Token { 27 | kind: TokenKind::IdentifierIdentifier, 28 | span: 0..3, 29 | content: "i32".to_string() 30 | })), 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/item.rs: -------------------------------------------------------------------------------- 1 | use winnow::{combinator::alt, Located, PResult, Parser}; 2 | 3 | use crate::ast::Item; 4 | 5 | use super::{declarations::parse_declaration, statements::parse_statement}; 6 | 7 | pub fn parse_item(s: &mut Located<&str>) -> PResult { 8 | alt(( 9 | parse_declaration.map(Item::Declaration), 10 | parse_statement.map(Item::Statement), 11 | )) 12 | .parse_next(s) 13 | } 14 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/mod.rs: -------------------------------------------------------------------------------- 1 | pub use program::parse_program; 2 | pub use tokens::parse_token; 3 | 4 | pub(crate) mod declarations; 5 | pub(crate) mod expressions; 6 | pub(crate) mod fragments; 7 | pub(crate) mod item; 8 | pub(crate) mod program; 9 | pub(crate) mod statements; 10 | pub(crate) mod tokens; 11 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/program.rs: -------------------------------------------------------------------------------- 1 | use winnow::combinator::{delimited, opt}; 2 | use winnow::Parser; 3 | use winnow::{combinator::repeat, Located, PResult}; 4 | 5 | use crate::ast::{Item, Program}; 6 | 7 | use super::fragments::separator::parse_separator; 8 | use super::item::parse_item; 9 | 10 | pub fn parse_program(s: &mut Located<&str>) -> PResult { 11 | delimited( 12 | opt(parse_separator), 13 | repeat(0.., parse_item), 14 | opt(parse_separator), 15 | ) 16 | .map(|items: Vec| Program { items }) 17 | .parse_next(s) 18 | } 19 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/statements/assignment.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{alt, opt}, 3 | Located, PResult, Parser, 4 | }; 5 | 6 | use crate::{ 7 | ast::{Assignment, CompoundAssignmentStatement, TokenKind}, 8 | syntax::{expressions::parse_expression, fragments::separator::parse_separator}, 9 | }; 10 | 11 | pub fn parse_compound_assignment(s: &mut Located<&str>) -> PResult { 12 | ( 13 | TokenKind::IdentifierIdentifier, 14 | opt(TokenKind::WhitespaceHorizontal), 15 | alt(((TokenKind::PunctuationEqualsSign).map(|_| CompoundAssignmentStatement::Simple),)), 16 | opt(TokenKind::WhitespaceHorizontal), 17 | parse_expression, 18 | parse_separator, 19 | ) 20 | .map(|(name, _, f, _, expr, _)| f(Assignment { name, expr })) 21 | .parse_next(s) 22 | } 23 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/statements/mod.rs: -------------------------------------------------------------------------------- 1 | mod assignment; 2 | mod r#while; 3 | mod write; 4 | 5 | use winnow::{ 6 | combinator::{alt, opt, terminated}, 7 | Located, PResult, Parser, 8 | }; 9 | 10 | use crate::ast::Statement; 11 | 12 | use self::{assignment::parse_compound_assignment, r#while::parse_while, write::parse_write}; 13 | 14 | use super::{ 15 | declarations::parse_declaration, expressions::parse_expression, 16 | fragments::separator::parse_separator, 17 | }; 18 | 19 | pub fn parse_statement(s: &mut Located<&str>) -> PResult { 20 | alt(( 21 | parse_declaration.map(Statement::Declaration), 22 | parse_write.map(Statement::Write), 23 | parse_while.map(Statement::While), 24 | parse_compound_assignment.map(Statement::CompoundAssignment), 25 | terminated(parse_expression, opt(parse_separator)).map(Statement::Expression), 26 | )) 27 | .parse_next(s) 28 | } 29 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/statements/while.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{cut_err, opt}, 3 | Located, PResult, Parser, 4 | }; 5 | 6 | use crate::{ 7 | ast::{TokenKind, WhileStatement}, 8 | syntax::{ 9 | expressions::parse_expression, 10 | fragments::{block::parse_block, separator::parse_separator}, 11 | }, 12 | }; 13 | 14 | pub fn parse_while(s: &mut Located<&str>) -> PResult { 15 | ( 16 | TokenKind::KeywordWhile, 17 | opt(TokenKind::WhitespaceHorizontal), 18 | cut_err(parse_expression), 19 | opt(TokenKind::WhitespaceHorizontal), 20 | cut_err(parse_block), 21 | parse_separator, 22 | ) 23 | .map(|(_, _, condition, _, block, _)| WhileStatement { condition, block }) 24 | .parse_next(s) 25 | } 26 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/statements/write.rs: -------------------------------------------------------------------------------- 1 | use winnow::{ 2 | combinator::{alt, cut_err, opt, terminated}, 3 | error::{StrContext, StrContextValue}, 4 | Located, PResult, Parser, 5 | }; 6 | 7 | use crate::{ 8 | ast::{FormatSpecifierFragment, TokenKind, WriteStatement}, 9 | syntax::{ 10 | expressions::parse_name, 11 | fragments::{format_specifier::parse_format_specifier, separator::parse_separator}, 12 | }, 13 | }; 14 | 15 | pub fn parse_write(s: &mut Located<&str>) -> PResult { 16 | ( 17 | TokenKind::KeywordWrite, 18 | opt(TokenKind::WhitespaceHorizontal), 19 | cut_err(terminated( 20 | alt(( 21 | parse_name.map(|name| WriteStatement { 22 | fragments: vec![FormatSpecifierFragment::Variable(name.name.content)], 23 | }), 24 | parse_format_specifier.map(|format_specifier| WriteStatement { 25 | fragments: format_specifier.0, 26 | }), 27 | )), 28 | parse_separator, 29 | )) 30 | .context(StrContext::Expected(StrContextValue::Description( 31 | "name or format specifier", 32 | ))), 33 | ) 34 | .map(|(_, _, stmt)| stmt) 35 | .parse_next(s) 36 | } 37 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/tokens/identifier.rs: -------------------------------------------------------------------------------- 1 | use unicode_ident::{is_xid_continue, is_xid_start}; 2 | use winnow::{ 3 | token::{any, take_while}, 4 | Located, PResult, Parser, 5 | }; 6 | 7 | use crate::ast::{Token, TokenKind}; 8 | 9 | use super::token; 10 | 11 | pub fn parse_identifier_identifier(s: &mut Located<&str>) -> PResult { 12 | ( 13 | any.verify(|ch| is_xid_start(*ch)), 14 | take_while(0.., |ch: char| is_xid_continue(ch)), 15 | ) 16 | .map(|(start, cont)| format!("{start}{cont}")) 17 | .with_span() 18 | .map(token(TokenKind::IdentifierIdentifier)) 19 | .parse_next(s) 20 | } 21 | 22 | #[cfg(test)] 23 | mod test { 24 | use pretty_assertions::assert_eq; 25 | use winnow::{Located, Parser}; 26 | 27 | use crate::{ 28 | ast::{Token, TokenKind}, 29 | syntax::tokens::identifier::parse_identifier_identifier, 30 | }; 31 | 32 | #[test] 33 | fn test_simple_identifier() { 34 | assert_eq!( 35 | parse_identifier_identifier.parse(Located::new("a0")), 36 | Ok(Token { 37 | kind: TokenKind::IdentifierIdentifier, 38 | span: 0..2, 39 | content: "a0".to_string() 40 | }), 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/tokens/keyword.rs: -------------------------------------------------------------------------------- 1 | use winnow::{combinator::alt, Located, PResult, Parser}; 2 | 3 | use crate::ast::{Token, TokenKind}; 4 | 5 | use super::token; 6 | 7 | pub fn parse_keyword(s: &mut Located<&str>) -> PResult { 8 | // please sort alt by enum declaration order 9 | alt(( 10 | kw("else", TokenKind::KeywordElse), 11 | kw("fn", TokenKind::KeywordFn), 12 | kw("if", TokenKind::KeywordIf), 13 | kw("read", TokenKind::KeywordRead), 14 | kw("return", TokenKind::KeywordReturn), 15 | kw("write", TokenKind::KeywordWrite), 16 | kw("while", TokenKind::KeywordWhile), 17 | )) 18 | .parse_next(s) 19 | } 20 | 21 | fn kw( 22 | keyword: &str, 23 | kind: TokenKind, 24 | ) -> impl for<'a> Fn(&mut Located<&'a str>) -> PResult + '_ { 25 | move |s| keyword.with_span().map(token(kind.clone())).parse_next(s) 26 | } 27 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/tokens/literal.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use winnow::{ 4 | combinator::{alt, cut_err, preceded, repeat}, 5 | error::ContextError, 6 | token::{one_of, take, take_while}, 7 | Located, PResult, Parser, 8 | }; 9 | 10 | use crate::ast::{FormatSpecifier, FormatSpecifierFragment, Token, TokenKind}; 11 | 12 | use super::token; 13 | 14 | pub fn parse_literal(s: &mut Located<&str>) -> PResult { 15 | alt((parse_integer_literal, parse_format_specifier)).parse_next(s) 16 | } 17 | 18 | pub fn parse_integer_literal(s: &mut Located<&str>) -> PResult { 19 | alt((parse_hexadecimal, parse_binary, parse_decimal)).parse_next(s) 20 | } 21 | 22 | pub fn parse_decimal(s: &mut Located<&str>) -> PResult { 23 | may_have_underscores_between(one_of('0'..='9'), one_of('0'..='9')) 24 | .with_span() 25 | .map(token(TokenKind::LiteralIntegerDecimal)) 26 | .parse_next(s) 27 | } 28 | 29 | pub fn parse_hexadecimal(s: &mut Located<&str>) -> PResult { 30 | may_have_underscores_between("0x", one_of(('0'..='9', 'a'..='f', 'A'..='F'))) 31 | .with_span() 32 | .map(token(TokenKind::LiteralIntegerHexadecimal)) 33 | .parse_next(s) 34 | } 35 | 36 | pub fn parse_binary(s: &mut Located<&str>) -> PResult { 37 | may_have_underscores_between("0b", one_of(('0', '1'))) 38 | .with_span() 39 | .map(token(TokenKind::LiteralIntegerBinary)) 40 | .parse_next(s) 41 | } 42 | 43 | fn may_have_underscores_between<'a, HeadParser, Head, RestParser, Rest>( 44 | head: HeadParser, 45 | rest: RestParser, 46 | ) -> impl Parser, String, ContextError> 47 | where 48 | HeadParser: Parser, Head, ContextError>, 49 | Head: Display, 50 | RestParser: Parser, Rest, ContextError>, 51 | String: FromIterator, 52 | { 53 | (head, repeat(0.., preceded(take_while(0.., '_'), rest))) 54 | .map(|(h, t): (_, Vec<_>)| format!("{}{}", h, String::from_iter(t))) 55 | } 56 | 57 | pub fn parse_format_specifier(s: &mut Located<&str>) -> PResult { 58 | ( 59 | '`', 60 | cut_err(( 61 | repeat::<_, _, Vec<_>, _, _>(0.., parse_format_specifier_fragment), 62 | '`', 63 | )), 64 | ) 65 | .with_span() 66 | .map(|((_, (seq, _)), span)| { 67 | let content = format!( 68 | "`{}`", 69 | seq.iter().map(|frag| frag.to_string()).collect::() 70 | ); 71 | Token { 72 | kind: TokenKind::LiteralFormatSpecifier(FormatSpecifier(seq)), 73 | content, 74 | span, 75 | } 76 | }) 77 | .parse_next(s) 78 | } 79 | 80 | pub fn parse_format_specifier_fragment(s: &mut Located<&str>) -> PResult { 81 | alt(( 82 | repeat(1.., alt((' ', "\\n".map(|_| '\n')))).map(FormatSpecifierFragment::Whitespace), 83 | ('{', TokenKind::IdentifierIdentifier, '}') 84 | .map(|(_, ident, _)| FormatSpecifierFragment::Variable(ident.content)), 85 | repeat( 86 | 1.., 87 | take(1usize).verify(|s| !matches!(s, " " | "\\" | "{" | "}" | "`")), 88 | ) 89 | .fold( 90 | || String::new(), 91 | |mut acc, curr| { 92 | acc.push_str(curr); 93 | acc 94 | }, 95 | ) 96 | .map(FormatSpecifierFragment::Text), 97 | )) 98 | .parse_next(s) 99 | } 100 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/tokens/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Display, ops::Range}; 2 | 3 | use winnow::{ 4 | combinator::{alt, empty}, 5 | error::ContextError, 6 | token::any, 7 | Located, PResult, Parser, 8 | }; 9 | 10 | use crate::ast::{Token, TokenKind}; 11 | 12 | use self::{ 13 | identifier::parse_identifier_identifier, 14 | keyword::parse_keyword, 15 | literal::parse_literal, 16 | punctuations::parse_punctuations, 17 | whitespaces::{parse_whitespace_horizontal, parse_whitespace_vertical}, 18 | }; 19 | 20 | mod identifier; 21 | mod keyword; 22 | mod literal; 23 | mod punctuations; 24 | mod whitespaces; 25 | 26 | pub fn parse_token(s: &mut Located<&str>) -> PResult { 27 | alt(( 28 | parse_keyword, 29 | parse_punctuations, 30 | parse_whitespace_horizontal, 31 | parse_whitespace_vertical, 32 | parse_identifier_identifier, 33 | parse_literal, 34 | any.with_span().map(token(TokenKind::Error)), 35 | empty.value("").with_span().map(token(TokenKind::Eof)), 36 | )) 37 | .parse_next(s) 38 | } 39 | 40 | #[inline(always)] 41 | pub(super) fn token(kind: TokenKind) -> impl Fn((T, Range)) -> Token { 42 | move |(content, span)| Token { 43 | kind: kind.clone(), 44 | content: content.to_string(), 45 | span, 46 | } 47 | } 48 | 49 | impl Parser, Token, ContextError> for TokenKind { 50 | fn parse_next(&mut self, s: &mut Located<&str>) -> PResult { 51 | parse_token 52 | .verify(|token| token.kind == self.clone()) 53 | .parse_next(s) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/tokens/punctuations.rs: -------------------------------------------------------------------------------- 1 | use winnow::{combinator::alt, Located, PResult, Parser}; 2 | 3 | use crate::ast::{Token, TokenKind}; 4 | 5 | use super::token; 6 | 7 | pub fn parse_punctuations(s: &mut Located<&str>) -> PResult { 8 | // please sort alt by enum declaration order 9 | alt([ 10 | punct("!", TokenKind::PunctuationExclamationMark), 11 | punct("#", TokenKind::PunctuationNumberSign), 12 | punct("$", TokenKind::PunctuationDollarSign), 13 | punct("%", TokenKind::PunctuationPercentSign), 14 | punct("&", TokenKind::PunctuationAmpersand), 15 | punct("(", TokenKind::PunctuationLeftParenthesis), 16 | punct(")", TokenKind::PunctuationRightParenthesis), 17 | punct("*", TokenKind::PunctuationAsterisk), 18 | punct("+", TokenKind::PunctuationPlusSign), 19 | punct(",", TokenKind::PunctuationComma), 20 | punct("-", TokenKind::PunctuationHyphenMinus), 21 | punct(".", TokenKind::PunctuationFullStop), 22 | punct("/", TokenKind::PunctuationSolidus), 23 | punct(":", TokenKind::PunctuationColon), 24 | punct(";", TokenKind::PunctuationSemicolon), 25 | punct("<", TokenKind::PunctuationLessThanSign), 26 | punct("=", TokenKind::PunctuationEqualsSign), 27 | punct(">", TokenKind::PunctuationGreaterThanSign), 28 | punct("?", TokenKind::PunctuationQuestionMark), 29 | punct("@", TokenKind::PunctuationCommercialAt), 30 | punct("[", TokenKind::PunctuationLeftSquareBracket), 31 | punct("\\", TokenKind::PunctuationReverseSolidus), 32 | punct("]", TokenKind::PunctuationRightSquareBracket), 33 | punct("^", TokenKind::PunctuationCircumflexAccent), 34 | punct("_", TokenKind::PunctuationLowLine), 35 | punct("{", TokenKind::PunctuationLeftCurlyBracket), 36 | punct("|", TokenKind::PunctuationVerticalLine), 37 | punct("}", TokenKind::PunctuationRightCurlyBracket), 38 | punct("~", TokenKind::PunctuationTilde), 39 | ]) 40 | .parse_next(s) 41 | } 42 | 43 | fn punct( 44 | punct: &str, 45 | kind: TokenKind, 46 | ) -> impl for<'a> Fn(&mut Located<&'a str>) -> PResult + '_ { 47 | move |s| punct.with_span().map(token(kind.clone())).parse_next(s) 48 | } 49 | -------------------------------------------------------------------------------- /crates/psl/src/syntax/tokens/whitespaces.rs: -------------------------------------------------------------------------------- 1 | use winnow::{token::take_while, Located, PResult, Parser}; 2 | 3 | use crate::ast::{Token, TokenKind}; 4 | 5 | use super::token; 6 | 7 | pub fn parse_whitespace_horizontal(s: &mut Located<&str>) -> PResult { 8 | take_while( 9 | 1.., 10 | [ 11 | '\u{0009}', // CHARACTER TABULATION 12 | '\u{0020}', // SPACE 13 | '\u{00AD}', // SOFT HYPHEN 14 | '\u{00A0}', // NO-BREAK SPACE 15 | '\u{1680}', // OGHAM SPACE MARK 16 | '\u{2000}', // EN QUAD 17 | '\u{2001}', // EM QUAD 18 | '\u{2002}', // EN SPACE 19 | '\u{2003}', // EM SPACE 20 | '\u{2004}', // THREE-PER-EM SPACE 21 | '\u{2005}', // FOUR-PER-EM SPACE 22 | '\u{2006}', // SIX-PER-EM SPACE 23 | '\u{2007}', // FIGURE SPACE 24 | '\u{2008}', // PUNCTUATION SPACE 25 | '\u{2009}', // THIN SPACE 26 | '\u{200A}', // HAIR SPACE 27 | '\u{200B}', // ZERO WIDTH SPACE 28 | '\u{200E}', // LEFT-TO-RIGHT MARK 29 | '\u{200F}', // RIGHT-TO-LEFT MARK 30 | '\u{202F}', // NARROW NO-BREAK SPACE 31 | '\u{205F}', // MEDIUM MATHEMATICAL SPACE 32 | '\u{3000}', // IDEPGRAPHIC SPACE 33 | '\u{FEFF}', // ZERO WIDTH NO-BREAK SPACE 34 | ], 35 | ) 36 | .with_span() 37 | .map(token(TokenKind::WhitespaceHorizontal)) 38 | .parse_next(s) 39 | } 40 | 41 | pub fn parse_whitespace_vertical(s: &mut Located<&str>) -> PResult { 42 | take_while( 43 | 1.., 44 | [ 45 | '\u{000A}', // LINE FEED 46 | '\u{000B}', // LINE TABULATION 47 | '\u{000C}', // FORM FEED 48 | '\u{000D}', // CARRIAGE RETURN 49 | '\u{0085}', // NEXT LINE 50 | '\u{2028}', // LINE SEPARATOR 51 | '\u{2029}', // PARAGRAPH SEPARATOR 52 | ], 53 | ) 54 | .with_span() 55 | .map(token(TokenKind::WhitespaceVertical)) 56 | .parse_next(s) 57 | } 58 | 59 | #[cfg(test)] 60 | mod test { 61 | use pretty_assertions::assert_eq; 62 | use winnow::{Located, Parser}; 63 | 64 | use crate::{ 65 | ast::{Token, TokenKind}, 66 | syntax::tokens::whitespaces::{parse_whitespace_horizontal, parse_whitespace_vertical}, 67 | }; 68 | 69 | #[test] 70 | fn test_horizontal() { 71 | assert_eq!( 72 | parse_whitespace_horizontal.parse(Located::new(" \t")), 73 | Ok(Token { 74 | kind: TokenKind::WhitespaceHorizontal, 75 | span: 0..2, 76 | content: " \t".to_string() 77 | }), 78 | ) 79 | } 80 | 81 | #[test] 82 | fn test_vertical() { 83 | assert_eq!( 84 | parse_whitespace_vertical.parse(Located::new("\n\r")), 85 | Ok(Token { 86 | kind: TokenKind::WhitespaceVertical, 87 | span: 0..2, 88 | content: "\n\r".to_string() 89 | }), 90 | ) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /snippets/10xx/p1000.psl: -------------------------------------------------------------------------------- 1 | i32 a = read i32 2 | i32 b = read i32 3 | i32 sum = a + b 4 | write sum 5 | -------------------------------------------------------------------------------- /snippets/24xx/p2420.psl: -------------------------------------------------------------------------------- 1 | i32 N = read i32 2 | i32 M = read i32 3 | i32 diff = if N < M { M - N } else { N - M } 4 | write diff -------------------------------------------------------------------------------- /snippets/25xx/p2557.psl: -------------------------------------------------------------------------------- 1 | write `Hello World!` 2 | -------------------------------------------------------------------------------- /snippets/26xx/p2609.psl: -------------------------------------------------------------------------------- 1 | i32 a = read i32 2 | i32 b = read i32 3 | i32 gcd = eval_gcd(a, b) 4 | i32 lcm = a * b / gcd 5 | write `{gcd}\n{lcm}` 6 | 7 | fn eval_gcd(i32 a, i32 b) i32 { 8 | if a > b { 9 | return eval_gcd(b, a) 10 | } 11 | if a == 0 { 12 | return b 13 | } 14 | return eval_gcd(b % a, a) 15 | } 16 | -------------------------------------------------------------------------------- /snippets/27xx/p2747.psl: -------------------------------------------------------------------------------- 1 | i32 pp = 0 2 | i32 p = 1 3 | 4 | i32 n = read i32 5 | while n > 0 { 6 | i32 tmp = pp + p 7 | pp = p 8 | p = tmp 9 | n = n - 1 10 | } 11 | 12 | write pp -------------------------------------------------------------------------------- /snippets/27xx/p2753.psl: -------------------------------------------------------------------------------- 1 | i32 year = read i32 2 | i32 is_leap = if year % 4 == 0 { 3 | if year % 400 == 0 { 4 | 1 5 | } else { 6 | if year % 100 == 0 { 7 | 0 8 | } else { 9 | 1 10 | } 11 | } 12 | } else { 13 | 0 14 | } 15 | 16 | write is_leap --------------------------------------------------------------------------------