rework parsing; add support for parentheses.

This commit is contained in:
trans_soup 2023-11-11 16:13:25 +01:00
parent 98757ce023
commit fd1bb299d4
3 changed files with 82 additions and 25 deletions

View File

@ -91,7 +91,3 @@ here are the commands that are specific to this REPL:
- `!.exit` exits the REPL.
- `!.env` prints out all the named functions.
- `!.clear` clears the console.
## parentheses
catgirl calculus supports using parentheses to group things. this implementation currently does not support this, thus it is non-compliant.

View File

@ -1,6 +1,8 @@
import Line from "./Line.mjs";
import Expr from "./Expr.mjs";
import { tokenize_line } from "./tokenize.mjs";
const Invalid = "invalid";
const Program = {
@ -27,18 +29,6 @@ export function parse (code) {
}
export function parse_line (line) {
if (line.length === 0) {
return {
type: Line.Empty,
};
}
if (line[0] === "#") {
return {
type: Line.Comment,
};
}
if (line[0] === "!") {
return {
type: Line.Special,
@ -46,7 +36,13 @@ export function parse_line (line) {
};
}
const tokens = line.split(/\s+/g).filter(t => t.length > 0);
const tokens = tokenize_line(line);
if (tokens.length === 0) {
return {
type: Line.Empty,
};
}
if (tokens.length >= 3 && tokens[1] === "=") {
return parse_binding(tokens);
@ -67,8 +63,6 @@ function parse_binding (tokens) {
if (tokens.length < 3) return invalid();
// TODO: handle parentheses here.
const name = tokens[0];
if (!is_valid_name(name)) return invalid();
@ -88,6 +82,7 @@ function parse_binding (tokens) {
}
function is_valid_name (name) {
if (typeof name !== "string") return false;
if (name.length === 0) return false;
if (name[0] === "#" || name[0] === "!") return false;
if (name === "=") return false;
@ -114,16 +109,19 @@ function Token (value) {
}
function parse_expression (tokens) {
if (!Array.isArray(tokens)) {
return Token(tokens);
}
if (tokens.length === 1) {
return Token(tokens[0]);
}
if (tokens.length === 0) {
return {
type: Expr.Invalid,
};
}
if (tokens.length === 1) {
return Token(tokens[0]);
}
if (tokens.some(is_right_arrow)) {
return parse_function (tokens);
}
@ -165,8 +163,8 @@ function parse_application (tokens) {
if (tokens.length === 2) {
return {
type: Expr.Application,
fn: Token(tokens[0]),
arg: Token(tokens[1]),
fn: parse_expression(tokens[0]),
arg: parse_expression(tokens[1]),
};
}

63
src/tokenize.mjs Normal file
View File

@ -0,0 +1,63 @@
import Line from "./Line.mjs";
export function tokenize_line (line) {
const tree = [];
const stack = [];
let node = tree;
let token = "";
function push (value) { node.push(value); }
function push_token () {
if (token.length > 0) {
push(token);
token = "";
}
}
const chars = line.split("");
for (const c of chars) {
if (is_parens_open(c)) {
push_token();
stack.push(node);
node.push([]);
node = node[node.length - 1];
continue;
}
if (is_parens_close(c)) {
push_token();
node = stack.pop();
continue;
}
if (is_whitespace(c)) {
push_token();
continue;
}
token += c;
}
push_token();
return tree;
}
function is_parens_open (c) {
return c === "(";
}
function is_parens_close (c) {
return c === ")";
}
function is_whitespace (c) {
return /\s/.test(c);
}
function exclude_comments (line) {
return line[0] !== "#";
}