69 | where
70 | T: Debug,
71 | {
72 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 | self.0.fmt(f)
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/docs/src/c0/token.md:
--------------------------------------------------------------------------------
1 | # 单词 (Token)
2 |
3 | 单词是词法分析的结果。
4 |
5 | ## 关键字
6 |
7 | ```
8 | FN_KW -> 'fn'
9 | LET_KW -> 'let'
10 | CONST_KW -> 'const'
11 | AS_KW -> 'as'
12 | WHILE_KW -> 'while'
13 | IF_KW -> 'if'
14 | ELSE_KW -> 'else'
15 | RETURN_KW -> 'return'
16 |
17 | // 这两个是扩展 c0 的
18 | BREAK_KW -> 'break'
19 | CONTINUE_KW -> 'continue'
20 | ```
21 |
22 | c0 有 8 个关键字。扩展 c0 增加了 2 个关键字。
23 |
24 | # 字面量
25 |
26 | ```
27 | digit -> [0-9]
28 | UINT_LITERAL -> digit+
29 |
30 | escape_sequence -> '\' [\\"'nrt]
31 | string_regular_char -> [^"\\]
32 | STRING_LITERAL -> '"' (string_regular_char | escape_sequence)* '"'
33 |
34 | // 扩展 c0
35 | DOUBLE_LITERAL -> digit+ '.' digit+ ([eE] [+-]? digit+)?
36 |
37 | char_regular_char -> [^'\\]
38 | CHAR_LITERAL -> '\'' (char_regular_char | escape_sequence) '\''
39 | ```
40 |
41 | 基础 c0 有两种字面量,分别是 _无符号整数_ 和 _字符串常量_。扩展 c0 增加了 _浮点数常量_ 和 _字符常量_。
42 |
43 | 语义约束:
44 |
45 | - 字符串字面量中的字符可以是 ASCII 中除了双引号 `"`、反斜线 `\\`、空白符 `\r` `\n` `\t` 以外的任何字符。转义序列可以是 `\'`、`\"`、`\\`、`\n`、`\t`、`\r`,含义与 C 中的对应序列相同。
46 |
47 | > UB: 对于无符号整数和浮点数常量超出相应数据类型表示范围的情况我们不做规定。你可以选择报错也可以选择无视。
48 |
49 | ## 标识符
50 |
51 | ```
52 | IDENT -> [_a-zA-Z] [_a-zA-Z0-9]*
53 | ```
54 |
55 | c0 的标识符由下划线或字母开头,后面可以接零或多个下划线、字母或数字。标识符不能和关键字重复。
56 |
57 | ## 运算符
58 |
59 | ```
60 | PLUS -> '+'
61 | MINUS -> '-'
62 | MUL -> '*'
63 | DIV -> '/'
64 | ASSIGN -> '='
65 | EQ -> '=='
66 | NEQ -> '!='
67 | LT -> '<'
68 | GT -> '>'
69 | LE -> '<='
70 | GE -> '>='
71 | L_PAREN -> '('
72 | R_PAREN -> ')'
73 | L_BRACE -> '{'
74 | R_BRACE -> '}'
75 | ARROW -> '->'
76 | COMMA -> ','
77 | COLON -> ':'
78 | SEMICOLON -> ';'
79 | ```
80 |
81 | ## 注释
82 |
83 | 注释是扩展 c0 内容,见 [扩展 c0](extended-c0.md#注释)
84 |
85 | ```
86 | COMMENT -> '//' regex(.*) '\n'
87 | ```
88 |
--------------------------------------------------------------------------------
/crates/r0codegen/src/ty.rs:
--------------------------------------------------------------------------------
1 | use r0syntax::util::P;
2 |
3 | #[derive(Debug, Clone, Eq, PartialEq)]
4 | pub enum Ty {
5 | Int,
6 | Double,
7 | Bool,
8 | Addr,
9 | Func(FuncTy),
10 | Void,
11 | }
12 |
13 | #[derive(Debug, Clone, Eq, PartialEq)]
14 | pub struct FuncTy {
15 | pub params: Vec>,
16 | pub ret: P,
17 | }
18 |
19 | impl Ty {
20 | pub fn size(&self) -> usize {
21 | match self {
22 | Ty::Int | Ty::Double | Ty::Addr => 8,
23 | Ty::Bool => 1,
24 | Ty::Func(_) => 0,
25 | Ty::Void => 0,
26 | }
27 | }
28 |
29 | pub fn size_slot(&self) -> usize {
30 | match self {
31 | Ty::Int | Ty::Double | Ty::Bool | Ty::Addr => 1,
32 | Ty::Func(_) => 0,
33 | Ty::Void => 0,
34 | }
35 | }
36 |
37 | pub fn get_func(&self) -> Option<&FuncTy> {
38 | match self {
39 | Ty::Func(f) => Some(f),
40 | _ => None,
41 | }
42 | }
43 | }
44 |
45 | impl std::fmt::Display for Ty {
46 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 | match self {
48 | Ty::Int => write!(f, "int"),
49 | Ty::Double => write!(f, "double"),
50 | Ty::Bool => write!(f, "bool"),
51 | Ty::Addr => write!(f, "addr"),
52 | Ty::Func(ty) => {
53 | write!(f, "Fn(")?;
54 | let mut param_iter = ty.params.iter();
55 |
56 | if let Some(r) = param_iter.next() {
57 | write!(f, "{}", r)?;
58 | }
59 | for r in param_iter {
60 | write!(f, ", {}", r)?;
61 | }
62 |
63 | write!(f, ") -> {}", ty.ret)
64 | }
65 | Ty::Void => write!(f, "void"),
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/crates/r0vm/src/lib.rs:
--------------------------------------------------------------------------------
1 | // #![feature(map_first_last)]
2 | #![allow(clippy::transmute_int_to_float)]
3 |
4 | pub mod error;
5 | pub mod opcodes;
6 | pub mod s0;
7 | #[cfg(test)]
8 | mod tests;
9 | mod util;
10 | #[cfg(feature = "vm")]
11 | pub mod vm;
12 |
13 | #[macro_export]
14 | /// Create an in-memory representation for s0 binary
15 | macro_rules! s0_bin {
16 | (
17 | $(
18 | // TODO: global variable declaration
19 | const $const_val:expr;
20 | )*
21 | $(
22 | // TODO: global variable declaration
23 | let $val:expr;
24 | )*
25 | $(
26 | fn $name:ident $loc_slots:literal $param:literal -> $ret:literal {
27 | $($inst:expr $(,)?)*
28 | }
29 | )+
30 | ) => {{
31 | use $crate::opcodes::Op::*;
32 | use $crate::util::IntoBytes;
33 | let mut globals = vec![];
34 |
35 | $({
36 | let bytes = $const_val.into_bytes();
37 | let glob = GlobalValue {
38 | is_const: true,
39 | bytes
40 | };
41 | globals.push(glob);
42 | })*
43 | $({
44 | let bytes = $val.into_bytes();
45 | let glob = GlobalValue {
46 | is_const: false,
47 | bytes
48 | };
49 | globals.push(glob);
50 | })*
51 |
52 | let mut fns = vec![];
53 | $({
54 | let name = stringify!($name);
55 | let bytes = name.into_bytes();
56 | let glob = GlobalValue{ is_const:true, bytes };
57 | let name_idx = globals.len();
58 | globals.push(glob);
59 |
60 | let loc_slots = $loc_slots;
61 | let inst = vec![$($inst),*];
62 | let func = FnDef{
63 | name: name_idx as u32,
64 | loc_slots,
65 | param_slots: $param,
66 | ret_slots: $ret,
67 | ins: inst,
68 | };
69 | fns.push(func);
70 | })+
71 | let s0 = S0{
72 | globals,
73 | functions: fns,
74 | };
75 | s0
76 | }};
77 | }
78 |
--------------------------------------------------------------------------------
/crates/syntax/src/span.rs:
--------------------------------------------------------------------------------
1 | use std::{fmt::Debug, ops::Index};
2 |
3 | /// A Span is the information of a piece of source code inside a file.
4 | ///
5 | /// `Span`s are only meaningful when indexing the file it is originated from.
6 | #[derive(Clone, Copy, Eq, PartialEq)]
7 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8 | pub struct Span {
9 | /// The start index (in bytes or other meaningful item index)
10 | /// in the file of this span
11 | pub idx: usize,
12 |
13 | /// The length of the span
14 | pub len: usize,
15 | }
16 |
17 | pub const DUMMY_SPAN: Span = Span {
18 | // ctx: usize::max_value(),
19 | idx: 0,
20 | len: 0,
21 | };
22 |
23 | impl Span {
24 | pub fn start(&self) -> usize {
25 | self.idx
26 | }
27 |
28 | pub fn end(&self) -> usize {
29 | self.idx + self.len
30 | }
31 |
32 | pub fn new(idx: usize, len: usize) -> Span {
33 | Span { idx, len }
34 | }
35 |
36 | pub fn new_idx(lo: usize, hi: usize) -> Span {
37 | let (lo, hi) = if lo > hi { (hi, lo) } else { (lo, hi) };
38 | let len = hi - lo;
39 | Span { idx: lo, len }
40 | }
41 |
42 | pub const fn eof() -> Span {
43 | Span {
44 | idx: usize::max_value(),
45 | len: 0,
46 | }
47 | }
48 | }
49 |
50 | impl std::ops::Add for Span {
51 | type Output = Span;
52 |
53 | fn add(self, rhs: Self) -> Self::Output {
54 | let start = std::cmp::min(self.start(), rhs.start());
55 | let end = std::cmp::max(self.end(), rhs.end());
56 | Span::new_idx(start, end)
57 | }
58 | }
59 |
60 | impl std::ops::AddAssign for Span {
61 | fn add_assign(&mut self, rhs: Self) {
62 | *self = *self + rhs
63 | }
64 | }
65 |
66 | impl Debug for Span {
67 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 | write!(f, "[{}, {})", self.idx, self.idx + self.len)
69 | }
70 | }
71 |
72 | impl Default for Span {
73 | fn default() -> Self {
74 | DUMMY_SPAN
75 | }
76 | }
77 |
78 | impl Index for Vec {
79 | type Output = [T];
80 | fn index(&self, index: Span) -> &Self::Output {
81 | &self[index.idx..(index.idx + index.len)]
82 | }
83 | }
84 |
85 | impl From for Span {
86 | fn from(s: logos::Span) -> Self {
87 | Span::new_idx(s.start, s.end)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/util.rs:
--------------------------------------------------------------------------------
1 | use r0syntax::span::Span;
2 | use std::io::Write;
3 | use unicode_width::UnicodeWidthStr;
4 |
5 | pub fn pretty_print_error(
6 | writer: &mut dyn Write,
7 | input: &str,
8 | error: &str,
9 | span: Span,
10 | ) -> Result<(), std::io::Error> {
11 | writeln!(writer, "{}", error)?;
12 |
13 | if span == Span::eof() {
14 | let line = input.lines().last().unwrap_or("");
15 | writeln!(writer, "{}", line)?;
16 | writeln!(writer, "{:space_width$}^", space_width = line.width())?;
17 |
18 | Ok(())
19 | } else {
20 | let start = line_span::find_line_range(input, span.start());
21 | let end = line_span::find_line_range(input, span.end());
22 |
23 | if let Some(line) = line_span::find_prev_line_range(input, span.start()) {
24 | writeln!(writer, "{}", &input[line])?;
25 | }
26 | if start == end {
27 | writeln!(writer, "{}", &input[start.clone()])?;
28 | writeln!(
29 | writer,
30 | "{:space_width$}{:^^line_width$}",
31 | "",
32 | "",
33 | space_width = input[start.start..span.start()].width(),
34 | line_width = input[span.start()..span.end()].width()
35 | )?;
36 | } else {
37 | let print_range = start.start..end.end;
38 | let input_range = input[print_range].lines().collect::>();
39 |
40 | writeln!(writer, "{}", input_range[0])?;
41 | writeln!(
42 | writer,
43 | "{:space_width$}{:^^line_width$}",
44 | "",
45 | "",
46 | space_width = input[start.start..span.start()].width(),
47 | line_width = input[span.start()..start.end].width()
48 | )?;
49 | for i in 1..(input_range.len() - 1) {
50 | writeln!(writer, "{}", input_range[i])?;
51 | writeln!(writer, "{:^^len$}", "", len = input_range[i].width())?;
52 | }
53 | writeln!(writer, "{}", input_range[input_range.len() - 1])?;
54 | writeln!(
55 | writer,
56 | "{:^^line_width$}",
57 | "",
58 | line_width = input[end.start..(span.end())].width()
59 | )?;
60 | }
61 | if let Some(line) = line_span::find_next_line_range(input, span.end()) {
62 | writeln!(writer, "{}", &input[line])?;
63 | }
64 | Ok(())
65 | }
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/crates/r0vm/src/s0/mod.rs:
--------------------------------------------------------------------------------
1 | // #[cfg(parse)]
2 | pub mod io;
3 |
4 | use crate::opcodes::Op;
5 | #[cfg(feature = "serde")]
6 | use serde::{Deserialize, Serialize};
7 | use std::{collections::HashMap, fmt::Display};
8 |
9 | /// S0 Assembly for use in R0VM
10 | #[derive(Debug, PartialEq, Eq, Clone)]
11 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12 | #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
13 | pub struct S0 {
14 | pub globals: Vec,
15 | pub functions: Vec,
16 | }
17 |
18 | impl Display for S0 {
19 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 | for global in &self.globals {
21 | writeln!(f, "{}", global)?;
22 | }
23 | writeln!(f)?;
24 | for func in &self.functions {
25 | writeln!(f, "{}", func)?;
26 | }
27 | Ok(())
28 | }
29 | }
30 |
31 | /// Global variable or constant, described by bytes, addressed by ID
32 | #[derive(Debug, PartialEq, Eq, Clone)]
33 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34 | #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
35 | pub struct GlobalValue {
36 | pub is_const: bool,
37 | pub bytes: Vec,
38 | }
39 |
40 | impl Display for GlobalValue {
41 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 | if self.is_const {
43 | write!(f, "const:")?;
44 | } else {
45 | write!(f, "static:")?;
46 | }
47 | for byte in &self.bytes {
48 | write!(f, " {:X}", byte)?;
49 | }
50 | if let Ok(s) = String::from_utf8(self.bytes.clone()) {
51 | write!(f, " (`{}`)", s.escape_default())?;
52 | }
53 | writeln!(f)
54 | }
55 | }
56 |
57 | /// Function definition
58 | #[derive(Debug, PartialEq, Eq, Clone)]
59 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
60 | #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
61 | pub struct FnDef {
62 | pub name: u32,
63 | pub ret_slots: u32,
64 | pub param_slots: u32,
65 | pub loc_slots: u32,
66 | pub ins: Vec,
67 | }
68 |
69 | impl Display for FnDef {
70 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 | writeln!(
72 | f,
73 | "fn [{}] {} {} -> {} {{",
74 | self.name, self.loc_slots, self.param_slots, self.ret_slots
75 | )?;
76 | for (idx, op) in self.ins.iter().enumerate() {
77 | writeln!(f, "{:5}: {:?}", idx, op)?;
78 | }
79 | writeln!(f, "}}")
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/docs/src/c0/method_notes.md:
--------------------------------------------------------------------------------
1 | # 实现方法指导
2 |
3 | 实现一个 parser 有很多种方法,这里会提供一些关于代码实现(而不是理论)的方法指导。
4 |
5 | 对于没有讲到的内容,可以参考 [去年的指导书](https://github.com/BUAA-SE-Compiling/c0-handbook#33-%E5%AE%9E%E7%8E%B0%E6%8C%87%E5%BC%95)
6 |
7 | ## 一些通用的部分
8 |
9 | ### 类型定义
10 |
11 | 对于词法、语法分析时用到的类型,因为类型确定且已知,可以使用继承实现。在支持和类型 (sum type) 的语言里也可以用和类型实现。这样做可以显著降低判断 token 或者语法树节点类型时的工作量,因为可以直接判断变量本身的类型,甚至直接进行模式匹配。比如:
12 |
13 | ```csharp
14 | /* 词法分析器 */
15 |
16 | class Token {}
17 |
18 | class NumberToken : Token {
19 | public double value;
20 | }
21 |
22 | // ...
23 |
24 | /* 语法分析器 */
25 |
26 | class Expr {}
27 |
28 | class Literal : Expr {}
29 |
30 | class IntegerLiteral : Literal {
31 | public long value;
32 | }
33 |
34 | class StringLiteral : Literal {
35 | public string value;
36 | }
37 |
38 | class BinaryExpr : Expr {
39 | public Operator op;
40 | public Expr lhs;
41 | public Expr rhs;
42 | }
43 |
44 | // ...
45 | ```
46 |
47 | 或者在支持的语言里使用带标签的联合类型:
48 |
49 | ```rust,ignore
50 | enum Expr {
51 | Literal(LiteralExpr),
52 | Binary(BinaryExpr),
53 | // ...
54 | }
55 |
56 | enum LiteralExpr {
57 | Integer(i64),
58 | String(String),
59 | // ...
60 | }
61 |
62 | struct BinaryExpr {
63 | pub op: Operator,
64 | pub lhs: Ptr,
65 | pub rhs: Ptr,
66 | }
67 |
68 | // ...
69 | ```
70 |
71 | ### 迭代器
72 |
73 | 迭代器(Iterator)是对一系列值的抽象,比如说一列输入的字符或者解析完的 token。使用迭代器可以有效地将输入数据和对数据的获取操作解耦,方便在不同时候使用不同方式输入数据,以及进行测试。常见高级语言都有对于迭代器的抽象,包括:
74 |
75 | - Java: `java.util.Iterator`
76 | - C#: `System.Collections.Generic.IEnumerator`
77 | - C++: `std::iterator::iterator_traits`
78 | - C++20: concept `std::ranges::input_iterator`
79 | - Python: 实现 `__next__` 的类型
80 | - JavaScript: 实现 `Symbol.iterator` 的类型
81 |
82 | 由于在解析时常常要回溯,使用的迭代器可以提供一些额外的方法,比如 `peek()` 用于查看下一个值但不移动迭代器,或者 `unread(value)` 用于将已经读出的值放回迭代器。
83 |
84 | ## 词法分析
85 |
86 | 词法分析这个主题比较简单,基本上就是对于每个 token 使用自动机(或者退化成普通的逻辑分支)进行解析。token 的组成一般比较简单,可以在分析时参考正则表达式的状态来设计自动机或逻辑分支。
87 |
88 | 当然,也有一些库允许你直接用正则表达式定义 token 来进行自动分析。好耶。
89 |
90 | > 不要学助教[用逻辑分支模拟自动机][bad_lexing](逃
91 |
92 | [bad_lexing]: https://github.com/01010101lzy/chigusa/blob/0a08176f4318542c1bb96114ac3f0df56ac9510d/src/c0/lexer.rs#L392-L511
93 |
94 | ## 语法分析
95 |
96 | ### 普通的递归下降分析法
97 |
98 | 递归下降是一个很简单、很直观的分析法,也是大多数人实现语法分析的首选方法。在实现递归下降分析器的时候,有一些可以降低编码难度的方法。
99 |
100 | #### 使用迭代器和辅助函数
101 |
102 | 看 miniplc0 java 版本基本上就够了(逃)
103 |
104 | #### 解析器组合子 (Parser Combinator)
105 |
106 | 助教没有试过这么写,如果你用 Haskell 来写的话或许可以试试 `parsec` 这个库。
107 |
108 | ### 使用 LL/LR 解析器生成器
109 |
110 | 自动生成解析器代码总感觉有点作弊的意思,不过用了就用了吧(笑)。如果你确定要用的话,记得选一个好用的,比如 [ANTLR][]。
111 |
112 | [antlr]: https://www.antlr.org
113 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 | build-judger:
10 | strategy:
11 | matrix:
12 | include:
13 | # - os: ubuntu-latest
14 | # artifact_name: rurikawa
15 | # asset_name: rurikawa-linux-amd64-gnu
16 | # # target_name: x86_64-unknown-linux-gnu
17 | - os: ubuntu-latest
18 | artifact_name: natrium
19 | artifact_name_2: navm
20 | asset_name: natrium-linux-amd64-musl
21 | asset_name_2: navm-linux-amd64-musl
22 | target_name: x86_64-unknown-linux-musl
23 | - os: windows-latest
24 | artifact_name: natrium.exe
25 | artifact_name_2: navm.exe
26 | asset_name: natrium-windows-amd64.exe
27 | asset_name_2: navm-windows-amd64.exe
28 | - os: macos-latest
29 | artifact_name: natrium
30 | artifact_name_2: navm
31 | asset_name: natrium-macos-amd64
32 | asset_name_2: navm-macos-amd64
33 |
34 | runs-on: ${{ matrix.os }}
35 | steps:
36 | - uses: actions/checkout@v2
37 | with:
38 | submodules: true
39 |
40 | - name: Build rust
41 | if: ${{ matrix.target_name == 0 }}
42 | run: |
43 | cargo fetch
44 | cargo build --frozen --release
45 |
46 | - name: install musl tools
47 | if: ${{ matrix.target_name == 'x86_64-unknown-linux-musl' }}
48 | run: sudo apt update && sudo apt install musl musl-dev musl-tools
49 |
50 | - name: Install rust target
51 | if: ${{ matrix.target_name != 0 }}
52 | continue-on-error: true
53 | run: rustup target add ${{ matrix.target_name }}
54 |
55 | - name: Build rust (targeted)
56 | if: ${{ matrix.target_name != 0 }}
57 | run: |
58 | cargo fetch
59 | cargo build --frozen --release --target ${{ matrix.target_name }}
60 |
61 | - uses: haya14busa/action-cond@v1
62 | id: condval
63 | with:
64 | cond: ${{ matrix.target_name != 0 }}
65 | if_true: target/${{matrix.target_name}}/release
66 | if_false: target/release
67 |
68 | - name: Upload binaries to release
69 | uses: svenstaro/upload-release-action@v1-release
70 | with:
71 | repo_token: ${{ secrets.GITHUB_TOKEN }}
72 | file: ${{ steps.condval.outputs.value }}/${{ matrix.artifact_name }}
73 | asset_name: ${{ matrix.asset_name }}
74 | tag: ${{ github.ref }}
75 | overwrite: true
76 | - name: Upload binaries to release
77 | uses: svenstaro/upload-release-action@v1-release
78 | with:
79 | repo_token: ${{ secrets.GITHUB_TOKEN }}
80 | file: ${{ steps.condval.outputs.value }}/${{ matrix.artifact_name_2 }}
81 | asset_name: ${{ matrix.asset_name_2 }}
82 | tag: ${{ github.ref }}
83 | overwrite: true
84 |
--------------------------------------------------------------------------------
/crates/r0codegen/src/generator/util.rs:
--------------------------------------------------------------------------------
1 | use bit_set::BitSet;
2 | use r0syntax::span::Span;
3 |
4 | use crate::{
5 | code::BasicBlock,
6 | err::{CompileError, CompileErrorKind},
7 | };
8 |
9 | use super::CompileResult;
10 |
11 | /// Cycle Finding state variable
12 | #[derive(Debug)]
13 | pub struct BBArranger<'st> {
14 | bb: &'st [BasicBlock],
15 | path: BitSet,
16 | vis: BitSet,
17 | in_degree: Vec,
18 | arr: Vec,
19 | }
20 |
21 | impl<'st> BBArranger<'st> {
22 | pub fn new(bb: &'st [BasicBlock]) -> BBArranger<'st> {
23 | BBArranger {
24 | bb,
25 | path: BitSet::new(),
26 | vis: BitSet::new(),
27 | in_degree: vec![0; bb.len()],
28 | arr: vec![],
29 | }
30 | }
31 |
32 | pub fn construct_arrangement(&mut self, start: usize) -> CompileResult<()> {
33 | self.vis(start);
34 | self.arr(start)
35 | }
36 |
37 | pub fn vis(&mut self, id: usize) {
38 | if self.path.contains(id) {
39 | // cycle does not count
40 | return;
41 | }
42 | self.in_degree[id] += 1;
43 | if self.vis.contains(id) {
44 | // visited node
45 | return;
46 | }
47 | self.vis.insert(id);
48 | self.path.insert(id);
49 | match self.bb[id].jump {
50 | crate::code::JumpInst::Jump(bb1) => {
51 | self.vis(bb1);
52 | }
53 | crate::code::JumpInst::JumpIf(bb1, bb2) => {
54 | self.vis(bb1);
55 | self.vis(bb2);
56 | }
57 | _ => {}
58 | }
59 | self.path.remove(id);
60 | }
61 |
62 | pub fn arr(&mut self, id: usize) -> CompileResult<()> {
63 | if self.path.contains(id) {
64 | // cycle does not count
65 | return Ok(());
66 | }
67 | self.in_degree[id] = self.in_degree[id]
68 | .checked_sub(1)
69 | .unwrap_or_else(|| panic!("id: {}, in_degrees: {:?}", id, &self.in_degree));
70 | if self.in_degree[id] != 0 {
71 | return Ok(());
72 | }
73 |
74 | self.arr.push(id);
75 | self.path.insert(id);
76 |
77 | match self.bb[id].jump {
78 | crate::code::JumpInst::Jump(bb1) => {
79 | self.arr(bb1)?;
80 | }
81 | crate::code::JumpInst::JumpIf(bb1, bb2) => {
82 | self.arr(bb1)?;
83 | self.arr(bb2)?;
84 | }
85 | crate::code::JumpInst::Return => {}
86 | crate::code::JumpInst::Unreachable => panic!(
87 | "Unreachable basic block {} being visited; block map:\n {:#?}",
88 | id, self.bb
89 | ),
90 | crate::code::JumpInst::Undefined => {
91 | return Err(CompileError(CompileErrorKind::NotAllRoutesReturn, None))
92 | }
93 | }
94 | self.path.remove(id);
95 | Ok(())
96 | }
97 |
98 | pub fn arrange(self) -> Vec {
99 | self.arr
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/crates/r0codegen/src/scope.rs:
--------------------------------------------------------------------------------
1 | use std::{cell::RefCell, sync::atomic::AtomicU64};
2 |
3 | use indexmap::IndexMap;
4 | use smol_str::SmolStr;
5 |
6 | use crate::ty::Ty;
7 |
8 | #[derive(Debug)]
9 | pub struct SymbolIdGenerator {
10 | next_id: u64,
11 | }
12 |
13 | impl SymbolIdGenerator {
14 | pub fn new() -> SymbolIdGenerator {
15 | SymbolIdGenerator { next_id: 0 }
16 | }
17 |
18 | pub fn next(&mut self) -> u64 {
19 | let id = self.next_id;
20 | self.next_id += 1;
21 | id
22 | }
23 | }
24 |
25 | #[derive(Debug)]
26 | pub struct Scope<'p> {
27 | symbol_gen: &'p RefCell,
28 | pub parent: Option<&'p Scope<'p>>,
29 | pub vars: IndexMap,
30 | }
31 |
32 | #[allow(clippy::new_without_default)]
33 | impl<'p> Scope<'p> {
34 | pub fn new_with_parent(parent: &'p Scope<'p>) -> Scope<'p> {
35 | Scope {
36 | symbol_gen: parent.symbol_gen,
37 | parent: Some(parent),
38 | vars: IndexMap::new(),
39 | }
40 | }
41 |
42 | pub fn new(symbol_gen: &'p RefCell) -> Scope<'p> {
43 | Scope {
44 | symbol_gen,
45 | parent: None,
46 | vars: IndexMap::new(),
47 | }
48 | }
49 |
50 | pub fn find_in_self(&self, ident: &str) -> Option<&Symbol> {
51 | self.vars.get(ident)
52 | }
53 |
54 | pub fn find<'s>(&'s self, ident: &str) -> Option<&'s Symbol> {
55 | let self_res = self.find_in_self(ident);
56 |
57 | if self_res.is_none() {
58 | if let Some(p) = self.parent {
59 | return p.find(ident);
60 | }
61 | }
62 | self_res
63 | }
64 |
65 | pub fn is_root_scope(&self) -> bool {
66 | self.parent.is_none()
67 | }
68 |
69 | pub fn find_is_global<'s>(&'s self, ident: &str) -> Option<(&'s Symbol, bool)> {
70 | let self_res = self.find_in_self(ident);
71 |
72 | if self_res.is_none() {
73 | if let Some(p) = self.parent {
74 | return p.find_is_global(ident);
75 | }
76 | }
77 |
78 | self_res.map(|x| (x, self.is_root_scope()))
79 | }
80 |
81 | pub fn insert(&mut self, ident: SmolStr, mut symbol: Symbol) -> Option {
82 | let entry = self.vars.entry(ident);
83 | match entry {
84 | indexmap::map::Entry::Occupied(_) => None,
85 | indexmap::map::Entry::Vacant(v) => {
86 | let id = self.symbol_gen.borrow_mut().next();
87 | symbol.id = id;
88 | v.insert(symbol);
89 | Some(id)
90 | }
91 | }
92 | }
93 |
94 | pub fn get_new_id(&self) -> u64 {
95 | self.symbol_gen.borrow_mut().next()
96 | }
97 | }
98 |
99 | #[derive(Debug, Clone)]
100 | pub struct Symbol {
101 | pub id: u64,
102 | pub ty: Ty,
103 | pub is_const: bool,
104 | }
105 |
106 | impl Symbol {
107 | pub fn new(ty: Ty, is_const: bool) -> Symbol {
108 | Symbol {
109 | ty,
110 | is_const,
111 | id: 0,
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // 使用 IntelliSense 了解相关属性。
3 | // 悬停以查看现有属性的描述。
4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "lldb",
9 | "request": "launch",
10 | "name": "Debug unit tests in library 'r0codegen'",
11 | "cargo": {
12 | "args": [
13 | "test",
14 | "--no-run",
15 | "--lib",
16 | "--package=r0codegen"
17 | ],
18 | "filter": {
19 | "name": "r0codegen",
20 | "kind": "lib"
21 | }
22 | },
23 | "args": [],
24 | "cwd": "${workspaceFolder}"
25 | },
26 | {
27 | "type": "lldb",
28 | "request": "launch",
29 | "name": "Debug unit tests in library 'r0syntax'",
30 | "cargo": {
31 | "args": [
32 | "test",
33 | "--no-run",
34 | "--lib",
35 | "--package=r0syntax"
36 | ],
37 | "filter": {
38 | "name": "r0syntax",
39 | "kind": "lib"
40 | }
41 | },
42 | "args": [],
43 | "cwd": "${workspaceFolder}"
44 | },
45 | {
46 | "type": "lldb",
47 | "request": "launch",
48 | "name": "Debug unit tests in library 'r0vm'",
49 | "cargo": {
50 | "args": [
51 | "test",
52 | "--no-run",
53 | "--lib",
54 | "--package=r0vm"
55 | ],
56 | "filter": {
57 | "name": "r0vm",
58 | "kind": "lib"
59 | }
60 | },
61 | "args": [],
62 | "cwd": "${workspaceFolder}"
63 | },
64 | {
65 | "type": "lldb",
66 | "request": "launch",
67 | "name": "Debug executable 'natrium'",
68 | "cargo": {
69 | "args": [
70 | "build",
71 | "--bin=natrium",
72 | "--package=natrium"
73 | ],
74 | "filter": {
75 | "name": "natrium",
76 | "kind": "bin"
77 | }
78 | },
79 | "args": [],
80 | "cwd": "${workspaceFolder}"
81 | },
82 | {
83 | "type": "lldb",
84 | "request": "launch",
85 | "name": "Debug unit tests in executable 'natrium'",
86 | "cargo": {
87 | "args": [
88 | "test",
89 | "--no-run",
90 | "--bin=natrium",
91 | "--package=natrium"
92 | ],
93 | "filter": {
94 | "name": "natrium",
95 | "kind": "bin"
96 | }
97 | },
98 | "args": [],
99 | "cwd": "${workspaceFolder}"
100 | }
101 | ]
102 | }
--------------------------------------------------------------------------------
/web/js/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import './index.styl'
3 | import * as Natrium from '../pkg/index'
4 | import AceEditor from 'react-ace'
5 |
6 | export interface AppProps {
7 | natrium: typeof Natrium
8 | }
9 |
10 | export interface AppState {
11 | error?: string
12 | code: string
13 | compiledCode?: string
14 | output: string
15 | }
16 |
17 | export class App extends React.Component {
18 | natrium: typeof Natrium
19 |
20 | constructor(props: AppProps) {
21 | super(props)
22 | this.natrium = props.natrium
23 | this.state = {
24 | error: undefined,
25 | code: '',
26 | compiledCode: undefined,
27 | output: '',
28 | }
29 | }
30 |
31 | onCodeUpdate(code: string) {
32 | this.setState({ code: code })
33 | }
34 |
35 | render() {
36 | return (
37 |
38 |
39 | this.onCodeUpdate(code)}>
40 |
41 |
42 |
43 |
44 | {this.state.compiledCode &&
{this.state.compiledCode}}
45 | {this.state.error &&
{this.state.error}}
46 | {this.state.output &&
{this.state.output}}
47 |
48 |
49 | )
50 | }
51 |
52 | compile(code: string) {
53 | try {
54 | let compiledCode = this.natrium.compile(code)
55 | this.setState({ compiledCode: compiledCode, output: '', error: undefined })
56 | } catch (e) {
57 | this.setState({ error: e, compiledCode: undefined })
58 | }
59 | }
60 |
61 | run(code: string) {
62 | try {
63 | this.setState({ output: '', compiledCode: undefined, error: undefined })
64 | this.natrium.run(
65 | code,
66 | () => '',
67 | (x: Uint8Array) => this.appendCode(x)
68 | )
69 | console.log('finished')
70 | } catch (e) {
71 | this.setState({ error: e, compiledCode: undefined })
72 | }
73 | }
74 |
75 | appendCode(x: Uint8Array) {
76 | let s = new TextDecoder('utf8').decode(x)
77 | console.log('out', s)
78 | this.setState((x) => ({
79 | output: x.output + s,
80 | }))
81 | }
82 | }
83 |
84 | interface EditorProps {
85 | onCodeChange: (code: string) => void
86 | code: string
87 | }
88 |
89 | class Editor extends React.Component {
90 | constructor(props: EditorProps) {
91 | super(props)
92 | this.props = props
93 | }
94 |
95 | props: EditorProps = undefined
96 |
97 | updateCode(code: string) {
98 | this.props.onCodeChange(code)
99 | }
100 |
101 | render() {
102 | return (
103 | this.updateCode(code)}
108 | width="100%"
109 | height="100%"
110 | fontSize="1rem"
111 | placeholder="// Your code here"
112 | editorProps={{
113 | $blockScrolling: true,
114 | }}
115 | setOptions={{
116 | wrap: true,
117 | displayIndentGuides: true,
118 | cursorStyle: 'smooth',
119 | }}
120 | >
121 | )
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/docs/src/c0/stmt.md:
--------------------------------------------------------------------------------
1 | # 语句
2 |
3 | ```
4 | stmt ->
5 | expr_stmt
6 | | decl_stmt
7 | | if_stmt
8 | | while_stmt
9 | | return_stmt
10 | | block_stmt
11 | | empty_stmt
12 | ```
13 |
14 | 语句是函数的最小组成部分。
15 |
16 | ## 表达式语句
17 |
18 | ```
19 | expr_stmt -> expr ';'
20 | ```
21 |
22 | 表达式语句由 _表达式_ 后接分号组成。表达式如果有值,值将会被丢弃。
23 |
24 | ## 声明语句
25 |
26 | ```
27 | let_decl_stmt -> 'let' IDENT ':' ty ('=' expr)? ';'
28 | const_decl_stmt -> 'const' IDENT ':' ty '=' expr ';'
29 | decl_stmt -> let_decl_stmt | const_decl_stmt
30 | ```
31 |
32 | 声明语句由 `let`(声明变量)或 `const`(声明常量)接 _标识符_、_类型_ 和可选的 _初始化表达式_ 组成。其中,常量声明语句必须有初始化表达式,而变量声明语句可以没有。
33 |
34 | 一个声明语句会在当前作用域中创建一个给定类型和标识符的变量或常量。声明语句有以下语义约束:
35 |
36 | - 在同一作用域内,一个标识符只能由一个变量或常量使用。
37 | - 变量或常量的类型不能为 `void`。
38 | - 如果存在初始化表达式,其类型应当与变量声明时的类型相同。
39 | - 常量只能被读取,不能被修改。
40 |
41 | 出现违反约束的声明语句是编译期错误。
42 |
43 | > UB: 没有初始化的变量的值未定义。我们不规定对于使用未初始化变量的行为的处理方式,你可以选择忽略、提供默认值或者报错。
44 |
45 | > UB: 我们不考虑局部变量和全局函数重名的情况。局部变量和全局变量重名的时候应当覆盖全局变量定义。
46 |
47 | 以下是一些可以通过编译的变量声明的例子:
48 |
49 | ```rust,ignore
50 | let i: int;
51 | let j: int = 1;
52 | const k: double = 1.20;
53 | ```
54 |
55 | 以下是一些不能通过编译的变量声明的例子:
56 |
57 | ```rust,ignore
58 | // 没有类型
59 | let l = 1;
60 | // 没有初始化
61 | const m: int;
62 | // 类型不匹配
63 | let n: double = 3;
64 | // 常量不能被修改
65 | const p: double = 3.0;
66 | p = 3.1415;
67 | ```
68 |
69 | ## 控制流语句
70 |
71 | 基础 C0 中有三种控制流语句,分别是 `if`、`while` 和 `return` 语句。
72 |
73 | > 对于 `if` 和 `while` 的条件,如果求值结果是 `int` 类型,则所有非零值均视为 `true`。
74 |
75 | ### `if` 语句
76 |
77 | ```
78 | if_stmt -> 'if' expr block_stmt ('else' (block_stmt | if_stmt))?
79 | // ^~~~ ^~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~
80 | // | if_block else_block
81 | // condition
82 | ```
83 |
84 | `if` 语句代表一组可选执行的语句。
85 |
86 | `if` 语句的执行流程是:
87 |
88 | - 求 `condition` 的值
89 | - 如果值为 `true`,则执行 `if_block`
90 | - 否则,如果存在 `else_block`,执行 `else_block`
91 | - 否则,执行下一条语句
92 |
93 | 请注意,**if 语句的条件表达式可以没有括号**,且 **条件执行的语句都必须是代码块**。
94 |
95 | 以下是一些合法的 if 语句:
96 |
97 | ```rust,ignore
98 | if x > 0 {
99 | x = x + 1;
100 | }
101 |
102 | if y < 0 {
103 | z = -1;
104 | } else if y > 0 {
105 | z = 1;
106 | } else {
107 | z = 0
108 | }
109 | ```
110 |
111 | 以下是一些不合法的 if 语句:
112 |
113 | ```rust,ignore
114 | // 必须是代码块
115 | if x > 0
116 | x = x + 1;
117 | ```
118 |
119 | ### `while` 语句
120 |
121 | ```
122 | while_stmt -> 'while' expr block_stmt
123 | // ^~~~ ^~~~~~~~~~while_block
124 | // condition
125 | ```
126 |
127 | while 语句代表一组可以重复执行的语句。
128 |
129 | while 语句的执行流程是:
130 |
131 | - 求值 `condition`
132 | - 如果为 `true`
133 | - 执行 `while_block`
134 | - 回到开头重新求值
135 | - 如果为 `false` 则执行之后的代码
136 |
137 | ### `return` 语句
138 |
139 | ```
140 | return_stmt -> 'return' expr? ';'
141 | ```
142 |
143 | 使用 `return` 语句从一个函数中返回。return 语句可以携带一个表达式作为返回值。
144 |
145 | return 语句有以下的语义约束:
146 |
147 | - 如果函数声明的返回值是 `void`,return 语句不能携带返回值;否则,return 语句必须携带返回值
148 | - 返回值表达式的类型必须与函数声明的返回值类型相同
149 | - 当执行到返回值类型是 `void` 的函数的末尾时,应视作存在一个 return 语句进行返回
150 |
151 | > UB: 在基础 C0 中不会出现部分分支没有返回值的情况,所以没有返回语句的分支的返回值是未定义的。在扩展 C0 中你必须检查每个分支都能够正常返回。
152 |
153 | ## 代码块
154 |
155 | ```
156 | block_stmt -> '{' stmt* '}'
157 | ```
158 |
159 | 一个代码块可以包含一条或多条语句。执行代码块的效果是顺序执行这些语句。
160 |
161 | 在基础 c0 中,一个代码块中的声明语句只能在其他类型的语句之前出现。
162 |
163 | 在扩展 c0(作用域嵌套)中,一个代码块是其所在作用域的子作用域。在扩展 c0(变量声明增强)中,一个代码块的任何地方均可声明变量。
164 |
165 | ## 空语句
166 |
167 | ```
168 | empty_stmt -> ';'
169 | ```
170 |
171 | 空语句没有任何作用,只是一个分号而已。
172 |
--------------------------------------------------------------------------------
/crates/syntax/src/token.rs:
--------------------------------------------------------------------------------
1 | use logos::{Lexer, Logos};
2 | use smol_str::SmolStr;
3 |
4 | fn parse_string_literal(i: &mut Lexer) -> Option {
5 | unescape::unescape(&i.slice()[1..i.slice().len() - 1])
6 | // Some(i.slice().into())
7 | }
8 |
9 | fn parse_char_literal(i: &mut Lexer) -> Option {
10 | unescape::unescape(&i.slice()[1..i.slice().len() - 1]).and_then(|x| x.chars().next())
11 | }
12 |
13 | #[derive(Debug, Clone, Logos)]
14 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15 | pub enum Token {
16 | #[token("fn")]
17 | FnKw,
18 | #[token("let")]
19 | LetKw,
20 | #[token("const")]
21 | ConstKw,
22 | #[token("as")]
23 | AsKw,
24 | #[token("while")]
25 | WhileKw,
26 | #[token("if")]
27 | IfKw,
28 | #[token("else")]
29 | ElseKw,
30 | #[token("return")]
31 | ReturnKw,
32 | #[token("break")]
33 | BreakKw,
34 | #[token("continue")]
35 | ContinueKw,
36 |
37 | #[regex(r"\d+", |lex| lex.slice().parse())]
38 | UIntLiteral(u64),
39 | #[regex(r"\d+\.\d+([eE][+-]?\d+)?", |lex| lex.slice().parse())]
40 | FloatLiteral(f64),
41 | #[regex(r#"'([^\\']|\\[rnt\\/"'])'"#, parse_char_literal)]
42 | CharLiteral(char),
43 | #[regex(r#""([^\\"]|\\([rnt\\/"']))*""#, parse_string_literal)]
44 | StringLiteral(String),
45 | #[regex(r"[_a-zA-Z][_a-zA-Z0-9]*", |lex| SmolStr::new(lex.slice()))]
46 | Ident(SmolStr),
47 |
48 | #[token(r"+")]
49 | Plus,
50 | #[token(r"-")]
51 | Minus,
52 | #[token(r"*")]
53 | Mul,
54 | #[token(r"/")]
55 | Div,
56 | #[token(r"=")]
57 | Assign,
58 | #[token(r"==")]
59 | Eq,
60 | #[token(r"!=")]
61 | Neq,
62 | #[token(r"<")]
63 | Lt,
64 | #[token(r">")]
65 | Gt,
66 | #[token(r"<=")]
67 | Le,
68 | #[token(r">=")]
69 | Ge,
70 | #[token("(")]
71 | LParen,
72 | #[token(")")]
73 | RParen,
74 | #[token("{")]
75 | LBrace,
76 | #[token("}")]
77 | RBrace,
78 | #[token(r"->")]
79 | Arrow,
80 | #[token(r",")]
81 | Comma,
82 | #[token(r":")]
83 | Colon,
84 | #[token(r";")]
85 | Semicolon,
86 |
87 | // Empty stuff
88 | #[regex(r"\s+", logos::skip, priority = 1)]
89 | Whitespace,
90 | #[regex(r"//.*\n", logos::skip)]
91 | Comment,
92 |
93 | // Error token
94 | #[error]
95 | Error,
96 | }
97 |
98 | impl Token {
99 | pub fn get_ident(&self) -> Option<&str> {
100 | match self {
101 | Token::Ident(i) => Some(&i),
102 | _ => None,
103 | }
104 | }
105 |
106 | pub fn get_ident_owned(self) -> Option {
107 | match self {
108 | Token::Ident(i) => Some(i),
109 | _ => None,
110 | }
111 | }
112 |
113 | pub fn get_uint(&self) -> Option {
114 | match self {
115 | Token::UIntLiteral(i) => Some(*i),
116 | Token::CharLiteral(c) => Some(*c as u64),
117 | _ => None,
118 | }
119 | }
120 |
121 | pub fn get_float(&self) -> Option {
122 | match self {
123 | Token::FloatLiteral(i) => Some(*i),
124 | _ => None,
125 | }
126 | }
127 |
128 | pub fn get_string(&self) -> Option<&str> {
129 | match self {
130 | Token::StringLiteral(i) => Some(&i),
131 | _ => None,
132 | }
133 | }
134 |
135 | pub fn get_string_owned(self) -> Option {
136 | match self {
137 | Token::StringLiteral(i) => Some(i),
138 | _ => None,
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/docs/src/c0/expr.md:
--------------------------------------------------------------------------------
1 | # 表达式
2 |
3 | ```
4 | expr ->
5 | operator_expr
6 | | negate_expr
7 | | assign_expr
8 | | as_expr
9 | | call_expr
10 | | literal_expr
11 | | ident_expr
12 | | group_expr
13 | ```
14 |
15 | 表达式是代码中运算的最小单位。在语法解析的时候,一个表达式会被展开成一棵树,称作表达式树。
16 |
17 | > 提示:对于 运算符表达式 `operator_expr`、取反表达式 `negate_expr` 和类型转换表达式 `as_expr` 可以使用局部的算符优先文法进行分析。
18 |
19 | 表达式中运算符的优先级从高到低为:
20 |
21 | | 运算符 | 结合性 |
22 | | --------------------------- | ------ |
23 | | 括号表达式 | - |
24 | | 函数调用 | - |
25 | | 前置 `-` | - |
26 | | `as` | - |
27 | | `*` `/` | 左到右 |
28 | | `+` `-` | 左到右 |
29 | | `>` `<` `>=` `<=` `==` `!=` | 左到右 |
30 | | `=` | 右到左 |
31 |
32 | ## 运算符表达式
33 |
34 | ```
35 | binary_operator -> '+' | '-' | '*' | '/' | '==' | '!=' | '<' | '>' | '<=' | '>='
36 | operator_expr -> expr binary_operator expr
37 | ```
38 |
39 | 运算符表达式是中间由一个运算符分隔、两边是子表达式的表达式。r0 一共有 10 种双目运算符。它们分别是:
40 |
41 | - 算数运算符 `+` `-` `*` `/`
42 | - 比较运算符 `>` `<` `>=` `<=` `==` `!=`
43 |
44 | 每个运算符的两侧必须是相同类型的数据。各运算符含义如下:
45 |
46 | | 运算符 | 含义 | 参数类型 | 结果类型 | 结合性 |
47 | | ------ | -------------------------- | -------- | ---------- | ------ |
48 | | `+` | 将左右两侧相加 | 数值 | 与参数相同 | 左到右 |
49 | | `-` | 左侧减去右侧 | 数值 | 与参数相同 | 左到右 |
50 | | `*` | 将左右两侧相乘 | 数值 | 与参数相同 | 左到右 |
51 | | `/` | 左侧除以右侧 | 数值 | 与参数相同 | 左到右 |
52 | | `>` | 如果左侧大于右侧则为真 | 数值 | 布尔\* | 左到右 |
53 | | `<` | 如果左侧小于右侧则为真 | 数值 | 布尔\* | 左到右 |
54 | | `>=` | 如果左侧大于等于右侧则为真 | 数值 | 布尔\* | 左到右 |
55 | | `<=` | 如果左侧小于等于右侧则为真 | 数值 | 布尔\* | 左到右 |
56 | | `==` | 如果左侧等于右侧则为真 | 数值 | 布尔\* | 左到右 |
57 | | `!=` | 如果左侧不等于右侧则为真 | 数值 | 布尔\* | 左到右 |
58 |
59 | ### \* 关于布尔类型
60 |
61 | 布尔类型的表达式只能出现在 `if` 和 `while` 语句的条件表达式中。因此,我们不强制规定布尔类型的值的表现形式。所有非 0 值都会被视为 true,0 会被视为 false。
62 |
63 | ## 取反表达式
64 |
65 | ```
66 | negate_expr -> '-' expr
67 | ```
68 |
69 | 取反表达式是在表达式前添加负号组成的表达式。取反表达式的语义是将表达式转换成它的相反数。
70 |
71 | ## 赋值表达式
72 |
73 | ```
74 | l_expr -> IDENT
75 | assign_expr -> l_expr '=' expr
76 | ```
77 |
78 | 赋值表达式是由 _左值表达式_、_等号 `=`_、_表达式_ 组成的表达式。赋值表达式的值类型永远是 `void`(即不能被使用)。
79 |
80 | 左值表达式是一个局部或全局的变量名。
81 |
82 | 赋值表达式的语义是将右侧表达式的计算结果赋给左侧表示的值。
83 |
84 | ## 类型转换表达式
85 |
86 | ```
87 | as_expr -> expr 'as' ty
88 | ```
89 |
90 | 类型转换表达式是由 _表达式_、_关键字 `as`_、_类型_ 组成的表达式。类型转换表达式的语义是将左侧表达式表示的值转换成右侧类型表示的值。
91 |
92 | 在 c0 实验中只会涉及到整数 `int` 和浮点数 `double` 之间的互相转换。
93 |
94 | ## 函数调用表达式
95 |
96 | ```
97 | call_param_list -> expr (',' expr)*
98 | call_expr -> IDENT '(' call_param_list? ')'
99 | ```
100 |
101 | 函数调用表达式是由 _函数名_ 和 _调用参数列表_ 组成的表达式。函数调用表达式的语义是使用给出的参数调用函数名代表的函数。函数必须在调用前声明过(也就是说不存在先出现调用后出现声明的函数)。
102 |
103 | ### 特殊情况
104 |
105 | 标准库中的函数在调用前不需要声明,见 [标准库文档](stdlib.md)。
106 |
107 | ## 字面量表达式
108 |
109 | ```
110 | literal_expr -> UINT_LITERAL | DOUBLE_LITERAL | STRING_LITERAL
111 |
112 | digit -> [0-9]
113 | UINT_LITERAL -> digit+
114 | DOUBLE_LITERAL -> digit+ '.' digit+ ([eE] [+-]? digit+)?
115 |
116 | escape_sequence -> '\' [\\"'nrt]
117 | string_regular_char -> [^"\\]
118 | STRING_LITERAL -> '"' (string_regular_char | escape_sequence)* '"'
119 | ```
120 |
121 | 字面量表达式可以是一个无符号整数、浮点数或者字符串的字面量。_整数_ 和 _浮点数字面量_ 的语义就是用对应类型表示的字面量的值(64 位);_字符串字面量_ 只会在 `putstr` 调用中出现,语义是对应的全局常量的编号。
122 |
123 | ## 标识符表达式
124 |
125 | ```
126 | ident_expr -> IDENT
127 | ```
128 |
129 | 标识符表达式是由标识符组成的表达式。其语义是标识符对应的局部或全局变量。标识符表达式的类型与标识符的类型相同。
130 |
131 | ## 括号表达式
132 |
133 | ```
134 | group_expr -> '(' expr ')'
135 | ```
136 |
137 | 括号表达式内部的表达式的值将被优先计算。
138 |
--------------------------------------------------------------------------------
/crates/r0vm/src/error.rs:
--------------------------------------------------------------------------------
1 | use failure::Fail;
2 | use std::fmt::Display;
3 |
4 | use crate::s0::S0;
5 |
6 | pub type Result = std::result::Result;
7 |
8 | #[derive(Fail, Debug)]
9 | pub enum Error {
10 | #[fail(display = "Invalid instruction: {}", _0)]
11 | InvalidInstruction(InvalidInstructionCtx),
12 |
13 | #[fail(display = "Stack overflow")]
14 | StackOverflow,
15 |
16 | #[fail(display = "Stack underflow")]
17 | StackUnderflow,
18 |
19 | #[fail(display = "Invalid local variable index {}", _0)]
20 | InvalidLocalIndex(u32),
21 |
22 | #[fail(display = "Invalid local variable index {}", _0)]
23 | InvalidArgIndex(u32),
24 |
25 | #[fail(display = "Invalid global variable index {}", _0)]
26 | InvalidGlobalIndex(u32),
27 |
28 | #[fail(
29 | display = "Invalid function name index in global variable for function {} :{}",
30 | _0, _1
31 | )]
32 | InvalidFunctionNameIndex(usize, u32),
33 |
34 | #[fail(display = "Invalid address 0x{:016x}", _0)]
35 | InvalidAddress(u64),
36 |
37 | #[fail(display = "Invalid stack offset {} (bp + {})", _0, _1)]
38 | InvalidStackOffset(u64, i64),
39 |
40 | #[fail(display = "Invalid function ID {}", _0)]
41 | InvalidFnId(u32),
42 |
43 | #[fail(display = "Unknown function (name index: {})", _0)]
44 | UnknownFunction(u32),
45 |
46 | #[fail(display = "Unknown function name: {}", _0)]
47 | UnknownFunctionName(String),
48 |
49 | #[fail(display = "Invalid instruction offset {}", _0)]
50 | InvalidInstructionOffset(usize),
51 |
52 | #[fail(display = "Dividing by zero")]
53 | DivZero,
54 |
55 | #[fail(display = "Arithmetic error")]
56 | ArithmeticErr,
57 |
58 | #[fail(display = "Allocated 0 size of memory")]
59 | AllocZero,
60 |
61 | #[fail(display = "Deallocating memory that is not allocated")]
62 | InvalidDeallocation,
63 |
64 | #[fail(display = "Out of memory")]
65 | OutOfMemory,
66 |
67 | #[fail(display = "Unaligned memory access of address 0x{:016x}", _0)]
68 | UnalignedAccess(u64),
69 |
70 | #[fail(display = "Control reaches end of function #{} without returning", _0)]
71 | ControlReachesEnd(usize),
72 |
73 | #[fail(display = "Unable to find entry point")]
74 | NoEntryPoint,
75 |
76 | #[fail(display = "Parse error")]
77 | ParseError,
78 |
79 | #[fail(display = "IO error: {}", _0)]
80 | IoError(std::io::Error),
81 |
82 | #[fail(display = "Allocation Layout error: {}", _0)]
83 | AllocLayoutError(std::alloc::LayoutErr),
84 |
85 | #[fail(display = "Halt")]
86 | Halt,
87 | }
88 |
89 | // impl Error {
90 | // pub fn format_with_ctx(&self, f: &mut std::fmt::Formatter, s0: &S0) -> std::fmt::Result {
91 | // self.fmt(f)
92 | // }
93 | // }
94 |
95 | // pub struct ErrorCtx<'e>{
96 | // error:'e,
97 | // }
98 |
99 | #[derive(Debug)]
100 | pub struct InvalidInstructionCtx {
101 | /// Instruction opcode
102 | pub inst: u8,
103 | /// Function id
104 | pub fn_id: u32,
105 | /// Instruction offset
106 | pub inst_off: u64,
107 | }
108 |
109 | impl Display for InvalidInstructionCtx {
110 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 | write!(
112 | f,
113 | "0x{:02x} at fn #{}:{}",
114 | self.inst, self.fn_id, self.inst_off
115 | )
116 | }
117 | }
118 |
119 | impl From for Error {
120 | fn from(x: std::io::Error) -> Self {
121 | Error::IoError(x)
122 | }
123 | }
124 |
125 | impl From for Error {
126 | fn from(x: std::alloc::LayoutErr) -> Self {
127 | Error::AllocLayoutError(x)
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/docs/src/c0/extended-c0.md:
--------------------------------------------------------------------------------
1 | # 扩展 C0
2 |
3 | 这里列出了实现之后可以获得加分的扩展 C0 特性。
4 |
5 | 加分的单位尚未确定,目前的加分数量都是相对值。
6 |
7 | 扩展 c0 包括:
8 |
9 | - 注释
10 | - 字符字面量
11 | - 类型转换 & 浮点数
12 | - 作用域嵌套
13 | - 变量声明增强
14 | - break/continue
15 | - 返回路径检查
16 |
17 | ## 注释
18 |
19 | 加分:5pt
20 |
21 | ```
22 | COMMENT -> '//' regex(.*) '\n'
23 | ```
24 |
25 | C0 的注释是从 `//` 开始到这行结束(遇到第一个 `\n`)为止的字符序列。注释不应当被词法分析输出。
26 |
27 | ## 字符字面量
28 |
29 | 加分:5pt
30 |
31 | ```
32 | char_regular_char -> [^'\\\n\r]
33 | CHAR_LITERAL -> '\'' (char_regular_char | escape_sequence) '\''
34 | literal_expr -> UINT_LITERAL | DOUBLE_LITERAL | STRING_LITERAL | CHAR_LITERAL
35 | ```
36 |
37 | 字符字面量是由单引号 `'` 包裹的单个字符或转义序列。其中单个字符可以是 ASCII 中除了单引号 `'`、反斜线 `\\`、空白符 `\r`(CR)、`\n`(LF)、`\t`(Tab) 以外的任何字符。转义序列可以是 `\'`、`\"`、`\\`、`\n`、`\t`、`\r`,含义与 C 中的对应序列相同。
38 |
39 | _字符字面量_ 的语义是被包裹的字符的 ASCII 编码无符号扩展到 64 位的整数值,类型是 `int`。
40 |
41 | ## 类型转换 & 浮点数类型
42 |
43 | 加分:25pt
44 |
45 | ### 类型转换
46 |
47 | ```
48 | AS_KW -> 'as'
49 | as_expr -> expr 'as' ty
50 | expr -> .. | as_expr
51 | ```
52 |
53 | 显式类型转换通过 `as` 表达式实现。语言中没有隐式类型转换。
54 |
55 | `表达式 as 类型` 表示将 `表达式` 的计算结果转换为 `类型` 所表示的类型的数据。`as` 表达式的左侧数据类型和右侧类型都不能是 `void`。
56 |
57 | 允许的类型转换包括:
58 |
59 | - 类型 T 转换到它自己
60 | - 浮点数 `double` 和整数 `int` 之间互相转换
61 |
62 | ### 浮点数类型
63 |
64 | ```
65 | DOUBLE_LITERAL -> digit+ '.' digit+ ([eE] [+-]? digit+)?
66 | // ^~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~
67 | // number exponent
68 | ```
69 |
70 | 浮点数类型 `double` 是遵循 IEEE 754 标准的 64 位浮点数(在其它语言中经常称作 `double`、`float64` 或 `f64`)。
71 |
72 | 带指数部分(`exponent`)的浮点数字面量的值是 `number * (10 ^ exponent)`,如 `1E6`、`1e+6` 均代表 `1000000`、`2.1e-2` 代表 `0.021`。
73 |
74 | 浮点数和整数之间不能进行运算。浮点数之间进行四则运算的结果仍为浮点数。
75 |
76 | ## 作用域嵌套
77 |
78 | 加分:10pt
79 |
80 | 简而言之,在任何一个代码块中都可以声明变量。
81 |
82 | 要求:
83 |
84 | - 每个代码块(`block_stmt`)都是一级作用域。
85 | - 每级作用域内部的变量声明不能重复。
86 | - 作用域内声明的变量可以覆盖上一级作用域中的变量。
87 | - 每个作用域内定义的变量在作用域结束后即失效
88 |
89 | 比如,下面的函数中变量 `x`(1)、`counter`(2)、循环内的 `x`(3) 可以被访问的区域如竖线左侧所示:
90 |
91 | ```rust,ignore
92 | 1 | fn fib_iter(x: int) -> int { // (1)
93 | | | let last_val: int = 1;
94 | | | let cur_val: int = 1;
95 | 2 | | let counter: int = x - 2; // (2)
96 | | | | while counter > 0 {
97 | 3 | | let x: int = cur_val + last_val; // (3)
98 | | | | last_val = cur_val;
99 | | | | cur_val = x;
100 | - | | }
101 | | | | return cur_val;
102 | - - | }
103 | ```
104 |
105 | ## 变量声明增强
106 |
107 | 加分:5pt
108 |
109 | 在每一级作用域中,你不仅可以在作用域顶端声明变量,也能在作用域中间声明变量。在作用域中间声明的变量同样遵循上一条的生命周期。在全局作用域中,变量声明和函数声明也可以混合。
110 |
111 | ## `break` 和 `continue`
112 |
113 | 加分:10pt
114 |
115 | ```
116 | BREAK_KW -> 'break'
117 | CONTINUE_KW -> 'continue'
118 |
119 | break_stmt -> 'break' ';'
120 |
121 | continue_stmt -> 'continue' ';'
122 | ```
123 |
124 | - `break` 和 `continue` 必须在循环体内使用,在其他地方使用是编译错误。
125 | - `break` 代表跳出循环体,控制转移到循环外的下一条语句。
126 | - `continue` 代表跳过本次循环体的代码,控制转移到循环体的最后一条语句。
127 |
128 | > 提示:进入循环之前记录一下跳转的目标位置
129 |
130 | ## 函数返回路径检查
131 |
132 | 加分:10pt
133 |
134 | 你需要对每一个函数的所有控制流进行检查,保证如果函数有返回值,那么所有可能的控制流(这里认为一个条件语句的所有分支都能访问到)都能导向 `return` 语句。比如,以下的函数不能通过编译:
135 |
136 | ```rust,ignore
137 | fn foo(i: int) -> int {
138 | if i == 0 {
139 | return 1;
140 | } else {
141 | putint(0);
142 | }
143 | // 这个分支没有返回
144 | }
145 | ```
146 |
147 | 这个也不行:
148 |
149 | ```rust,ignore
150 | fn bar() -> int {
151 | let i: int;
152 | i = getint();
153 | while i > 0 {
154 | i = i - 1;
155 | if i <= 0 {
156 | return i;
157 | }
158 | }
159 | // 这个分支没有返回
160 | }
161 | ```
162 |
163 | 这个可以,因为在到达函数结尾之前两个分支都返回了:
164 |
165 | ```rust,ignore
166 | fn baz(i: int) -> int {
167 | if i == 0 {
168 | return 1;
169 | } else {
170 | return 0;
171 | }
172 | // 没有分支可以到达这里
173 | }
174 | ```
175 |
176 | > 提示:用基本块表示函数就能看得一清二楚了。
177 |
178 | > UB: 我们不会考察对于无限循环的控制流检查。你可以选择报错,也可以选择无视。
179 |
--------------------------------------------------------------------------------
/docs/src/c0/c0.md:
--------------------------------------------------------------------------------
1 | # c0 语法说明
2 |
3 | c0 是一个用于编译原理课程的微型语言。c0 提供的功能类似于 C,但是为了减少编译器实现的压力(减少前瞻和/或回溯),在语言风格上大量参考了 Rust 的设计。请注意,这个语言**并不是**对 Rust 语言的简化。
4 |
5 | ## 语法定义
6 |
7 | 以下是 c0 语言的全部语法定义,**包括所有扩展语法**。
8 |
9 | ```
10 | // # 单词
11 |
12 | // ## 关键字
13 | FN_KW -> 'fn'
14 | LET_KW -> 'let'
15 | CONST_KW -> 'const'
16 | AS_KW -> 'as'
17 | WHILE_KW -> 'while'
18 | IF_KW -> 'if'
19 | ELSE_KW -> 'else'
20 | RETURN_KW -> 'return'
21 | BREAK_KW -> 'break'
22 | CONTINUE_KW -> 'continue'
23 |
24 | // ## 字面量
25 | digit -> [0-9]
26 | UINT_LITERAL -> digit+
27 | DOUBLE_LITERAL -> digit+ '.' digit+ ([eE] digit+)?
28 |
29 | escape_sequence -> '\' [\\"'nrt]
30 | string_regular_char -> [^"\\]
31 | STRING_LITERAL -> '"' (string_regular_char | escape_sequence)* '"'
32 |
33 | char_regular_char -> [^'\\]
34 | CHAR_LITERAL -> '\'' (char_regular_char | escape_sequence) '\''
35 |
36 | // ## 标识符
37 | IDENT -> [_a-zA-Z] [_a-zA-Z0-9]*
38 |
39 | // ## 符号
40 | PLUS -> '+'
41 | MINUS -> '-'
42 | MUL -> '*'
43 | DIV -> '/'
44 | ASSIGN -> '='
45 | EQ -> '=='
46 | NEQ -> '!='
47 | LT -> '<'
48 | GT -> '>'
49 | LE -> '<='
50 | GE -> '>='
51 | L_PAREN -> '('
52 | R_PAREN -> ')'
53 | L_BRACE -> '{'
54 | R_BRACE -> '}'
55 | ARROW -> '->'
56 | COMMA -> ','
57 | COLON -> ':'
58 | SEMICOLON -> ';'
59 |
60 | // ## 注释
61 | COMMENT -> '//' regex(.*) '\n'
62 |
63 | // # 表达式
64 | expr ->
65 | operator_expr
66 | | negate_expr
67 | | assign_expr
68 | | as_expr
69 | | call_expr
70 | | literal_expr
71 | | ident_expr
72 | | group_expr
73 |
74 | binary_operator -> '+' | '-' | '*' | '/' | '==' | '!=' | '<' | '>' | '<=' | '>='
75 | operator_expr -> expr binary_operator expr
76 |
77 | negate_expr -> '-' expr
78 |
79 | assign_expr -> l_expr '=' expr
80 |
81 | as_expr -> expr 'as' ty
82 |
83 | call_param_list -> expr (',' expr)*
84 | call_expr -> IDENT '(' call_param_list? ')'
85 |
86 | literal_expr -> UINT_LITERAL | DOUBLE_LITERAL | STRING_LITERAL | CHAR_LITERAL
87 |
88 | ident_expr -> IDENT
89 |
90 | group_expr -> '(' expr ')'
91 |
92 | // ## 左值表达式
93 | l_expr -> IDENT
94 |
95 | // ## 类型
96 | ty -> IDENT
97 |
98 | // # 语句
99 | stmt ->
100 | expr_stmt
101 | | decl_stmt
102 | | if_stmt
103 | | while_stmt
104 | | break_stmt
105 | | continue_stmt
106 | | return_stmt
107 | | block_stmt
108 | | empty_stmt
109 |
110 | expr_stmt -> expr ';'
111 |
112 | let_decl_stmt -> 'let' IDENT ':' ty ('=' expr)? ';'
113 | const_decl_stmt -> 'const' IDENT ':' ty '=' expr ';'
114 | decl_stmt -> let_decl_stmt | const_decl_stmt
115 |
116 | if_stmt -> 'if' expr block_stmt ('else' 'if' expr block_stmt)* ('else' block_stmt)?
117 |
118 | while_stmt -> 'while' expr block_stmt
119 |
120 | break_stmt -> 'break' ';'
121 |
122 | continue_stmt -> 'continue' ';'
123 |
124 | return_stmt -> 'return' expr? ';'
125 |
126 | block_stmt -> '{' stmt* '}'
127 |
128 | empty_stmt -> ';'
129 |
130 | // # 函数
131 | function_param -> 'const'? IDENT ':' ty
132 | function_param_list -> function_param (',' function_param)*
133 | function -> 'fn' IDENT '(' function_param_list? ')' '->' ty block_stmt
134 |
135 | // # 程序
136 | item -> function | decl_stmt
137 | program -> item*
138 | ```
139 |
140 | 其中,表达式中运算符的优先级从高到低为:
141 |
142 | | 运算符 | 结合性 |
143 | | --------------------------- | ------ |
144 | | 括号表达式 | - |
145 | | 函数调用 | - |
146 | | 前置 `-` | - |
147 | | `as` | - |
148 | | `*` `/` | 左到右 |
149 | | `+` `-` | 左到右 |
150 | | `>` `<` `>=` `<=` `==` `!=` | 左到右 |
151 | | `=` | 右到左 |
152 |
153 | ## 语法参考
154 |
155 | 以下是一些符合语法规范的程序。
156 |
157 | ```rust,ignore
158 | fn fib(x: int) -> int {
159 | if x<=1 {
160 | return 1;
161 | }
162 | let result: int = fib(x - 1);
163 | result = result + fib(x - 2);
164 | return result;
165 | }
166 |
167 | fn main() -> int {
168 | let i: int = 0;
169 | let j: int;
170 | j = getint();
171 | while i < j {
172 | putint(i);
173 | putchar(32);
174 | putint(fib(i));
175 | putln();
176 | i = i + 1;
177 | }
178 | return 0;
179 | }
180 | ```
181 |
--------------------------------------------------------------------------------
/web/src/lib.rs:
--------------------------------------------------------------------------------
1 | use bytes::Bytes;
2 | use natrium::util::pretty_print_error;
3 | use r0vm::s0::S0;
4 | use std::{fmt::Write as FmtWrite, io, io::Write};
5 | use wasm_bindgen::prelude::*;
6 | use web_sys::console;
7 |
8 | #[cfg(feature = "wee_alloc")]
9 | #[global_allocator]
10 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
11 |
12 | // This is like the `main` function, except for JavaScript.
13 | #[wasm_bindgen(start)]
14 | pub fn main_js() -> Result<(), JsValue> {
15 | #[cfg(debug_assertions)]
16 | console_error_panic_hook::set_once();
17 |
18 | // Your code goes here!
19 | unsafe { console::log_1(&JsValue::from_str("Hello world!")) };
20 |
21 | Ok(())
22 | }
23 |
24 | fn compile_internal(input: &str) -> Result {
25 | let l = r0syntax::lexer::spanned_lexer(input);
26 | let mut p = r0syntax::parser::Parser::new(l);
27 | let r = p.parse();
28 | let program = match r {
29 | Ok(p) => p,
30 | Err(e) => {
31 | let mut err = Vec::new();
32 | if let Some(span) = e.span {
33 | pretty_print_error(&mut err, input, &format!("{:?}", e.kind), span)
34 | .map_err(|x| x.to_string())?;
35 | } else {
36 | writeln!(err, "{:?}", e.kind).map_err(|x| x.to_string())?;
37 | }
38 | return Err(unsafe { String::from_utf8_unchecked(err) });
39 | }
40 | };
41 |
42 | let s0 = match r0codegen::generator::compile(&program) {
43 | Ok(p) => p,
44 | Err(e) => {
45 | let mut err = Vec::new();
46 | if let Some(span) = e.span {
47 | pretty_print_error(&mut err, input, &format!("{:?}", e.kind), span)
48 | .map_err(|x| x.to_string())?;
49 | } else {
50 | writeln!(err, "{:?}", e.kind).map_err(|x| x.to_string())?;
51 | }
52 | return Err(unsafe { String::from_utf8_unchecked(err) });
53 | }
54 | };
55 |
56 | Ok(s0)
57 | }
58 |
59 | #[wasm_bindgen]
60 | pub fn compile(input: &str) -> Result {
61 | compile_internal(input)
62 | .map_err(|x| JsValue::from_str(&x))
63 | .map(|x| x.to_string())
64 | }
65 |
66 | #[wasm_bindgen]
67 | pub fn run(
68 | input: &str,
69 | read_chunk: js_sys::Function,
70 | write_chunk: js_sys::Function,
71 | ) -> Result<(), JsValue> {
72 | let code = compile_internal(input).map_err(|x| JsValue::from_str(&x))?;
73 | let mut stdout = JsStdioAdaptor::new(read_chunk, write_chunk);
74 | let mut var_name = io::empty();
75 | let mut vm = r0vm::vm::R0Vm::new(&code, Box::new(var_name), Box::new(stdout)).map_err(|x| {
76 | let mut s = String::new();
77 | write!(s, "{:?}", x).unwrap();
78 | s
79 | })?;
80 | vm.run_to_end().unwrap();
81 | Ok(())
82 | }
83 |
84 | struct JsStdioAdaptor {
85 | read_chunk: js_sys::Function,
86 | write_chunk: js_sys::Function,
87 | pending_read: Option,
88 | }
89 |
90 | impl JsStdioAdaptor {
91 | pub fn new(read_chunk: js_sys::Function, write_chunk: js_sys::Function) -> JsStdioAdaptor {
92 | JsStdioAdaptor {
93 | read_chunk,
94 | write_chunk,
95 | pending_read: None,
96 | }
97 | }
98 | }
99 |
100 | impl io::Read for JsStdioAdaptor {
101 | fn read(&mut self, buf: &mut [u8]) -> io::Result {
102 | if let Some(mut data) = self.pending_read.take() {
103 | if data.len() > buf.len() {
104 | let remianing = data.split_off(buf.len());
105 | self.pending_read = Some(remianing);
106 | buf.copy_from_slice(&data[..]);
107 | Ok(buf.len())
108 | } else {
109 | let buf_sub = &mut buf[0..data.len()];
110 | buf_sub.copy_from_slice(&data[..]);
111 | Ok(data.len())
112 | }
113 | } else {
114 | let val: JsValue = self.read_chunk.call0(&JsValue::null()).unwrap();
115 | if !val.is_string() {
116 | return Err(io::Error::new(
117 | io::ErrorKind::InvalidInput,
118 | "Value is not a string",
119 | ));
120 | }
121 | let val_str = val.as_string().unwrap();
122 | let mut data = Bytes::from(val_str);
123 | if data.len() > buf.len() {
124 | let remianing = data.split_off(buf.len());
125 | self.pending_read = Some(remianing);
126 | buf.copy_from_slice(&data[..]);
127 | Ok(buf.len())
128 | } else {
129 | let buf_sub = &mut buf[0..data.len()];
130 | buf_sub.copy_from_slice(&data[..]);
131 | Ok(data.len())
132 | }
133 | }
134 | }
135 | }
136 |
137 | impl std::io::Write for JsStdioAdaptor {
138 | fn write(&mut self, buf: &[u8]) -> io::Result {
139 | let js_buf = js_sys::Uint8Array::from(buf);
140 | self.write_chunk.call1(&JsValue::null(), &js_buf).unwrap();
141 | Ok(buf.len())
142 | }
143 |
144 | fn flush(&mut self) -> io::Result<()> {
145 | Ok(())
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | use clap::Clap;
2 | use logos::{Lexer, Logos};
3 | use natrium::util::pretty_print_error;
4 | use r0syntax::{ast::Program, span::Span, token::Token};
5 | use r0vm::s0::io::WriteBinary;
6 | use std::{
7 | io::{Read, Write},
8 | path::PathBuf,
9 | str::FromStr,
10 | };
11 |
12 | fn main() {
13 | let opt = Opt::parse();
14 | let input = std::fs::read_to_string(&opt.input).expect("Unable to read input file");
15 |
16 | let output_file = get_output(&opt);
17 | let mut output = build_output(output_file, opt.interpret);
18 |
19 | let lexer = r0syntax::lexer::spanned_lexer(&input);
20 | if !opt.interpret && opt.emit == EmitTarget::Token {
21 | dump_lex(lexer, output);
22 | }
23 |
24 | let program = parser(lexer, &input);
25 | if !opt.interpret && opt.emit == EmitTarget::Ast {
26 | dump_ast(program, output);
27 | }
28 |
29 | let s0 = compile_s0(&program, &input);
30 | if !opt.interpret {
31 | if opt.emit == EmitTarget::O0 {
32 | s0.write_binary(&mut output)
33 | .expect("Failed to write to output");
34 | } else {
35 | write!(output, "{}", s0).expect("Failed to write to output");
36 | }
37 | } else {
38 | let stdin = std::io::stdin();
39 | let stdout = std::io::stdout();
40 | let mut vm = r0vm::vm::R0Vm::new(&s0, Box::new(stdin), Box::new(stdout)).unwrap();
41 |
42 | match vm.run_to_end() {
43 | Ok(_) => {}
44 | Err(e) => {
45 | eprintln!("{}", &s0);
46 | eprintln!("{}", e);
47 | eprintln!("{}", vm.debug_stack());
48 | }
49 | }
50 | }
51 | }
52 |
53 | /// Get real output path based on options. None means stdout.
54 | fn get_output(opt: &Opt) -> Option {
55 | match opt.output.as_deref() {
56 | Some("-") => None,
57 | Some(x) => PathBuf::from_str(x).ok(),
58 | None => {
59 | let filename = opt
60 | .input
61 | .file_stem()
62 | .and_then(|x| x.to_str())
63 | .unwrap_or("a");
64 | let ext = match opt.emit {
65 | EmitTarget::O0 => "o0",
66 | EmitTarget::Text => "s0",
67 | EmitTarget::Token => "tokenstream",
68 | EmitTarget::Ast => "ast",
69 | };
70 | let out_file = format!("{}.{}", filename, ext);
71 | Some(out_file.into())
72 | }
73 | }
74 | }
75 |
76 | fn build_output(output: Option, interpret: bool) -> Box {
77 | if interpret {
78 | return Box::new(std::io::stdout());
79 | }
80 | if let Some(path) = output {
81 | let file = std::fs::File::create(path).expect("Failed to open file");
82 | Box::new(file)
83 | } else {
84 | Box::new(std::io::stdout())
85 | }
86 | }
87 |
88 | fn dump_lex(lexer: T, mut output: Box) -> !
89 | where
90 | T: Iterator- ,
91 | {
92 | for (token, span) in lexer {
93 | writeln!(output, "{:?} at {:?}", token, span).expect("Failed to write");
94 | }
95 | std::process::exit(0);
96 | }
97 |
98 | fn dump_ast(ast: Program, mut output: Box) -> ! {
99 | writeln!(output, "{:?}", ast).expect("Failed to write to output");
100 | std::process::exit(0);
101 | }
102 |
103 | fn parser(lexer: T, input: &str) -> Program
104 | where
105 | T: Iterator
- ,
106 | {
107 | let mut p = r0syntax::parser::Parser::new(lexer);
108 | let r = p.parse();
109 |
110 | match r {
111 | Ok(p) => p,
112 | Err(e) => {
113 | if let Some(span) = e.span {
114 | pretty_print_error(
115 | &mut std::io::stdout(),
116 | &input,
117 | &format!("{:?}", e.kind),
118 | span,
119 | )
120 | .unwrap();
121 | } else {
122 | println!("{:?}", e.kind);
123 | }
124 | std::process::exit(1);
125 | }
126 | }
127 | }
128 |
129 | fn compile_s0(program: &Program, input: &str) -> r0vm::s0::S0 {
130 | match r0codegen::generator::compile(program) {
131 | Ok(p) => p,
132 | Err(e) => {
133 | if let Some(span) = e.span {
134 | pretty_print_error(
135 | &mut std::io::stdout(),
136 | &input,
137 | &format!("{:?}", e.kind),
138 | span,
139 | )
140 | .unwrap();
141 | } else {
142 | println!("{:?}", e.kind);
143 | }
144 | std::process::exit(1);
145 | }
146 | }
147 | }
148 |
149 | #[derive(Clap, Debug)]
150 | struct Opt {
151 | /// Input file
152 | pub input: PathBuf,
153 |
154 | /// Emit target
155 | ///
156 | /// O0: binary object code;
157 | /// Text: text format code;
158 | /// Token: token stream;
159 | /// Ast: abstract syntax tree
160 | #[clap(long, default_value = "o0")]
161 | pub emit: EmitTarget,
162 |
163 | /// Output file. Defaults to `.o0|s0|tt|ast`
164 | #[clap(long, short)]
165 | pub output: Option,
166 |
167 | /// Interpret the input file with virtual machine; alias: `--run`
168 | #[cfg(feature = "vm")]
169 | #[clap(short = 'i', long, alias = "run")]
170 | pub interpret: bool,
171 | }
172 |
173 | #[derive(Debug, Copy, Clone, Eq, PartialEq)]
174 | enum EmitTarget {
175 | O0,
176 | Text,
177 | Token,
178 | Ast,
179 | }
180 |
181 | impl FromStr for EmitTarget {
182 | type Err = String;
183 |
184 | fn from_str(s: &str) -> Result {
185 | Ok(match s.to_lowercase().as_str() {
186 | "o0" => EmitTarget::O0,
187 | "text" | "s0" => EmitTarget::Text,
188 | "token" | "lex" => EmitTarget::Token,
189 | "ast" | "parse" => EmitTarget::Ast,
190 | _ => return Err(format!("Expected one of: o0, text, token, ast; got: {}", s)),
191 | })
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/crates/syntax/src/ast.rs:
--------------------------------------------------------------------------------
1 | //! This crate lists the common AST items inside R0.
2 | //!
3 | //! For the pointer type, see `crate::util::{P, Mut}`
4 |
5 | use crate::{span::Span, util::P};
6 | #[cfg(feature = "serde_impl")]
7 | use serde::{Deserialize, Serialize};
8 | use smol_str::SmolStr;
9 |
10 | #[derive(Debug, Clone)]
11 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
12 | pub struct Program {
13 | pub decls: Vec,
14 | pub funcs: Vec,
15 | }
16 |
17 | pub trait AstNode {
18 | fn span(&self) -> Span;
19 | }
20 |
21 | #[derive(Debug, Clone)]
22 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
23 | pub struct FuncStmt {
24 | pub span: Span,
25 | pub name: Ident,
26 | pub params: Vec,
27 | pub ret_ty: TyDef,
28 | pub body: BlockStmt,
29 | }
30 |
31 | #[derive(Debug, Clone)]
32 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
33 | pub struct FuncParam {
34 | pub is_const: bool,
35 | pub name: Ident,
36 | pub ty: TyDef,
37 | }
38 |
39 | #[derive(Debug, Clone)]
40 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
41 | pub enum Stmt {
42 | Block(BlockStmt),
43 | While(WhileStmt),
44 | If(IfStmt),
45 | Expr(Expr),
46 | Decl(DeclStmt),
47 | Return(ReturnStmt),
48 | Break(Span),
49 | Continue(Span),
50 | Empty(Span),
51 | }
52 |
53 | impl Stmt {
54 | pub fn span(&self) -> Span {
55 | match self {
56 | Stmt::Block(i) => i.span,
57 | Stmt::While(i) => i.span,
58 | Stmt::If(i) => i.span,
59 | Stmt::Expr(i) => i.span(),
60 | Stmt::Decl(i) => i.span,
61 | Stmt::Return(i) => i.span,
62 | Stmt::Break(s) => *s,
63 | Stmt::Continue(s) => *s,
64 | Stmt::Empty(s) => *s,
65 | }
66 | }
67 | }
68 |
69 | #[derive(Debug, Clone)]
70 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
71 | pub struct DeclStmt {
72 | pub is_const: bool,
73 | pub name: Ident,
74 | pub ty: TyDef,
75 | pub val: Option
>,
76 | pub span: Span,
77 | }
78 |
79 | #[derive(Debug, Clone)]
80 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
81 | pub struct ReturnStmt {
82 | pub val: Option
>,
83 | pub span: Span,
84 | }
85 |
86 | #[derive(Debug, Clone)]
87 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
88 | pub struct TyDef {
89 | pub span: Span,
90 | pub name: SmolStr,
91 | pub params: Option>,
92 | }
93 |
94 | #[derive(Debug, Clone)]
95 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
96 | pub struct BlockStmt {
97 | pub span: Span,
98 | pub stmts: Vec,
99 | }
100 |
101 | #[derive(Debug, Clone)]
102 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
103 | pub struct WhileStmt {
104 | pub span: Span,
105 | pub cond: P,
106 | pub body: P,
107 | }
108 |
109 | #[derive(Debug, Clone)]
110 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
111 | pub struct IfStmt {
112 | pub span: Span,
113 | pub cond: P,
114 | pub if_block: P,
115 | pub else_block: IfElseBlock,
116 | }
117 |
118 | #[derive(Debug, Clone)]
119 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
120 | pub enum IfElseBlock {
121 | None,
122 | If(P),
123 | Block(P),
124 | }
125 |
126 | #[derive(Debug, Clone)]
127 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
128 | pub enum Expr {
129 | Ident(Ident),
130 | Assign(AssignExpr),
131 | As(AsExpr),
132 | Literal(LiteralExpr),
133 | Unary(UnaryExpr),
134 | Binary(BinaryExpr),
135 | Call(CallExpr),
136 | }
137 |
138 | impl Expr {
139 | pub fn span(&self) -> Span {
140 | match self {
141 | Expr::Ident(x) => x.span,
142 | Expr::Assign(x) => x.span,
143 | Expr::As(x) => x.span,
144 | Expr::Literal(x) => x.span,
145 | Expr::Unary(x) => x.span,
146 | Expr::Binary(x) => x.span,
147 | Expr::Call(x) => x.span,
148 | }
149 | }
150 | }
151 |
152 | #[derive(Debug, Clone)]
153 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
154 | pub struct LiteralExpr {
155 | pub span: Span,
156 | pub kind: LiteralKind,
157 | }
158 |
159 | #[derive(Debug, Clone)]
160 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
161 | pub enum LiteralKind {
162 | Integer(u64),
163 | Float(f64),
164 | String(String),
165 | Char(char),
166 | }
167 |
168 | #[derive(Debug, Clone)]
169 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
170 | pub struct UnaryExpr {
171 | pub span: Span,
172 | pub op: UnaryOp,
173 | pub expr: P,
174 | }
175 |
176 | #[derive(Debug, Clone)]
177 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
178 | pub struct AssignExpr {
179 | pub span: Span,
180 | pub allow_assign_const: bool,
181 | pub lhs: P,
182 | pub rhs: P,
183 | }
184 |
185 | #[derive(Debug, Clone)]
186 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
187 | pub struct AsExpr {
188 | pub span: Span,
189 | pub val: P,
190 | pub ty: TyDef,
191 | }
192 |
193 | #[derive(Debug, Clone)]
194 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
195 | pub struct BinaryExpr {
196 | pub span: Span,
197 | pub op: BinaryOp,
198 | pub lhs: P,
199 | pub rhs: P,
200 | }
201 |
202 | #[derive(Debug, Clone)]
203 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
204 | pub struct CallExpr {
205 | pub span: Span,
206 | pub func: Ident,
207 | pub params: Vec,
208 | }
209 |
210 | #[derive(Debug, Copy, Clone)]
211 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
212 | pub enum UnaryOp {
213 | Neg,
214 | Pos,
215 | }
216 |
217 | #[derive(Debug, Copy, Clone)]
218 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
219 | pub enum BinaryOp {
220 | Add,
221 | Sub,
222 | Mul,
223 | Div,
224 | Gt,
225 | Lt,
226 | Ge,
227 | Le,
228 | Eq,
229 | Neq,
230 | }
231 |
232 | #[derive(Debug, Clone)]
233 | #[cfg_attr(feature = "serde_impl", derive(Serialize, Deserialize))]
234 | pub struct Ident {
235 | pub span: Span,
236 | pub name: SmolStr,
237 | }
238 |
--------------------------------------------------------------------------------
/docs/src/c0/notes.md:
--------------------------------------------------------------------------------
1 | # 设计笔记与讨论
2 |
3 | > 这里存放着设计 2020 软院编译原理所使用的语言的时候所考虑的一些东西。
4 | >
5 |
6 | > Rynco:我认为,实验的目标应当是让学生尽可能多的了解一个真实的编译器是如何运行的。因此,我们需要尽可能消减不必要的内容,比如复杂的指令集、寄存器分配、过于繁琐的语法等等。剩下来的应该是一个只包含核心内容的现代语言。
7 |
8 | ## 语法
9 |
10 | > 邵老师:最好不要自创一门新的语言或者改编自小众语言。
11 | >
12 | > Rynco:我个人是倾向于创建一门新的语言的。
13 |
14 | 考点:词法、语法分析
15 |
16 | > 按照 hambaka 的意思,c0 的语法可以进一步简化,降低实现难度。
17 |
18 | Hambaka 建议的语法修改包括:
19 |
20 | - 去除隐式类型转换,所有类型转换必须显式声明
21 | - 只保留 while 和/或 for 作为唯一/二可用的循环
22 | - 去除 switch 语句
23 |
24 | Rynco 正在考虑的语法修改包括:
25 |
26 | - 类型后置
27 | - 规范 bool 类型
28 |
29 | > Rynco:
30 | >
31 | > 我计划的是一个长得有点 Rust(只借鉴了关键字和部分语法,因为解析器写起来容易)的 C,不知道其他人怎么看
32 | >
33 | > 我希望这么做的原因是:如果使用类型后置的话,就可以把类型关键字解析完全放在语义解析里面做了。从而不需要在 parse 的时候维护一个符号表来确定哪些标识符是类型、哪些是变量,或者在解析到类型转换时回溯了(见 [附录1][ref1] )。
34 |
35 | [ref1]: #附录1:C 风格的语法在解析时的回溯问题与解决方案
36 |
37 | ```rust,ignore
38 | let global_var: double = -123456.789e10;
39 |
40 | fn add(a: int, b: int) -> int {
41 | return a + b;
42 | }
43 |
44 | fn main() -> void {
45 | let a: int = 3;
46 | let b: int = 4;
47 | let c: double = add(a, b) as double;
48 | if global_var > 0 {
49 | print("Hello");
50 | } else {
51 | print(c);
52 | }
53 | }
54 | ```
55 |
56 | > Rynco: 自动类型推断应该不会有了……吧?有人要当进阶内容做我不反对。
57 | >
58 |
59 | ### 暂定的语法内容
60 |
61 | > Rynco: 这里决定的内容会在实现示例编译器的时候再次确认,保证实现编译器的工作量不会过大。如果实现示例编译器的时候发现什么地方难度偏大的话还会再砍。
62 |
63 | #### 字面量
64 |
65 | 字面量包括以下内容:
66 |
67 | - 整数
68 | - 浮点数
69 | - 字符
70 | - 字符串
71 | - 布尔值
72 |
73 | #### 运算
74 |
75 | 算术运算:
76 |
77 | - 相反数
78 | - 加
79 | - 减
80 | - 乘
81 | - 除
82 |
83 | 比较运算:
84 |
85 | - 大于
86 | - 小于
87 | - 大于等于
88 | - 小于等于
89 | - 等于
90 | - 不等于
91 |
92 | 赋值运算:
93 |
94 | - 赋值
95 |
96 | 进阶版本可以支持以下运算(虚拟机已支持):
97 |
98 | - 布尔非
99 | - 布尔与
100 | - 布尔或
101 | - 按位与
102 | - 按位或
103 | - 按位异或
104 | - 左移
105 | - 算术右移
106 | - 逻辑右移
107 |
108 | #### 变量声明与赋值
109 |
110 | 变量声明使用 `let` 或 `const` 关键字声明,语法见 附录1。
111 |
112 | 赋值表达式使用等号 `=` 作为运算符,运算结果类型为空值(`void`)。
113 |
114 | > 比如 `(a = b) == c` 是合法的表达式,但是类型检查会出错。赋值作为表达式而不是语句的原因是赋值语句和表达式的 FIRST 集会相交。
115 |
116 | 要求实现变量作用域。
117 |
118 | #### 条件表达式和循环
119 |
120 | 一种条件表达式:`if-elseif-else` 表达式。
121 |
122 | 一种循环:`while` 循环。
123 |
124 | #### 函数
125 |
126 | 函数使用 `fn` 关键字声明,语法见 附录1。
127 |
128 | 不要求实现前向引用。
129 |
130 |
131 | ## 类型系统
132 |
133 | 考点:类型转换
134 |
135 | 虚拟机的设计默认使用 64 位整数和浮点数。
136 |
137 | 会有整数和浮点数(没法用同一种类型假装能转换的)
138 |
139 | 虚拟机的设计支持数组、结构体和堆内存分配,这三者可以作为进阶内容选做。
140 |
141 | ### 暂定的类型系统
142 |
143 | 必做
144 |
145 | - `int` (`i64`)
146 | - `double` (`f64`)
147 | - `void` (`unit` / `()`)
148 |
149 | 进阶(待砍刀)
150 |
151 | - `bool`
152 | (存储上等同于 `u8`,`false == 0u8`, `true == 255u8`)
153 | - `u8`/`u16`/`u32`/`u64`
154 | - `i8`/`i16`/`i32`/`i64`
155 | - struct
156 | - array (`[T; N]`)
157 | - pointer (`&T`)
158 | - 自动类型推断(省去 `let variable` 后面的类型)
159 |
160 | ## 虚拟机
161 |
162 | 考点:代码生成
163 |
164 | 编译目标是 r0vm 虚拟机,是栈式虚拟机。编译到 JVM / Dotnet CLR / x86 等目标如果想做的话可以选做,需要提前跟助教声明。
165 |
166 | 虚拟机设计已经基本确定,见相关文档。
167 |
168 | ## 附录
169 |
170 | ### 附录1:C 风格的语法在解析时的回溯问题与解决方案
171 |
172 | 考虑以下 C 风格的变量声明语句的语法规则(`int`、`double` 等类型名称不是关键字):
173 |
174 | ```
175 | # 标识符
176 | ident -> "int" | "x"
177 | # 运算符
178 | op -> "+" | "-"
179 |
180 | # 类型,在词法分析时被分析为标识符
181 | ty -> ident
182 | # 表达式
183 | expr -> "0" | "1" | ident | expr op expr
184 |
185 | # 变量声明语句
186 | decl_stmt -> ty ident "=" expr ";"
187 | # 表达式语句
188 | expr_stmt -> expr ";"
189 |
190 | # 语句
191 | stmt -> decl_stmt | expr_stmt
192 | ```
193 |
194 | 显然,FIRST(`decl_stmt`) ∩ FIRST(`expr_stmt`) == { `ident` }。鉴于大部分同学在实现的时候都会考虑采用递归下降分析法(这门课的大作业里不会真的有人去手写/生成 LL/LR 吧?),这个 FIRST 集的重合会造成大量的回溯。
195 |
196 | 类似的还有显式类型转换:
197 |
198 | ```
199 | ident -> "int" | "x"
200 | ty -> ident
201 |
202 | # 括号表达式
203 | paren_expr -> "(" expr ")"
204 | # C 风格的显式类型转换
205 | cast_expr -> "(" ty ")" expr
206 |
207 | expr -> ident | cast_expr | paren_expr
208 | ```
209 |
210 | 如果没有在语法分析的时候就建立符号表,在分析代码 `(int)(x)` 时甚至在读完 `(int)` 这三个 token 之后都不能确定解析的表达式到底是类型转换还是括号。为了解决这个问题,要么需要预读不确定数量的 token (在类型声明可能大于 1 个 token 时),要么遇到括号就要准备回溯,两者在代码上实现难度都偏大。
211 |
212 | 因此,我建议在设计语法的时候就考虑这类问题,避免大量的 FIRST 集重合现象,降低递归下降语法分析器的实现难度。具体方案如下:
213 |
214 | > 在以下的语法规则中,`ty` 代表类型,`ident` 代表标识符,`expr` 代表表达式,`block` 代表语法块。
215 | >
216 |
217 | 一,将变量和函数声明中的类型后置,使用关键字开始此类语句,避免与 `expr` 的 FIRST 集重叠。此语法与多种现代语言(如 TypeScript、Kotlin、Go、Rust)相类似。
218 |
219 | > 九个六:`const` 可以考虑砍掉(待定)
220 |
221 | ```
222 | # 修改前:
223 |
224 | decl_stmt -> "const"? ty ident ("=" expr)? ";"
225 | # int myVariable = 123 + 456;
226 |
227 | function_param -> ty ident ("," ty ident)*
228 | function -> ty ident "(" function_param? ")" block
229 | # int add(int a, int b) { ... }
230 |
231 | # 修改后:
232 |
233 | decl_stmt -> ("let" | "const") ident ":" ty ("=" expr)? ";"
234 | # let myVariable: int = 123 + 456;
235 |
236 | function_param -> ident ":" ty ("," ident ":" ty)*
237 | function -> "fn" ident "(" function_param? ")" "->" ty block
238 | # fn add(a: int, b: int) -> int { ... }
239 | ```
240 |
241 | 二,将显式类型转换的语法从括号变为使用 `as` 做运算符的二元表达式。此语法与多种现代语言(如 TypeScript、Kotlin、Rust、C#)相类似。
242 |
243 | ```
244 | # 修改前:
245 |
246 | cast_expr -> "(" ty ")" expr
247 | # (int)42.0
248 | # (double)(a + b)
249 |
250 | # 修改后:
251 |
252 | cast_expr -> expr "as" ty
253 | # 42.0 as int
254 | # (a + b) as double
255 | ```
256 |
257 |
258 | ### 附录2:九个六先生的例程
259 |
260 | ```rust,ignore
261 | let a1, a2, a3, a4, a5: int;
262 |
263 | fn me(x: int) -> int {
264 | return x;
265 | }
266 |
267 | fn add(x: double, y: double) -> int {
268 | let xx: int = x as int;
269 | let yy: int = y as int;
270 | return xx + yy
271 | }
272 |
273 | fn give(idx: int) -> int {
274 | if idx == 1 {
275 | return a1;
276 | }
277 | else if idx == 2 {
278 | return a2;
279 | }
280 | else if idx == 3 {
281 | return a3;
282 | }
283 | else if idx == 4 {
284 | return a4;
285 | }
286 | else if idx == 5 {
287 | return a5;
288 | }
289 | else {
290 | return 114514.0 as int
291 | }
292 | }
293 |
294 | fn set(idx: int, val: int) -> void {
295 | if idx == 1 {
296 | a1 = val;
297 | }
298 | else if idx == 2 {
299 | a2 = val;
300 | }
301 | else if idx == 3 {
302 | a3 = val;
303 | }
304 | else if idx == 4 {
305 | a4 = val;
306 | }
307 | else if idx == 5 {
308 | a5 = val;
309 | }
310 | }
311 |
312 | fn main() -> void {
313 | let a, b, c, t: int;
314 | let five: int = 5;
315 | a = getint();
316 | b = getint();
317 | set(1, a);
318 | set(2, b);
319 | t = 3;
320 | while t <= five {
321 | c = add(a as double, b as double);
322 | b = a;
323 | a = c;
324 | set(t, c);
325 | t = t + 1;
326 | }
327 | print(give(me(five)));
328 | ```
329 |
--------------------------------------------------------------------------------
/crates/r0vm/src/s0/io.rs:
--------------------------------------------------------------------------------
1 | //! Module for reading and writing s0 values
2 | use super::*;
3 | use tracing::*;
4 | // use nom::*;
5 | use std::io::{Read, Write};
6 |
7 | /// Read and write from binary source
8 | pub trait WriteBinary: Sized {
9 | fn read_binary(r: &mut dyn Read) -> std::io::Result