├── .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
--------------------------------------------------------------------------------