Working in Rust with Parser.bind(&mut String)
This commit is contained in:
parent
7c8194633b
commit
fed2aef6e4
7
.gitignore
vendored
7
.gitignore
vendored
@ -1 +1,8 @@
|
||||
/target
|
||||
|
||||
|
||||
#Added by cargo
|
||||
#
|
||||
#already existing elements were commented out
|
||||
|
||||
#/target
|
||||
|
117
Cargo.lock
generated
Normal file
117
Cargo.lock
generated
Normal file
@ -0,0 +1,117 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"lexical-core",
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pivot"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "pivot"
|
||||
version = "0.1.0"
|
||||
authors = ["Garen Tyler <garentyler@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
regex = "1.4.1"
|
||||
nom = "5.1.2"
|
10
output.s
10
output.s
@ -1,10 +0,0 @@
|
||||
.global main
|
||||
main:
|
||||
push {fp, lr}
|
||||
ldr r0, =1
|
||||
cmp r0, #1
|
||||
moveq r0, #'.'
|
||||
movne r0, #'F'
|
||||
bl putchar
|
||||
mov r0, #0
|
||||
pop {fp, pc}
|
20
package.json
20
package.json
@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "pivot",
|
||||
"version": "1.0.0",
|
||||
"description": "A programming language",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"run": "deno run -c tsconfig.json --allow-read --allow-write --unstable src/index.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ElementG9/pivot.git"
|
||||
},
|
||||
"author": "Garen Tyler",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ElementG9/pivot/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ElementG9/pivot#readme"
|
||||
}
|
197
src/ast.rs
Normal file
197
src/ast.rs
Normal file
@ -0,0 +1,197 @@
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum AstNodeKind {
|
||||
// Primitives
|
||||
Integer,
|
||||
Identifier,
|
||||
// Unary operators
|
||||
Not,
|
||||
// Infix operators
|
||||
NotEqual,
|
||||
Equal,
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
// Control flow
|
||||
Block,
|
||||
IfStatement,
|
||||
WhileLoop,
|
||||
Program,
|
||||
// Functions and variables
|
||||
FunctionCall,
|
||||
FunctionReturn,
|
||||
FunctionDefinition,
|
||||
VariableDefinition,
|
||||
Assign,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AstNode {
|
||||
pub kind: AstNodeKind,
|
||||
pub value: String,
|
||||
pub subnodes: Vec<AstNode>
|
||||
}
|
||||
impl AstNode {
|
||||
pub fn new(kind: AstNodeKind, value: String, subnodes: Vec<AstNode>) -> AstNode {
|
||||
AstNode {
|
||||
kind,
|
||||
value,
|
||||
subnodes
|
||||
}
|
||||
}
|
||||
pub fn emit(&self, f: &mut dyn std::fmt::Write) -> std::fmt::Result {
|
||||
use AstNodeKind::*;
|
||||
match self.kind {
|
||||
Integer => write!(f, "i64.const {}\n", self.value),
|
||||
Add => {
|
||||
self.subnodes[0].emit(f)?;
|
||||
self.subnodes[1].emit(f)?;
|
||||
write!(f, "i64.add\n")
|
||||
}
|
||||
Program => {
|
||||
write!(f, "(module\n")?;
|
||||
for node in &self.subnodes {
|
||||
node.emit(f);
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
_ => Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Primitives
|
||||
pub fn integer(num: i64) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Integer,
|
||||
value: num.to_string(),
|
||||
subnodes: vec![],
|
||||
}
|
||||
}
|
||||
pub fn identifier(id: String) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Identifier,
|
||||
value: id,
|
||||
subnodes: vec![],
|
||||
}
|
||||
}
|
||||
// Unary operators
|
||||
pub fn not(operand: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Not,
|
||||
value: "not".into(),
|
||||
subnodes: vec![operand],
|
||||
}
|
||||
}
|
||||
// Infix operators
|
||||
pub fn not_equal(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::NotEqual,
|
||||
value: "not_equal".into(),
|
||||
subnodes: vec![left, right]
|
||||
}
|
||||
}
|
||||
pub fn equal(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Equal,
|
||||
value: "equal".into(),
|
||||
subnodes: vec![left, right]
|
||||
}
|
||||
}
|
||||
pub fn add(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Add,
|
||||
value: "add".into(),
|
||||
subnodes: vec![left, right]
|
||||
}
|
||||
}
|
||||
pub fn subtract(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Subtract,
|
||||
value: "subtract".into(),
|
||||
subnodes: vec![left, right]
|
||||
}
|
||||
}
|
||||
pub fn multiply(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Multiply,
|
||||
value: "multiply".into(),
|
||||
subnodes: vec![left, right]
|
||||
}
|
||||
}
|
||||
pub fn divide(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Divide,
|
||||
value: "divide".into(),
|
||||
subnodes: vec![left, right]
|
||||
}
|
||||
}
|
||||
// Control flow
|
||||
pub fn block(statements: Vec<AstNode>) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Block,
|
||||
value: "block".into(),
|
||||
subnodes: statements
|
||||
}
|
||||
}
|
||||
pub fn if_statement(conditional: AstNode, consequence: AstNode, alternative: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::IfStatement,
|
||||
value: "if_statement".into(),
|
||||
subnodes: vec![conditional, consequence, alternative]
|
||||
}
|
||||
}
|
||||
pub fn while_loop(conditional: AstNode, body: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::WhileLoop,
|
||||
value: "while_loop".into(),
|
||||
subnodes: vec![conditional, body],
|
||||
}
|
||||
}
|
||||
pub fn program(statements: Vec<AstNode>) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Program,
|
||||
value: "program".into(),
|
||||
subnodes: statements
|
||||
}
|
||||
}
|
||||
// Functions and variables
|
||||
pub fn function_call(name: String, parameters: Vec<AstNode>) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::FunctionCall,
|
||||
value: name,
|
||||
subnodes: parameters,
|
||||
}
|
||||
}
|
||||
pub fn function_return(operand: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::FunctionReturn,
|
||||
value: "return".into(),
|
||||
subnodes: vec![operand],
|
||||
}
|
||||
}
|
||||
pub fn function_definition(name: String, parameters: Vec<AstNode>, body: AstNode) -> AstNode {
|
||||
let mut params = vec![body];
|
||||
for p in parameters {
|
||||
params.push(p);
|
||||
}
|
||||
AstNode {
|
||||
kind: AstNodeKind::FunctionDefinition,
|
||||
value: name,
|
||||
subnodes: params,
|
||||
}
|
||||
}
|
||||
pub fn variable_definition(name: String, value: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::VariableDefinition,
|
||||
value: name,
|
||||
subnodes: vec![value],
|
||||
}
|
||||
}
|
||||
pub fn assign(name: String, value: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
kind: AstNodeKind::Assign,
|
||||
value: name,
|
||||
subnodes: vec![value],
|
||||
}
|
||||
}
|
||||
}
|
273
src/ast.ts
273
src/ast.ts
@ -1,273 +0,0 @@
|
||||
import { emit } from './codegen.ts';
|
||||
|
||||
export interface AST {
|
||||
emit(): void;
|
||||
equals(other: AST): boolean;
|
||||
}
|
||||
|
||||
// Number node.
|
||||
export class Num implements AST {
|
||||
constructor(public value: number) {}
|
||||
|
||||
emit() {
|
||||
emit(` ldr r0, =${this.value}`);
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Num
|
||||
&& this.value === other.value;
|
||||
}
|
||||
}
|
||||
// Identifier node.
|
||||
export class Id implements AST {
|
||||
constructor(public value: string) {}
|
||||
|
||||
emit() {
|
||||
throw Error('Not implemented yet');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Id
|
||||
&& this.value === other.value;
|
||||
}
|
||||
}
|
||||
// Operator nodes.
|
||||
export class Not implements AST {
|
||||
constructor(public operand: AST) {}
|
||||
|
||||
emit() {
|
||||
this.operand.emit();
|
||||
emit(' cmp r0, #0');
|
||||
emit(' moveq r0, #1');
|
||||
emit(' movne r0, #0');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Not
|
||||
&& this.operand.equals(other.operand);
|
||||
}
|
||||
}
|
||||
export abstract class Infix {
|
||||
constructor(public left: AST, public right: AST) {}
|
||||
emit() {
|
||||
this.left.emit();
|
||||
emit(' push {r0, ip}');
|
||||
this.right.emit();
|
||||
emit(' pop {r1, ip}');
|
||||
}
|
||||
}
|
||||
export class Equal extends Infix implements AST {
|
||||
constructor(public left: AST, public right: AST) {
|
||||
super(left, right);
|
||||
}
|
||||
|
||||
emit() {
|
||||
super.emit();
|
||||
emit(' cmp r0, r1');
|
||||
emit(' moveq r0, #1');
|
||||
emit(' movne r0, #0');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Equal
|
||||
&& this.left.equals(other.left)
|
||||
&& this.right.equals(other.right);
|
||||
}
|
||||
}
|
||||
export class NotEqual extends Infix implements AST {
|
||||
constructor(public left: AST, public right: AST) {
|
||||
super(left, right);
|
||||
}
|
||||
|
||||
emit() {
|
||||
super.emit();
|
||||
emit(' cmp r0, r1');
|
||||
emit(' moveq r0, #0');
|
||||
emit(' movne r0, #1');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof NotEqual
|
||||
&& this.left.equals(other.left)
|
||||
&& this.right.equals(other.right);
|
||||
}
|
||||
}
|
||||
export class Add extends Infix implements AST {
|
||||
constructor(public left: AST, public right: AST) {
|
||||
super(left, right);
|
||||
}
|
||||
|
||||
emit() {
|
||||
super.emit();
|
||||
emit(' add r0, r0, r1');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Add
|
||||
&& this.left.equals(other.left)
|
||||
&& this.right.equals(other.right);
|
||||
}
|
||||
}
|
||||
export class Subtract extends Infix implements AST {
|
||||
constructor(public left: AST, public right: AST) {
|
||||
super(left, right);
|
||||
}
|
||||
|
||||
emit() {
|
||||
super.emit();
|
||||
emit(' sub r0, r0, r1');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Subtract
|
||||
&& this.left.equals(other.left)
|
||||
&& this.right.equals(other.right);
|
||||
}
|
||||
}
|
||||
export class Multiply extends Infix implements AST {
|
||||
constructor(public left: AST, public right: AST) {
|
||||
super(left, right);
|
||||
}
|
||||
|
||||
emit() {
|
||||
super.emit();
|
||||
emit(' mul r0, r0, r1');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Multiply
|
||||
&& this.left.equals(other.left)
|
||||
&& this.right.equals(other.right);
|
||||
}
|
||||
}
|
||||
export class Divide extends Infix implements AST {
|
||||
constructor(public left: AST, public right: AST) {
|
||||
super(left, right);
|
||||
}
|
||||
|
||||
emit() {
|
||||
super.emit();
|
||||
emit(' udiv r0, r0, r1');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Divide
|
||||
&& this.left.equals(other.left)
|
||||
&& this.right.equals(other.right);
|
||||
}
|
||||
}
|
||||
// Function call node.
|
||||
export class FunctionCall implements AST {
|
||||
constructor(public callee: string, public args: Array<AST>) {}
|
||||
|
||||
emit() {
|
||||
throw Error('Not implemented yet');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof FunctionCall
|
||||
&& this.callee === other.callee
|
||||
&& this.args.length === other.args.length
|
||||
&& this.args.every((arg: AST, i: number) => arg.equals(other.args[i]));
|
||||
}
|
||||
}
|
||||
// Return node.
|
||||
export class Return implements AST {
|
||||
constructor(public operand: AST) {}
|
||||
|
||||
emit() {
|
||||
throw Error('Not implemented yet');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Return
|
||||
&& this.operand.equals(other.operand);
|
||||
}
|
||||
}
|
||||
// Block node.
|
||||
export class Block implements AST {
|
||||
constructor(public statements: Array<AST>) {}
|
||||
|
||||
emit() {
|
||||
this.statements.forEach((statement: any) => statement.emit());
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Block
|
||||
&& this.statements.length === other.statements.length
|
||||
&& this.statements.every((arg: AST, i: number) => arg.equals(other.statements[i]));
|
||||
}
|
||||
}
|
||||
// If node.
|
||||
export class If implements AST {
|
||||
constructor(public conditional: AST,
|
||||
public consequence: AST,
|
||||
public alternative: AST) {}
|
||||
|
||||
emit() {
|
||||
throw Error('Not implemented yet');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof If
|
||||
&& this.conditional.equals(other.conditional)
|
||||
&& this.consequence.equals(other.consequence)
|
||||
&& this.alternative.equals(other.alternative);
|
||||
}
|
||||
}
|
||||
// Function definition node.
|
||||
export class FunctionDefinition implements AST {
|
||||
constructor(public name: string,
|
||||
public parameters: Array<string>,
|
||||
public body: AST) {}
|
||||
|
||||
emit() {
|
||||
throw Error(`Not implemented yet (${this.name})`);
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof FunctionDefinition
|
||||
&& this.name === other.name
|
||||
&& this.parameters.length === other.parameters.length
|
||||
&& this.parameters.every((arg: string, i: number) => arg === other.parameters[i])
|
||||
&& this.body.equals(other.body);
|
||||
}
|
||||
}
|
||||
// Variable declaration node.
|
||||
export class VariableDeclaration implements AST {
|
||||
constructor(public name: string, public value: AST) {}
|
||||
|
||||
emit() {
|
||||
throw Error('Not implemented yet');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof VariableDeclaration
|
||||
&& this.name === other.name
|
||||
&& this.value.equals(other.value);
|
||||
}
|
||||
}
|
||||
// Assignment node.
|
||||
export class Assign implements AST {
|
||||
constructor(public name: string, public value: AST) {}
|
||||
|
||||
emit() {
|
||||
throw Error('Not implemented yet');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Assign
|
||||
&& this.name === other.name
|
||||
&& this.value.equals(other.value);
|
||||
}
|
||||
}
|
||||
// While loop node.
|
||||
export class While implements AST {
|
||||
constructor(public conditional: AST, public body: AST) {}
|
||||
|
||||
emit() {
|
||||
throw Error('Not implemented yet');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof While
|
||||
&& this.conditional.equals(other.conditional)
|
||||
&& this.body.equals(other.body);
|
||||
}
|
||||
}
|
||||
// Variable node.
|
||||
export class Var implements AST {
|
||||
constructor(public name: string, public value: AST) {}
|
||||
|
||||
emit() {
|
||||
throw Error('Not implemented yet');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Var
|
||||
&& this.name === other.name
|
||||
&& this.value.equals(other.value);
|
||||
}
|
||||
}
|
235
src/ast_old.rs
Normal file
235
src/ast_old.rs
Normal file
@ -0,0 +1,235 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct AstNode {
|
||||
pub value: Option<String>,
|
||||
pub kind: String,
|
||||
pub subtokens: Option<Vec<AstNode>>,
|
||||
}
|
||||
impl std::fmt::Display for AstNode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.kind[..] {
|
||||
"number" => write!(f, "{}", self.value.as_ref().expect("number had blank value")),
|
||||
"identifier" => write!(f, "{}", self.value.as_ref().expect("number had blank value")),
|
||||
"not" => write!(f, "!({})", self.subtokens.as_ref().expect("not had blank subtokens")[0]),
|
||||
"equal" => write!(f, "({} == ({}))", self.subtokens.as_ref().expect("equal had blank subtokens")[0], self.subtokens.as_ref().expect("equal had blank subtokens")[1]),
|
||||
"not_equal" => write!(f, "({} != {})", self.subtokens.as_ref().expect("not_equal had blank subtokens")[0], self.subtokens.as_ref().expect("not_equal had blank subtokens")[1]),
|
||||
"add" => write!(f, "({} + {})", self.subtokens.as_ref().expect("add had blank subtokens")[0], self.subtokens.as_ref().expect("add had blank subtokens")[1]),
|
||||
"subtract" => write!(f, "({} - {})", self.subtokens.as_ref().expect("subtract had blank subtokens")[0], self.subtokens.as_ref().expect("subtract had blank subtokens")[1]),
|
||||
"multiply" => write!(f, "({} * {})", self.subtokens.as_ref().expect("multiply had blank subtokens")[0], self.subtokens.as_ref().expect("multiply had blank subtokens")[1]),
|
||||
"divide" => write!(f, "({} / {})", self.subtokens.as_ref().expect("divide had blank subtokens")[0], self.subtokens.as_ref().expect("divide had blank subtokens")[1]),
|
||||
"call" => {
|
||||
write!(f, "({}(", self.value.as_ref().expect("call had blank value"))?;
|
||||
let args = self.subtokens.as_ref().expect("call had blank subtokens");
|
||||
if args.len() > 0 {
|
||||
write!(f, "{}", args[0])?;
|
||||
if args.len() > 1 {
|
||||
for i in 1..args.len() {
|
||||
write!(f, ", {}", args[i])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(f, "))")
|
||||
},
|
||||
"return" => write!(f, "return {}", self.subtokens.as_ref().expect("return had blank subtokens")[0]),
|
||||
"block" => {
|
||||
write!(f, "{{\n")?;
|
||||
let stmts = self.subtokens.as_ref().expect("block had blank subtokens");
|
||||
if stmts.len() > 0 {
|
||||
write!(f, "{};", stmts[0])?;
|
||||
if stmts.len() > 1 {
|
||||
for i in 1..stmts.len() {
|
||||
write!(f, "\n{};", stmts[i])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(f, "\n}}")
|
||||
}
|
||||
"if" => {
|
||||
let parts = self.subtokens.as_ref().expect("if had blank subtokens");
|
||||
write!(f, "(if ({}) {{{}}} else {{{}}})", parts[0], parts[1], parts[2])
|
||||
}
|
||||
"function" => {
|
||||
let parts = self.subtokens.as_ref().expect("function had blank subtokens");
|
||||
write!(f, "function {}(", self.value.as_ref().expect("function had blank value"))?;
|
||||
let params = &parts[1..];
|
||||
if params.len() > 0 {
|
||||
write!(f, "{}", params[0])?;
|
||||
if params.len() > 1 {
|
||||
for i in 1..params.len() {
|
||||
write!(f, ", {}", params[i])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(f, ") {}", parts[0])
|
||||
}
|
||||
"variable" => write!(f, "var {} = {}", self.value.as_ref().expect("var had blank value"), self.subtokens.as_ref().expect("var had blank subtokens")[0]),
|
||||
"assign" => write!(f, "{} = {}", self.value.as_ref().expect("assign had blank value"), self.subtokens.as_ref().expect("assign had blank subtokens")[0]),
|
||||
"while" => {
|
||||
let parts = self.subtokens.as_ref().expect("while had blank subtokens");
|
||||
write!(f, "while ({})\n{}\n", parts[0], parts[1])
|
||||
}
|
||||
"program" => {
|
||||
write!(f, "{{\n")?;
|
||||
let stmts = self.subtokens.as_ref().expect("program had blank subtokens");
|
||||
if stmts.len() > 0 {
|
||||
write!(f, "{};", stmts[0])?;
|
||||
if stmts.len() > 1 {
|
||||
for i in 1..stmts.len() {
|
||||
write!(f, "\n{};", stmts[i])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(f, "\n}}")
|
||||
}
|
||||
_ => write!(f, "(unknown node type {})", self.kind),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl AstNode {
|
||||
pub fn emit(&self, f: &mut dyn Write) -> Result<(), std::fmt::Error> {
|
||||
match &self.kind[..] {
|
||||
"number" => write!(f, "i32.const {}\n", self.value.as_ref().expect("number had blank value")),
|
||||
"add" => {
|
||||
let subtokens = self.subtokens.as_ref().expect("add had blank subtokens");
|
||||
subtokens[0].emit(f)?;
|
||||
subtokens[1].emit(f)?;
|
||||
write!(f, "i32.add\n")
|
||||
},
|
||||
_ => Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn number(num: i32) -> AstNode {
|
||||
AstNode {
|
||||
value: Some(num.to_string()),
|
||||
kind: "number".into(),
|
||||
subtokens: None,
|
||||
}
|
||||
}
|
||||
pub fn identifier<T: Into<String>>(id: T) -> AstNode {
|
||||
AstNode {
|
||||
value: Some(id.into()),
|
||||
kind: "identifier".into(),
|
||||
subtokens: None,
|
||||
}
|
||||
}
|
||||
pub fn not(operand: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "not".into(),
|
||||
subtokens: Some(vec![operand]),
|
||||
}
|
||||
}
|
||||
pub fn equal(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "equal".into(),
|
||||
subtokens: Some(vec![left, right]),
|
||||
}
|
||||
}
|
||||
pub fn not_equal(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "not_equal".into(),
|
||||
subtokens: Some(vec![left, right]),
|
||||
}
|
||||
}
|
||||
pub fn add(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "add".into(),
|
||||
subtokens: Some(vec![left, right]),
|
||||
}
|
||||
}
|
||||
pub fn subtract(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "subtract".into(),
|
||||
subtokens: Some(vec![left, right]),
|
||||
}
|
||||
}
|
||||
pub fn multiply(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "multiply".into(),
|
||||
subtokens: Some(vec![left, right]),
|
||||
}
|
||||
}
|
||||
pub fn divide(left: AstNode, right: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "divide".into(),
|
||||
subtokens: Some(vec![left, right]),
|
||||
}
|
||||
}
|
||||
pub fn call<T: Into<String>>(callee: T, args: Vec<AstNode>) -> AstNode {
|
||||
AstNode {
|
||||
value: Some(callee.into()),
|
||||
kind: "call".into(),
|
||||
subtokens: Some(args),
|
||||
}
|
||||
}
|
||||
pub fn r#return(operand: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "return".into(),
|
||||
subtokens: Some(vec![operand]),
|
||||
}
|
||||
}
|
||||
pub fn block(statements: Vec<AstNode>) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "block".into(),
|
||||
subtokens: Some(statements),
|
||||
}
|
||||
}
|
||||
pub fn r#if(conditional: AstNode, consequence: AstNode, alternative: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "if".into(),
|
||||
subtokens: Some(vec![conditional, consequence, alternative]),
|
||||
}
|
||||
}
|
||||
pub fn function<T: Into<String>>(name: T, parameters: Vec<(T, T)>, body: AstNode) -> AstNode {
|
||||
// Turn the parameter strings into ids.
|
||||
let mut params = vec![];
|
||||
params.push(body); // First one will always be the body.
|
||||
for p in parameters {
|
||||
params.push(AstNode::identifier(p));
|
||||
}
|
||||
AstNode {
|
||||
value: Some(name.into()),
|
||||
kind: "function".into(),
|
||||
subtokens: Some(params),
|
||||
}
|
||||
}
|
||||
pub fn variable<T: Into<String>>(name: T, value: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: Some(name.into()),
|
||||
kind: "variable".into(),
|
||||
subtokens: Some(vec![value]),
|
||||
}
|
||||
}
|
||||
pub fn assign<T: Into<String>>(name: T, value: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: Some(name.into()),
|
||||
kind: "assign".into(),
|
||||
subtokens: Some(vec![value]),
|
||||
}
|
||||
}
|
||||
pub fn r#while(conditional: AstNode, body: AstNode) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "while".into(),
|
||||
subtokens: Some(vec![conditional, body]),
|
||||
}
|
||||
}
|
||||
pub fn program(statements: Vec<AstNode>) -> AstNode {
|
||||
AstNode {
|
||||
value: None,
|
||||
kind: "program".into(),
|
||||
subtokens: Some(statements),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
import { AST } from './ast.ts';
|
||||
import { existsSync } from 'https://deno.land/std/fs/mod.ts';
|
||||
|
||||
const outputFileName = '../output.s';
|
||||
|
||||
const outputToFile = false;
|
||||
export async function emit(input: string) {
|
||||
if (outputToFile) {
|
||||
if (existsSync(outputFileName)) {
|
||||
await Deno.remove(outputFileName);
|
||||
}
|
||||
await Deno.create(outputFileName);
|
||||
const outputFile = await Deno.open(outputFileName, {
|
||||
write: true,
|
||||
append: true,
|
||||
});
|
||||
await Deno.write(outputFile.rid, new TextEncoder().encode(input));
|
||||
} else {
|
||||
console.log(input);
|
||||
}
|
||||
}
|
||||
|
||||
export class Main implements AST {
|
||||
constructor(public statements: Array<AST>) {}
|
||||
|
||||
emit() {
|
||||
emit('.global main');
|
||||
emit('main:');
|
||||
emit(' push {fp, lr}');
|
||||
this.statements.forEach((statement: any) => statement.emit());
|
||||
emit(' mov r0, #0');
|
||||
emit(' pop {fp, pc}');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Main
|
||||
&& this.statements.length === other.statements.length
|
||||
&& this.statements.every((arg: AST, i: number) => arg.equals(other.statements[i]));
|
||||
}
|
||||
}
|
||||
|
||||
export class Assert implements AST {
|
||||
constructor(public condition: AST) {}
|
||||
|
||||
emit() {
|
||||
this.condition.emit();
|
||||
emit(' cmp r0, #1');
|
||||
emit(` moveq r0, #'.'`);
|
||||
emit(` movne r0, #'F'`);
|
||||
emit(' bl putchar');
|
||||
}
|
||||
equals(other: AST): boolean {
|
||||
return other instanceof Assert
|
||||
&& this.condition.equals(other.condition);
|
||||
}
|
||||
}
|
10
src/index.ts
10
src/index.ts
@ -1,10 +0,0 @@
|
||||
import { parse } from './parser.ts';
|
||||
|
||||
let result = parse(`
|
||||
function main() {
|
||||
assert(1);
|
||||
assert(!0);
|
||||
}
|
||||
`);
|
||||
// console.log(JSON.stringify(result, null, 2));
|
||||
result.emit();
|
6
src/lib.rs
Normal file
6
src/lib.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
extern crate regex;
|
||||
|
||||
pub mod ast;
|
||||
pub mod parse;
|
61
src/main.rs
Normal file
61
src/main.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use pivot::ast::AstNode;
|
||||
use regex::Regex;
|
||||
|
||||
fn main() {
|
||||
pivot::parse::parse();
|
||||
// let src = AstNode::program(vec![
|
||||
// AstNode::add(
|
||||
// AstNode::integer(2),
|
||||
// AstNode::integer(3),
|
||||
// )
|
||||
// ]);
|
||||
// println!("{:?}", src);
|
||||
// let mut out = String::new();
|
||||
// src.emit(&mut out);
|
||||
// println!("{}", out);
|
||||
// test();
|
||||
}
|
||||
|
||||
// fn test() {
|
||||
// use pivot::ast::AstNode;
|
||||
//
|
||||
// let src = r#"
|
||||
// function factorial(n) {
|
||||
// var result = 1;
|
||||
// while (n != 1) {
|
||||
// result = result * n;
|
||||
// n = n - 1;
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
// "#;
|
||||
//
|
||||
// let actual_ast = pivot::parse::parse();
|
||||
// let expected_ast = AstNode::function("factorial", vec!["n"],
|
||||
// AstNode::block(vec![
|
||||
// AstNode::variable("result", AstNode::number(1)),
|
||||
// AstNode::r#while(
|
||||
// AstNode::not_equal(
|
||||
// AstNode::identifier("n"),
|
||||
// AstNode::number(1)
|
||||
// ),
|
||||
// AstNode::block(vec![
|
||||
// AstNode::assign("result",
|
||||
// AstNode::multiply(
|
||||
// AstNode::identifier("result"),
|
||||
// AstNode::identifier("n")
|
||||
// )
|
||||
// ),
|
||||
// AstNode::assign("n",
|
||||
// AstNode::subtract(
|
||||
// AstNode::identifier("n"),
|
||||
// AstNode::number(1)
|
||||
// )
|
||||
// )
|
||||
// ]),
|
||||
// ),
|
||||
// AstNode::r#return(AstNode::identifier("result"))
|
||||
// ])
|
||||
// );
|
||||
// println!("{}", expected_ast);
|
||||
// }
|
371
src/parse.rs
Normal file
371
src/parse.rs
Normal file
@ -0,0 +1,371 @@
|
||||
use crate::ast::AstNode;
|
||||
use std::ops::Range;
|
||||
use regex::Regex;
|
||||
|
||||
pub fn parse() -> AstNode {
|
||||
let src = "420";
|
||||
let mut n = String::new();
|
||||
|
||||
println!("src: {:?}", src);
|
||||
println!("n: {:?}", n);
|
||||
{
|
||||
let mut num = Parser::regex(r"\d+(\.\d+)?").bind(&mut n);
|
||||
|
||||
let mut full = num;
|
||||
println!("full: {}", full);
|
||||
println!("full.parse: {:?}", full.parse(src));
|
||||
println!("full: {}", full);
|
||||
}
|
||||
println!("src: {:?}", src);
|
||||
println!("n: {:?}", n);
|
||||
AstNode::block(vec![])
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ParserKind<'a> {
|
||||
Literal(String),
|
||||
Regex(Regex),
|
||||
And,
|
||||
Or,
|
||||
Repeat(usize),
|
||||
RepeatRange(Range<usize>),
|
||||
Bind(&'a mut String),
|
||||
}
|
||||
impl<'a> std::fmt::Display for ParserKind<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use ParserKind::*;
|
||||
match self {
|
||||
Literal(s) => write!(f, "Literal \"{}\"", s),
|
||||
Regex(r) => write!(f, "Regex /{}/", r.as_str()),
|
||||
And => write!(f, "And"),
|
||||
Or => write!(f, "Or"),
|
||||
Repeat(num) => write!(f, "Repeat {}", num),
|
||||
RepeatRange(range) => write!(f, "RepeatRange {:?}", range),
|
||||
Bind(_) => write!(f, "Bind"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Parser<'a> {
|
||||
kind: ParserKind<'a>,
|
||||
subparsers: Vec<Parser<'a>>,
|
||||
// bind: Option<&'a mut String>,
|
||||
}
|
||||
impl<'a> std::fmt::Display for Parser<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.pretty_print(f, 0)
|
||||
}
|
||||
}
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn parse<T: Into<String>>(&mut self, src: T) -> Result<(String, String), String> {
|
||||
use ParserKind::*;
|
||||
let s: String = src.into();
|
||||
match &mut self.kind {
|
||||
Literal(literal) => {
|
||||
if s.len() >= literal.len() && s[..literal.len()] == literal[..] {
|
||||
Ok((s[..literal.len()].to_owned(), s[literal.len()..].to_owned()))
|
||||
} else {
|
||||
Err(s)
|
||||
}
|
||||
}
|
||||
Regex(re) => {
|
||||
if let Some(mat) = re.find(&s) {
|
||||
if mat.start() == 0 {
|
||||
Ok((s[mat.start()..mat.end()].to_owned(), s[mat.end()..].to_owned()))
|
||||
} else {
|
||||
Err(s)
|
||||
}
|
||||
} else {
|
||||
Err(s)
|
||||
}
|
||||
}
|
||||
And => {
|
||||
let (lmatched, lrest) = self.subparsers[0].parse(s)?;
|
||||
let (rmatched, rrest) = self.subparsers[1].parse(lrest)?;
|
||||
Ok((lmatched + &rmatched, rrest))
|
||||
}
|
||||
Or => {
|
||||
if let Ok(lresult) = self.subparsers[0].parse(s.clone()) {
|
||||
Ok(lresult)
|
||||
} else {
|
||||
self.subparsers[1].parse(s.clone())
|
||||
}
|
||||
}
|
||||
Repeat(num_repeats) => {
|
||||
let mut matched = String::new();
|
||||
let mut rest = s.clone();
|
||||
for _ in 0..*num_repeats {
|
||||
let (m, r) = self.subparsers[0].parse(rest)?;
|
||||
matched += &m;
|
||||
rest = r;
|
||||
}
|
||||
Ok((matched, rest))
|
||||
}
|
||||
RepeatRange(range) => {
|
||||
let mut matched = String::new();
|
||||
let mut rest = s.clone();
|
||||
|
||||
// Parse up to range.start
|
||||
for _ in 0..range.start {
|
||||
let (m, r) = self.subparsers[0].parse(rest)?;
|
||||
matched += &m;
|
||||
rest = r;
|
||||
}
|
||||
|
||||
// Parse optionally up to range.end
|
||||
for _ in 0..(range.end - range.start) {
|
||||
let parse_result = self.subparsers[0].parse(rest);
|
||||
if let Err(r) = parse_result {
|
||||
rest = r;
|
||||
break;
|
||||
} else {
|
||||
let (m, r) = parse_result.unwrap();
|
||||
matched += &m;
|
||||
rest = r;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((matched, rest))
|
||||
}
|
||||
Bind(var) => {
|
||||
let (matched, rest) = self.subparsers[0].parse(s)?;
|
||||
**var = matched.clone();
|
||||
Ok((matched, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Static
|
||||
pub fn literal<T: Into<String>>(s: T) -> Parser<'a> {
|
||||
Parser {
|
||||
kind: ParserKind::Literal(s.into()),
|
||||
subparsers: vec![],
|
||||
// bind: None,
|
||||
}
|
||||
}
|
||||
pub fn regex<T: Into<String>>(s: T) -> Parser<'a> {
|
||||
Parser {
|
||||
kind: ParserKind::Regex(Regex::new(&s.into()).expect("could not compile regex")),
|
||||
subparsers: vec![],
|
||||
// bind: None,
|
||||
}
|
||||
}
|
||||
|
||||
// Instance
|
||||
pub fn and(self, r: Parser<'a>) -> Parser<'a> {
|
||||
Parser {
|
||||
kind: ParserKind::And,
|
||||
subparsers: vec![self, r],
|
||||
// bind: None,
|
||||
}
|
||||
}
|
||||
pub fn or(self, r: Parser<'a>) -> Parser<'a> {
|
||||
Parser {
|
||||
kind: ParserKind::Or,
|
||||
subparsers: vec![self, r],
|
||||
// bind: None,
|
||||
}
|
||||
}
|
||||
pub fn repeat(self, num_repeats: usize) -> Parser<'a> {
|
||||
Parser {
|
||||
kind: ParserKind::Repeat(num_repeats),
|
||||
subparsers: vec![self],
|
||||
// bind: None,
|
||||
}
|
||||
}
|
||||
pub fn repeat_range(self, num_repeats: Range<usize>) -> Parser<'a> {
|
||||
Parser {
|
||||
kind: ParserKind::RepeatRange(num_repeats),
|
||||
subparsers: vec![self],
|
||||
// bind: None,
|
||||
}
|
||||
}
|
||||
pub fn optional(self) -> Parser<'a> {
|
||||
Parser {
|
||||
kind: ParserKind::RepeatRange(0..1),
|
||||
subparsers: vec![self],
|
||||
// bind: None,
|
||||
}
|
||||
}
|
||||
pub fn bind(self, s: &'a mut String) -> Parser<'a> {
|
||||
Parser {
|
||||
kind: ParserKind::Bind(s),
|
||||
subparsers: vec![self],
|
||||
}
|
||||
}
|
||||
|
||||
// Other
|
||||
pub fn pretty_print(&self, f: &mut std::fmt::Formatter<'_>, indent: usize) -> std::fmt::Result {
|
||||
for _ in 0..indent {
|
||||
write!(f, " ");
|
||||
}
|
||||
write!(f, "{}", self.kind)?;
|
||||
if self.subparsers.len() > 0 {
|
||||
write!(f, " [\n")?;
|
||||
for subparser in &self.subparsers {
|
||||
subparser.pretty_print(f, indent + 2)?;
|
||||
write!(f, ",\n")?;
|
||||
}
|
||||
for _ in 0..indent {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
write!(f, "]")
|
||||
} else {
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use combinators::*;
|
||||
// pub mod combinators {
|
||||
// pub struct Parser<'a> {
|
||||
// source: &'a str,
|
||||
// subparsers: Vec<Parser<'a>>,
|
||||
// pub parse: Box<Fn(&'a str) -> Result<(&'a str, &'a str), &'a str>>,
|
||||
// }
|
||||
// impl Parser {
|
||||
// // pub type S = Into<String>;
|
||||
// pub fn literal<'a>(literal: &'a str) -> Parser {
|
||||
// Parser {
|
||||
// source: literal.into(),
|
||||
// subparsers: vec![],
|
||||
// parse: Box::new(|s: &'a str| -> Result<(&'a str, &'a str), &'a str> {
|
||||
// if src.len() >= literal.len() {
|
||||
// if src[..literal.len()] == literal[..] {
|
||||
// return Ok((&src[..literal.len()], &src[literal.len()..]));
|
||||
// }
|
||||
// }
|
||||
// Err(&src[..])
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// pub fn literal<'a, T>(literal: &'a str) -> T where T: Fn(&str) -> Result<(&str, &str), &str> + 'a {
|
||||
// move |src: &str| -> Result<(&str, &str), &str> {
|
||||
// if src.len() >= literal.len() {
|
||||
// if src[..literal.len()] == literal[..] {
|
||||
// return Ok((&src[..literal.len()], &src[literal.len()..]));
|
||||
// }
|
||||
// }
|
||||
// Err(&src[..])
|
||||
// }
|
||||
// }
|
||||
// pub fn and<'a>(left: T, right: T) -> T where T: Fn(&str) -> Result<(&str, &str), &str> + 'a {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Whitespace and comments.
|
||||
// let whitespace = Parser.regex(/[ \n\r\t]+/y);
|
||||
// let comments = Parser.regex(/[/][/].*/y).or(
|
||||
// Parser.regex(/[/][*].*[*][/]/sy)
|
||||
// );
|
||||
// let ignored = Parser.zeroOrMore(whitespace.or(comments));
|
||||
// // Tokens
|
||||
// let token = (pattern: RegExp) => Parser.regex(pattern).bind((value: any) => ignored.and(Parser.constant(value)));
|
||||
// let FUNCTION = token(/function\b/y);
|
||||
// let IF = token(/if\b/y);
|
||||
// let ELSE = token(/else\b/y);
|
||||
// let RETURN = token(/return\b/y);
|
||||
// let ASSIGN = token(/=/y).map(_ => Assign);
|
||||
// let VAR = token(/var\b/y);
|
||||
// let WHILE = token(/while\b/y);
|
||||
// let COMMA = token(/[,]/y);
|
||||
// let SEMICOLON = token(/[;]/y);
|
||||
// let LEFT_PAREN = token(/[(]/y);
|
||||
// let RIGHT_PAREN = token(/[)]/y);
|
||||
// let LEFT_BRACE = token(/[{]/y);
|
||||
// let RIGHT_BRACE = token(/[}]/y);
|
||||
// let NUMBER = token(/[0-9]/y).map((digits: any) => new Num(parseInt(digits)));
|
||||
// let ID = token(/[a-zA-Z_][a-zA-Z0-9_]*/y).map((x: any) => new Id(x));
|
||||
// let NOT = token(/!/y).map(_ => Not);
|
||||
// let EQUAL = token(/==/y).map(_ => Equal);
|
||||
// let NOT_EQUAL = token(/!=/y).map(_ => NotEqual);
|
||||
// let PLUS = token(/[+]/y).map(_ => Add);
|
||||
// let MINUS = token(/[-]/y).map(_ => Subtract);
|
||||
// let STAR = token(/[*]/y).map(_ => Multiply);
|
||||
// let SLASH = token(/[/]/y).map(_ => Divide);
|
||||
// // Expression parser
|
||||
// let expression: Parser<AST> = Parser.error('expression parser used before definition');
|
||||
// // Call parser
|
||||
// let args: Parser<Array<AST>> = expression.bind((arg: any) => Parser.zeroOrMore(COMMA.and(expression)).bind((args: any) => Parser.constant([arg, ...args]))).or(Parser.constant([]));
|
||||
// let functionCall: Parser<AST> = ID.bind((callee: any) => LEFT_PAREN.and(args.bind((args: any) => RIGHT_PAREN.and(Parser.constant(callee.equals(new Id('assert')) ? new Assert(args[0]) : new FunctionCall(callee, args))))));
|
||||
// // Atom
|
||||
// let atom: Parser<AST> = functionCall.or(ID).or(NUMBER).or(LEFT_PAREN.and(expression).bind((e: any) => RIGHT_PAREN.and(Parser.constant(e))));
|
||||
// // Unary operators
|
||||
// let unary: Parser<AST> = Parser.optional(NOT).bind((not: any) => atom.map((operand: any) => not ? new Not(operand) : operand));
|
||||
// // Infix operators
|
||||
// let infix = (operatorParser: any, operandParser: any) =>
|
||||
// operandParser.bind((operand: any) =>
|
||||
// Parser.zeroOrMore(
|
||||
// operatorParser.bind((operator: any) =>
|
||||
// operandParser.bind((operand: any) =>
|
||||
// Parser.constant({ operator, operand })
|
||||
// )
|
||||
// )
|
||||
// ).map((operatorTerms: any) =>
|
||||
// operatorTerms.reduce((left: any, { operator, operand }: { operator: any, operand: any }) =>
|
||||
// new operator(left, operand), operand)
|
||||
// )
|
||||
// );
|
||||
// let product = infix(STAR.or(SLASH), unary);
|
||||
// let sum = infix(PLUS.or(MINUS), product);
|
||||
// let comparison = infix(EQUAL.or(NOT_EQUAL), sum);
|
||||
// // Associativity
|
||||
// // Closing the loop: expression
|
||||
// expression.parse = comparison.parse;
|
||||
// // Statement
|
||||
// let statement: Parser<AST> = Parser.error('statement parser used before definition');
|
||||
// let returnStatement: Parser<AST> = RETURN.and(expression).bind((operand: any) => SEMICOLON.and(Parser.constant(new Return(operand))));
|
||||
// let expressionStatement: Parser<AST> = expression.bind((operand: any) => SEMICOLON.and(Parser.constant(operand)));
|
||||
// let ifStatement: Parser<AST> = IF.and(LEFT_PAREN).and(expression).bind((conditional: any) =>
|
||||
// RIGHT_PAREN.and(statement).bind((consequence: any) =>
|
||||
// ELSE.and(statement).bind((alternative: any) =>
|
||||
// Parser.constant(new If(conditional, consequence, alternative))
|
||||
// )
|
||||
// )
|
||||
// );
|
||||
// let whileStatement: Parser<AST> = WHILE.and(LEFT_PAREN).and(expression).bind((conditional: any) =>
|
||||
// RIGHT_PAREN.and(statement).bind((body: any) =>
|
||||
// Parser.constant(new While(conditional, body))
|
||||
// )
|
||||
// );
|
||||
// let varStatement: Parser<AST> = VAR.and(ID).bind((name: any) =>
|
||||
// ASSIGN.and(expression).bind((value: any) =>
|
||||
// SEMICOLON.and(Parser.constant(new Var(name, value)))
|
||||
// )
|
||||
// );
|
||||
// let assignmentStatement: Parser<AST> = ID.bind((name: any) =>
|
||||
// ASSIGN.and(expression).bind((value: any) =>
|
||||
// SEMICOLON.and(Parser.constant(new Assign(name, value)))
|
||||
// )
|
||||
// );
|
||||
// let blockStatement: Parser<AST> = LEFT_BRACE.and(Parser.zeroOrMore(statement)).bind((statements: any) =>
|
||||
// RIGHT_BRACE.and(Parser.constant(new Block(statements)))
|
||||
// );
|
||||
// let parameters: Parser<Array<string>> = ID.bind((param: any) =>
|
||||
// Parser.zeroOrMore(COMMA.and(ID)).bind((params: any) =>
|
||||
// Parser.constant([param, ...params])
|
||||
// )
|
||||
// ).or(Parser.constant([]));
|
||||
// let functionStatement: Parser<AST> = FUNCTION.and(ID).bind((name: any) =>
|
||||
// LEFT_PAREN.and(parameters).bind((parameters: any) =>
|
||||
// RIGHT_PAREN.and(blockStatement).bind((block: any) =>
|
||||
// Parser.constant(name.equals(new Id('main')) ? new Main(block.statements) : new FunctionDefinition(name, parameters, block))
|
||||
// )
|
||||
// )
|
||||
// );
|
||||
// let statementParser: Parser<AST> =
|
||||
// returnStatement
|
||||
// .or(functionStatement)
|
||||
// .or(ifStatement)
|
||||
// .or(whileStatement)
|
||||
// .or(varStatement)
|
||||
// .or(assignmentStatement)
|
||||
// .or(blockStatement)
|
||||
// .or(expressionStatement);
|
||||
// statement.parse = statementParser.parse;
|
||||
// let parser: Parser<AST> = ignored.and(Parser.zeroOrMore(statement)).map((statements: any) => new Block(statements));
|
||||
//
|
||||
// return parser.parseStringToCompletion(source);
|
200
src/parser.ts
200
src/parser.ts
@ -1,200 +0,0 @@
|
||||
import { AST, Num, Id, Not, Equal, NotEqual, Add, Subtract, Multiply, Divide, FunctionCall, Return, If, While, Var, Assign, Block, FunctionDefinition } from './ast.ts';
|
||||
import { Main, Assert } from './codegen.ts';
|
||||
|
||||
export function parse(source: string): AST {
|
||||
// Whitespace and comments.
|
||||
let whitespace = Parser.regex(/[ \n\r\t]+/y);
|
||||
let comments = Parser.regex(/[/][/].*/y).or(
|
||||
Parser.regex(/[/][*].*[*][/]/sy)
|
||||
);
|
||||
let ignored = Parser.zeroOrMore(whitespace.or(comments));
|
||||
// Tokens
|
||||
let token = (pattern: RegExp) => Parser.regex(pattern).bind((value: any) => ignored.and(Parser.constant(value)));
|
||||
let FUNCTION = token(/function\b/y);
|
||||
let IF = token(/if\b/y);
|
||||
let ELSE = token(/else\b/y);
|
||||
let RETURN = token(/return\b/y);
|
||||
let ASSIGN = token(/=/y).map(_ => Assign);
|
||||
let VAR = token(/var\b/y);
|
||||
let WHILE = token(/while\b/y);
|
||||
let COMMA = token(/[,]/y);
|
||||
let SEMICOLON = token(/[;]/y);
|
||||
let LEFT_PAREN = token(/[(]/y);
|
||||
let RIGHT_PAREN = token(/[)]/y);
|
||||
let LEFT_BRACE = token(/[{]/y);
|
||||
let RIGHT_BRACE = token(/[}]/y);
|
||||
let NUMBER = token(/[0-9]/y).map((digits: any) => new Num(parseInt(digits)));
|
||||
let ID = token(/[a-zA-Z_][a-zA-Z0-9_]*/y).map((x: any) => new Id(x));
|
||||
let NOT = token(/!/y).map(_ => Not);
|
||||
let EQUAL = token(/==/y).map(_ => Equal);
|
||||
let NOT_EQUAL = token(/!=/y).map(_ => NotEqual);
|
||||
let PLUS = token(/[+]/y).map(_ => Add);
|
||||
let MINUS = token(/[-]/y).map(_ => Subtract);
|
||||
let STAR = token(/[*]/y).map(_ => Multiply);
|
||||
let SLASH = token(/[/]/y).map(_ => Divide);
|
||||
// Expression parser
|
||||
let expression: Parser<AST> = Parser.error('expression parser used before definition');
|
||||
// Call parser
|
||||
let args: Parser<Array<AST>> = expression.bind((arg: any) => Parser.zeroOrMore(COMMA.and(expression)).bind((args: any) => Parser.constant([arg, ...args]))).or(Parser.constant([]));
|
||||
let functionCall: Parser<AST> = ID.bind((callee: any) => LEFT_PAREN.and(args.bind((args: any) => RIGHT_PAREN.and(Parser.constant(callee.equals(new Id('assert')) ? new Assert(args[0]) : new FunctionCall(callee, args))))));
|
||||
// Atom
|
||||
let atom: Parser<AST> = functionCall.or(ID).or(NUMBER).or(LEFT_PAREN.and(expression).bind((e: any) => RIGHT_PAREN.and(Parser.constant(e))));
|
||||
// Unary operators
|
||||
let unary: Parser<AST> = Parser.optional(NOT).bind((not: any) => atom.map((operand: any) => not ? new Not(operand) : operand));
|
||||
// Infix operators
|
||||
let infix = (operatorParser: any, operandParser: any) =>
|
||||
operandParser.bind((operand: any) =>
|
||||
Parser.zeroOrMore(
|
||||
operatorParser.bind((operator: any) =>
|
||||
operandParser.bind((operand: any) =>
|
||||
Parser.constant({ operator, operand })
|
||||
)
|
||||
)
|
||||
).map((operatorTerms: any) =>
|
||||
operatorTerms.reduce((left: any, { operator, operand }: { operator: any, operand: any }) =>
|
||||
new operator(left, operand), operand)
|
||||
)
|
||||
);
|
||||
let product = infix(STAR.or(SLASH), unary);
|
||||
let sum = infix(PLUS.or(MINUS), product);
|
||||
let comparison = infix(EQUAL.or(NOT_EQUAL), sum);
|
||||
// Associativity
|
||||
// Closing the loop: expression
|
||||
expression.parse = comparison.parse;
|
||||
// Statement
|
||||
let statement: Parser<AST> = Parser.error('statement parser used before definition');
|
||||
let returnStatement: Parser<AST> = RETURN.and(expression).bind((operand: any) => SEMICOLON.and(Parser.constant(new Return(operand))));
|
||||
let expressionStatement: Parser<AST> = expression.bind((operand: any) => SEMICOLON.and(Parser.constant(operand)));
|
||||
let ifStatement: Parser<AST> = IF.and(LEFT_PAREN).and(expression).bind((conditional: any) =>
|
||||
RIGHT_PAREN.and(statement).bind((consequence: any) =>
|
||||
ELSE.and(statement).bind((alternative: any) =>
|
||||
Parser.constant(new If(conditional, consequence, alternative))
|
||||
)
|
||||
)
|
||||
);
|
||||
let whileStatement: Parser<AST> = WHILE.and(LEFT_PAREN).and(expression).bind((conditional: any) =>
|
||||
RIGHT_PAREN.and(statement).bind((body: any) =>
|
||||
Parser.constant(new While(conditional, body))
|
||||
)
|
||||
);
|
||||
let varStatement: Parser<AST> = VAR.and(ID).bind((name: any) =>
|
||||
ASSIGN.and(expression).bind((value: any) =>
|
||||
SEMICOLON.and(Parser.constant(new Var(name, value)))
|
||||
)
|
||||
);
|
||||
let assignmentStatement: Parser<AST> = ID.bind((name: any) =>
|
||||
ASSIGN.and(expression).bind((value: any) =>
|
||||
SEMICOLON.and(Parser.constant(new Assign(name, value)))
|
||||
)
|
||||
);
|
||||
let blockStatement: Parser<AST> = LEFT_BRACE.and(Parser.zeroOrMore(statement)).bind((statements: any) =>
|
||||
RIGHT_BRACE.and(Parser.constant(new Block(statements)))
|
||||
);
|
||||
let parameters: Parser<Array<string>> = ID.bind((param: any) =>
|
||||
Parser.zeroOrMore(COMMA.and(ID)).bind((params: any) =>
|
||||
Parser.constant([param, ...params])
|
||||
)
|
||||
).or(Parser.constant([]));
|
||||
let functionStatement: Parser<AST> = FUNCTION.and(ID).bind((name: any) =>
|
||||
LEFT_PAREN.and(parameters).bind((parameters: any) =>
|
||||
RIGHT_PAREN.and(blockStatement).bind((block: any) =>
|
||||
Parser.constant(name.equals(new Id('main')) ? new Main(block.statements) : new FunctionDefinition(name, parameters, block))
|
||||
)
|
||||
)
|
||||
);
|
||||
let statementParser: Parser<AST> =
|
||||
returnStatement
|
||||
.or(functionStatement)
|
||||
.or(ifStatement)
|
||||
.or(whileStatement)
|
||||
.or(varStatement)
|
||||
.or(assignmentStatement)
|
||||
.or(blockStatement)
|
||||
.or(expressionStatement);
|
||||
statement.parse = statementParser.parse;
|
||||
let parser: Parser<AST> = ignored.and(Parser.zeroOrMore(statement)).map((statements: any) => new Block(statements));
|
||||
|
||||
return parser.parseStringToCompletion(source);
|
||||
}
|
||||
|
||||
export class Parser<T> {
|
||||
constructor(public parse: (src: Source) => (ParseResult<T> | null)) {}
|
||||
|
||||
parseStringToCompletion(string: string): T {
|
||||
let source = new Source(string, 0);
|
||||
let result = this.parse(source);
|
||||
if (!result)
|
||||
throw Error('Parse error at index 0');
|
||||
let index = result.source.index;
|
||||
if (index != result.source.string.length)
|
||||
throw Error(`Parse error at index ${index}`);
|
||||
return result.value;
|
||||
}
|
||||
|
||||
static regex(regex: RegExp): Parser<string> {
|
||||
return new Parser(source => source.match(regex));
|
||||
}
|
||||
static constant<U>(value: U): Parser<U> {
|
||||
return new Parser(source => new ParseResult(value, source));
|
||||
}
|
||||
static error<U>(message: string): Parser<U> {
|
||||
return new Parser(source => {
|
||||
throw Error(message);
|
||||
})
|
||||
}
|
||||
static zeroOrMore<U>(parser: Parser<U>): Parser<Array<U>> {
|
||||
return new Parser(source => {
|
||||
let results = [];
|
||||
let item: any;
|
||||
while (item = parser.parse(source)) {
|
||||
source = item.source;
|
||||
results.push(item.value);
|
||||
}
|
||||
return new ParseResult(results, source);
|
||||
});
|
||||
}
|
||||
static optional<U>(parser: Parser<U>): Parser<U | null> {
|
||||
return parser.or(Parser.constant(null));
|
||||
}
|
||||
|
||||
or(parser: Parser<T>): Parser<T> {
|
||||
return new Parser(source => {
|
||||
let result = this.parse(source);
|
||||
return result ? result : parser.parse(source);
|
||||
});
|
||||
}
|
||||
bind<U>(callback: (value: T) => Parser<U>): Parser<U> {
|
||||
return new Parser(source => {
|
||||
let result = this.parse(source);
|
||||
if (result) {
|
||||
let value = result.value;
|
||||
let source = result.source;
|
||||
return callback(value).parse(source);
|
||||
} else return null;
|
||||
});
|
||||
}
|
||||
and<U>(parser: Parser<U>): Parser<U> {
|
||||
return this.bind(_ => parser);
|
||||
}
|
||||
map<U>(callback: (t: T) => U): Parser<U> {
|
||||
return this.bind(value => Parser.constant(callback(value)))
|
||||
}
|
||||
}
|
||||
export class Source {
|
||||
constructor(public string: string, public index: number) {}
|
||||
|
||||
match(regex: RegExp): (ParseResult<string> | null) {
|
||||
regex.lastIndex = this.index;
|
||||
let match = this.string.match(regex);
|
||||
if (match) {
|
||||
let value = match[0];
|
||||
let newIndex = this.index + value.length;
|
||||
let source = new Source(this.string, newIndex);
|
||||
return new ParseResult(value, source);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
export class ParseResult<T> {
|
||||
constructor(public value: T, public source: Source) {}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": false
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user