mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 09:46:30 +00:00
add sugar; special loop, compound assign
This commit is contained in:
parent
b6c7f09f8b
commit
344214f1a4
@ -29,9 +29,11 @@ export type StmtKind =
|
||||
vtype?: VType;
|
||||
}
|
||||
| { type: "let"; param: Param; value: Expr }
|
||||
| { type: "assign"; subject: Expr; value: Expr }
|
||||
| { type: "assign"; assignType: AssignType; subject: Expr; value: Expr }
|
||||
| { type: "expr"; expr: Expr };
|
||||
|
||||
export type AssignType = "=" | "+=" | "-=";
|
||||
|
||||
export type Expr = {
|
||||
kind: ExprKind;
|
||||
pos: Pos;
|
||||
@ -59,6 +61,15 @@ export type ExprKind =
|
||||
type: "sym";
|
||||
ident: string;
|
||||
sym: Sym;
|
||||
}
|
||||
| { type: "while"; cond: Expr; body: Expr }
|
||||
| { type: "for_in"; param: Param; value: Expr; body: Expr }
|
||||
| {
|
||||
type: "for";
|
||||
decl?: Stmt;
|
||||
cond?: Expr;
|
||||
incr?: Stmt;
|
||||
body: Expr;
|
||||
};
|
||||
|
||||
export type UnaryType = "not";
|
||||
@ -119,3 +130,25 @@ export type Anno = {
|
||||
values: Expr[];
|
||||
pos: Pos;
|
||||
};
|
||||
|
||||
export class AstCreator {
|
||||
private nextNodeId = 0;
|
||||
|
||||
public stmt(kind: StmtKind, pos: Pos): Stmt {
|
||||
const id = this.nextNodeId;
|
||||
this.nextNodeId += 1;
|
||||
return { kind, pos, id };
|
||||
}
|
||||
|
||||
public expr(kind: ExprKind, pos: Pos): Expr {
|
||||
const id = this.nextNodeId;
|
||||
this.nextNodeId += 1;
|
||||
return { kind, pos, id };
|
||||
}
|
||||
|
||||
public etype(kind: ETypeKind, pos: Pos): EType {
|
||||
const id = this.nextNodeId;
|
||||
this.nextNodeId += 1;
|
||||
return { kind, pos, id };
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,9 @@ export interface AstVisitor<Args extends unknown[] = []> {
|
||||
visitBoolExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitNullExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitLoopExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitWhileExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitForInExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitForExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitBlockExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitSymExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitParam?(param: Param, ...args: Args): VisitRes;
|
||||
@ -157,6 +160,24 @@ export function visitExpr<Args extends unknown[] = []>(
|
||||
if (v.visitLoopExpr?.(expr, ...args) == "stop") return;
|
||||
visitExpr(expr.kind.body, v, ...args);
|
||||
break;
|
||||
case "while":
|
||||
if (v.visitWhileExpr?.(expr, ...args) == "stop") return;
|
||||
visitExpr(expr.kind.cond, v, ...args);
|
||||
visitExpr(expr.kind.body, v, ...args);
|
||||
break;
|
||||
case "for_in":
|
||||
if (v.visitForInExpr?.(expr, ...args) == "stop") return;
|
||||
visitParam(expr.kind.param, v, ...args);
|
||||
visitExpr(expr.kind.value, v, ...args);
|
||||
visitExpr(expr.kind.body, v, ...args);
|
||||
break;
|
||||
case "for":
|
||||
if (v.visitForExpr?.(expr, ...args) == "stop") return;
|
||||
if (expr.kind.decl) visitStmt(expr.kind.decl, v, ...args);
|
||||
if (expr.kind.cond) visitExpr(expr.kind.cond, v, ...args);
|
||||
if (expr.kind.incr) visitStmt(expr.kind.incr, v, ...args);
|
||||
visitExpr(expr.kind.body, v, ...args);
|
||||
break;
|
||||
case "block":
|
||||
if (v.visitBlockExpr?.(expr, ...args) == "stop") return;
|
||||
expr.kind.stmts.map((stmt) => visitStmt(stmt, v, ...args));
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { Builtins } from "./arch.ts";
|
||||
import { EType, Expr, Stmt } from "./ast.ts";
|
||||
import { printStackTrace, Reporter } from "./info.ts";
|
||||
import { Pos } from "./token.ts";
|
||||
@ -166,6 +165,9 @@ export class Checker {
|
||||
throw new Error();
|
||||
}
|
||||
const pos = stmt.pos;
|
||||
if (stmt.kind.assignType !== "=") {
|
||||
throw new Error("invalid ast: compound assign should be desugered");
|
||||
}
|
||||
const value = this.checkExpr(stmt.kind.value);
|
||||
switch (stmt.kind.subject.kind.type) {
|
||||
case "field": {
|
||||
@ -199,7 +201,10 @@ export class Checker {
|
||||
case "index": {
|
||||
const subject = this.checkExpr(stmt.kind.subject.kind.subject);
|
||||
if (subject.type !== "array" && subject.type !== "string") {
|
||||
this.report(`cannot index on non-array, got: ${subject.type}`, pos);
|
||||
this.report(
|
||||
`cannot index on non-array, got: ${subject.type}`,
|
||||
pos,
|
||||
);
|
||||
return { type: "error" };
|
||||
}
|
||||
const indexValue = this.checkExpr(stmt.kind.subject.kind.value);
|
||||
@ -207,7 +212,10 @@ export class Checker {
|
||||
this.report("cannot index on array with non-int", pos);
|
||||
return { type: "error" };
|
||||
}
|
||||
if (subject.type == "array" && !vtypesEqual(subject.inner, value)) {
|
||||
if (
|
||||
subject.type == "array" &&
|
||||
!vtypesEqual(subject.inner, value)
|
||||
) {
|
||||
this.report(
|
||||
`cannot assign incompatible type to array ` +
|
||||
`'${vtypeToString(subject)}'` +
|
||||
@ -357,7 +365,7 @@ export class Checker {
|
||||
if (subject.type === "array") {
|
||||
return subject.inner;
|
||||
}
|
||||
return { type: "int" }
|
||||
return { type: "int" };
|
||||
}
|
||||
|
||||
public checkCallExpr(expr: Expr): VType {
|
||||
|
50
compiler/desugar/compound_assign.ts
Normal file
50
compiler/desugar/compound_assign.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { AstCreator, Stmt } from "../ast.ts";
|
||||
import { AstVisitor, VisitRes, visitStmt, visitStmts } from "../ast_visitor.ts";
|
||||
|
||||
export class CompoundAssignDesugarer implements AstVisitor {
|
||||
public constructor(private astCreator: AstCreator) {}
|
||||
|
||||
public desugar(stmts: Stmt[]) {
|
||||
visitStmts(stmts, this);
|
||||
}
|
||||
|
||||
visitAssignStmt(stmt: Stmt): VisitRes {
|
||||
if (stmt.kind.type !== "assign") {
|
||||
throw new Error();
|
||||
}
|
||||
switch (stmt.kind.assignType) {
|
||||
case "=":
|
||||
return;
|
||||
case "+=": {
|
||||
stmt.kind = {
|
||||
type: "assign",
|
||||
assignType: "=",
|
||||
subject: stmt.kind.subject,
|
||||
value: this.astCreator.expr({
|
||||
type: "binary",
|
||||
binaryType: "+",
|
||||
left: stmt.kind.subject,
|
||||
right: stmt.kind.value,
|
||||
}, stmt.kind.value.pos),
|
||||
};
|
||||
visitStmt(stmt, this);
|
||||
return "stop";
|
||||
}
|
||||
case "-=": {
|
||||
stmt.kind = {
|
||||
type: "assign",
|
||||
assignType: "=",
|
||||
subject: stmt.kind.subject,
|
||||
value: this.astCreator.expr({
|
||||
type: "binary",
|
||||
binaryType: "-",
|
||||
left: stmt.kind.subject,
|
||||
right: stmt.kind.value,
|
||||
}, stmt.kind.value.pos),
|
||||
};
|
||||
visitStmt(stmt, this);
|
||||
return "stop";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
242
compiler/desugar/special_loop.ts
Normal file
242
compiler/desugar/special_loop.ts
Normal file
@ -0,0 +1,242 @@
|
||||
import { AstCreator, Expr, ExprKind, Stmt, StmtKind } from "../ast.ts";
|
||||
import { AstVisitor, visitExpr, VisitRes, visitStmts } from "../ast_visitor.ts";
|
||||
import { Pos } from "../token.ts";
|
||||
|
||||
export class SpecialLoopDesugarer implements AstVisitor {
|
||||
public constructor(
|
||||
private astCreator: AstCreator,
|
||||
) {}
|
||||
|
||||
public desugar(stmts: Stmt[]) {
|
||||
visitStmts(stmts, this);
|
||||
}
|
||||
|
||||
visitWhileExpr(expr: Expr): VisitRes {
|
||||
if (expr.kind.type !== "while") {
|
||||
throw new Error();
|
||||
}
|
||||
const npos: Pos = { index: 0, line: 1, col: 1 };
|
||||
const Expr = (kind: ExprKind, pos = npos) =>
|
||||
this.astCreator.expr(kind, pos);
|
||||
const Stmt = (kind: StmtKind, pos = npos) =>
|
||||
this.astCreator.stmt(kind, pos);
|
||||
|
||||
expr.kind = {
|
||||
type: "loop",
|
||||
body: Expr({
|
||||
type: "block",
|
||||
stmts: [
|
||||
Stmt({
|
||||
type: "expr",
|
||||
expr: Expr({
|
||||
type: "if",
|
||||
cond: Expr({
|
||||
type: "unary",
|
||||
unaryType: "not",
|
||||
subject: expr.kind.cond,
|
||||
}),
|
||||
truthy: Expr({
|
||||
type: "block",
|
||||
stmts: [
|
||||
Stmt({ type: "break" }),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
Stmt({
|
||||
type: "expr",
|
||||
expr: expr.kind.body,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
};
|
||||
|
||||
visitExpr(expr, this);
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitForInExpr(expr: Expr): VisitRes {
|
||||
if (expr.kind.type !== "for_in") {
|
||||
throw new Error();
|
||||
}
|
||||
const npos: Pos = { index: 0, line: 1, col: 1 };
|
||||
const Expr = (kind: ExprKind, pos = npos) =>
|
||||
this.astCreator.expr(kind, pos);
|
||||
const Stmt = (kind: StmtKind, pos = npos) =>
|
||||
this.astCreator.stmt(kind, pos);
|
||||
|
||||
expr.kind = {
|
||||
type: "block",
|
||||
stmts: [
|
||||
Stmt({
|
||||
type: "let",
|
||||
param: { ident: "::values", pos: npos },
|
||||
value: expr.kind.value,
|
||||
}),
|
||||
Stmt({
|
||||
type: "let",
|
||||
param: { ident: "::length", pos: npos },
|
||||
value: Expr({
|
||||
type: "call",
|
||||
subject: Expr({
|
||||
type: "ident",
|
||||
value: "int_array_length",
|
||||
}),
|
||||
args: [
|
||||
Expr({
|
||||
type: "ident",
|
||||
value: "::values",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
Stmt({
|
||||
type: "let",
|
||||
param: { ident: "::index", pos: npos },
|
||||
value: Expr({ type: "int", value: 0 }),
|
||||
}, expr.pos),
|
||||
Stmt({
|
||||
type: "expr",
|
||||
expr: Expr({
|
||||
type: "loop",
|
||||
body: Expr({
|
||||
type: "block",
|
||||
stmts: [
|
||||
Stmt({
|
||||
type: "expr",
|
||||
expr: Expr({
|
||||
type: "if",
|
||||
cond: Expr({
|
||||
type: "unary",
|
||||
unaryType: "not",
|
||||
subject: Expr({
|
||||
type: "binary",
|
||||
binaryType: "<",
|
||||
left: Expr({
|
||||
type: "ident",
|
||||
value: "::index",
|
||||
}),
|
||||
right: Expr({
|
||||
type: "ident",
|
||||
value: "::length",
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
truthy: Expr({
|
||||
type: "block",
|
||||
stmts: [
|
||||
Stmt({
|
||||
type: "break",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
Stmt({
|
||||
type: "let",
|
||||
param: expr.kind.param,
|
||||
value: Expr({
|
||||
type: "index",
|
||||
subject: Expr({
|
||||
type: "ident",
|
||||
value: "::values",
|
||||
}),
|
||||
value: Expr({
|
||||
type: "ident",
|
||||
value: "::index",
|
||||
}),
|
||||
}),
|
||||
}, expr.pos),
|
||||
Stmt({
|
||||
type: "expr",
|
||||
expr: expr.kind.body,
|
||||
}),
|
||||
Stmt({
|
||||
type: "assign",
|
||||
assignType: "+=",
|
||||
subject: Expr({
|
||||
type: "ident",
|
||||
value: "::index",
|
||||
}),
|
||||
value: Expr({
|
||||
type: "int",
|
||||
value: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
visitExpr(expr, this);
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitForExpr(expr: Expr): VisitRes {
|
||||
if (expr.kind.type !== "for") {
|
||||
throw new Error();
|
||||
}
|
||||
const Expr = (
|
||||
kind: ExprKind,
|
||||
pos: Pos = { index: 0, line: 1, col: 1 },
|
||||
) => this.astCreator.expr(kind, pos);
|
||||
const Stmt = (
|
||||
kind: StmtKind,
|
||||
pos: Pos = { index: 0, line: 1, col: 1 },
|
||||
) => this.astCreator.stmt(kind, pos);
|
||||
|
||||
expr.kind = {
|
||||
type: "block",
|
||||
stmts: [
|
||||
...[expr.kind.decl]
|
||||
.filter((v) => v !== undefined),
|
||||
Stmt({
|
||||
type: "expr",
|
||||
expr: Expr({
|
||||
type: "loop",
|
||||
body: Expr({
|
||||
type: "block",
|
||||
stmts: [
|
||||
...[expr.kind.cond]
|
||||
.filter((v) => v !== undefined)
|
||||
.map(
|
||||
(cond) =>
|
||||
Stmt({
|
||||
type: "expr",
|
||||
expr: Expr({
|
||||
type: "if",
|
||||
cond: Expr({
|
||||
type: "unary",
|
||||
unaryType: "not",
|
||||
subject: cond,
|
||||
}),
|
||||
truthy: Expr({
|
||||
type: "block",
|
||||
stmts: [
|
||||
Stmt({
|
||||
type: "break",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
Stmt({
|
||||
type: "expr",
|
||||
expr: expr.kind.body,
|
||||
}),
|
||||
...[expr.kind.incr]
|
||||
.filter((v) => v !== undefined),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
visitExpr(expr, this);
|
||||
return "stop";
|
||||
}
|
||||
}
|
@ -42,6 +42,9 @@ export class Lexer {
|
||||
"or",
|
||||
"and",
|
||||
"not",
|
||||
"while",
|
||||
"for",
|
||||
"in",
|
||||
];
|
||||
if (keywords.includes(value)) {
|
||||
return this.token(value, pos);
|
||||
@ -126,10 +129,6 @@ export class Lexer {
|
||||
this.step();
|
||||
return this.token("+=", pos);
|
||||
}
|
||||
if (first === "-" && !this.done() && this.test(">")) {
|
||||
this.step();
|
||||
return this.token("->", pos);
|
||||
}
|
||||
if (first === ":") {
|
||||
if (!this.done() && this.test(":")) {
|
||||
this.step();
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { Ast, File } from "./ast.ts";
|
||||
import { Ast, AstCreator, File } from "./ast.ts";
|
||||
import { stmtToString } from "./ast_visitor.ts";
|
||||
import { Checker } from "./checker.ts";
|
||||
import { CompoundAssignDesugarer } from "./desugar/compound_assign.ts";
|
||||
import { SpecialLoopDesugarer } from "./desugar/special_loop.ts";
|
||||
import { Reporter } from "./info.ts";
|
||||
import { Lexer } from "./lexer.ts";
|
||||
import { Lowerer } from "./lowerer.ts";
|
||||
@ -16,19 +19,22 @@ class Compilation {
|
||||
}
|
||||
}
|
||||
|
||||
//const text = await Deno.readTextFile("example.slg");
|
||||
const text = await Deno.readTextFile(Deno.args[0]);
|
||||
|
||||
const astCreator = new AstCreator();
|
||||
const reporter = new Reporter();
|
||||
|
||||
const lexer = new Lexer(text, reporter);
|
||||
|
||||
const parser = new Parser(lexer, reporter);
|
||||
const parser = new Parser(lexer, astCreator, reporter);
|
||||
const ast = parser.parse();
|
||||
|
||||
// console.log(JSON.stringify(ast, null, 4));
|
||||
new SpecialLoopDesugarer(astCreator).desugar(ast);
|
||||
|
||||
new Resolver(reporter).resolve(ast);
|
||||
|
||||
new CompoundAssignDesugarer(astCreator).desugar(ast);
|
||||
|
||||
new Checker(reporter).check(ast);
|
||||
|
||||
if (reporter.errorOccured()) {
|
||||
@ -40,7 +46,5 @@ const lowerer = new Lowerer(lexer.currentPos());
|
||||
lowerer.lower(ast);
|
||||
lowerer.printProgram();
|
||||
const program = lowerer.finish();
|
||||
//console.log(JSON.stringify(program, null, 4));
|
||||
// console.log(JSON.stringify(program));
|
||||
|
||||
await Deno.writeTextFile("out.slgbc", JSON.stringify(program));
|
||||
|
@ -1,5 +1,7 @@
|
||||
import {
|
||||
Anno,
|
||||
AssignType,
|
||||
AstCreator,
|
||||
BinaryType,
|
||||
EType,
|
||||
ETypeKind,
|
||||
@ -15,9 +17,12 @@ import { Pos, Token } from "./token.ts";
|
||||
|
||||
export class Parser {
|
||||
private currentToken: Token | null;
|
||||
private nextNodeId = 0;
|
||||
|
||||
public constructor(private lexer: Lexer, private reporter: Reporter) {
|
||||
public constructor(
|
||||
private lexer: Lexer,
|
||||
private astCreator: AstCreator,
|
||||
private reporter: Reporter,
|
||||
) {
|
||||
this.currentToken = lexer.next();
|
||||
}
|
||||
|
||||
@ -35,7 +40,9 @@ export class Parser {
|
||||
) {
|
||||
stmts.push(this.parseSingleLineBlockStmt());
|
||||
this.eatSemicolon();
|
||||
} else if (this.test("{") || this.test("if") || this.test("loop")) {
|
||||
} else if (
|
||||
["{", "if", "loop", "while", "for"].some((tt) => this.test(tt))
|
||||
) {
|
||||
const expr = this.parseMultiLineBlockExpr();
|
||||
stmts.push(this.stmt({ type: "expr", expr }, expr.pos));
|
||||
} else {
|
||||
@ -57,6 +64,12 @@ export class Parser {
|
||||
if (this.test("loop")) {
|
||||
return this.parseLoop();
|
||||
}
|
||||
if (this.test("while")) {
|
||||
return this.parseWhile();
|
||||
}
|
||||
if (this.test("for")) {
|
||||
return this.parseFor();
|
||||
}
|
||||
this.report("expected expr");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
@ -106,7 +119,9 @@ export class Parser {
|
||||
} else if (this.test("fn")) {
|
||||
stmts.push(this.parseSingleLineBlockStmt());
|
||||
stmts.push(this.parseFn());
|
||||
} else if (this.test("{") || this.test("if") || this.test("loop")) {
|
||||
} else if (
|
||||
["{", "if", "loop", "while", "for"].some((tt) => this.test(tt))
|
||||
) {
|
||||
const expr = this.parseMultiLineBlockExpr();
|
||||
if (this.test("}")) {
|
||||
this.step();
|
||||
@ -115,13 +130,19 @@ export class Parser {
|
||||
stmts.push(this.stmt({ type: "expr", expr }, expr.pos));
|
||||
} else {
|
||||
const expr = this.parseExpr();
|
||||
if (this.test("=")) {
|
||||
if (this.test("=") || this.test("+=") || this.test("-=")) {
|
||||
const assignType = this.current().type as AssignType;
|
||||
this.step();
|
||||
const value = this.parseExpr();
|
||||
this.eatSemicolon();
|
||||
stmts.push(
|
||||
this.stmt(
|
||||
{ type: "assign", subject: expr, value },
|
||||
{
|
||||
type: "assign",
|
||||
assignType,
|
||||
subject: expr,
|
||||
value,
|
||||
},
|
||||
expr.pos,
|
||||
),
|
||||
);
|
||||
@ -294,15 +315,22 @@ export class Parser {
|
||||
const value = this.parseExpr();
|
||||
return this.stmt({ type: "let", param, value }, pos);
|
||||
}
|
||||
|
||||
private parseAssign(): Stmt {
|
||||
const pos = this.pos();
|
||||
const subject = this.parseExpr();
|
||||
if (!this.test("=")) {
|
||||
return this.stmt({ type: "expr", expr: subject }, pos);
|
||||
}
|
||||
if (this.test("=") || this.test("+=") || this.test("-=")) {
|
||||
const assignType = this.current().type as AssignType;
|
||||
this.step();
|
||||
const value = this.parseExpr();
|
||||
return this.stmt({ type: "assign", subject, value }, pos);
|
||||
return this.stmt({
|
||||
type: "assign",
|
||||
assignType,
|
||||
subject,
|
||||
value,
|
||||
}, pos);
|
||||
}
|
||||
return this.stmt({ type: "expr", expr: subject }, pos);
|
||||
}
|
||||
|
||||
private parseReturn(): Stmt {
|
||||
@ -329,13 +357,92 @@ export class Parser {
|
||||
const pos = this.pos();
|
||||
this.step();
|
||||
if (!this.test("{")) {
|
||||
this.report("expected '}'");
|
||||
this.report("expected '{'");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
const body = this.parseExpr();
|
||||
return this.expr({ type: "loop", body }, pos);
|
||||
}
|
||||
|
||||
private parseWhile(): Expr {
|
||||
const pos = this.pos();
|
||||
this.step();
|
||||
const cond = this.parseExpr();
|
||||
if (!this.test("{")) {
|
||||
this.report("expected '{'");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
const body = this.parseExpr();
|
||||
return this.expr({ type: "while", cond, body }, pos);
|
||||
}
|
||||
|
||||
private parseFor(): Expr {
|
||||
const pos = this.pos();
|
||||
this.step();
|
||||
|
||||
if (this.test("(")) {
|
||||
return this.parseForClassicTail(pos);
|
||||
}
|
||||
|
||||
const paramRes = this.parseParam();
|
||||
if (!paramRes.ok) {
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
const param = paramRes.value;
|
||||
|
||||
if (!this.test("in")) {
|
||||
this.report("expected 'in'");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
this.step();
|
||||
const value = this.parseExpr();
|
||||
|
||||
if (!this.test("{")) {
|
||||
this.report("expected '{'");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
const body = this.parseExpr();
|
||||
return this.expr({ type: "for_in", param, value, body }, pos);
|
||||
}
|
||||
|
||||
private parseForClassicTail(pos: Pos): Expr {
|
||||
this.step();
|
||||
let decl: Stmt | undefined;
|
||||
if (!this.test(";")) {
|
||||
decl = this.parseLet();
|
||||
}
|
||||
if (!this.test(";")) {
|
||||
this.report("expected ';'");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
this.step();
|
||||
let cond: Expr | undefined;
|
||||
if (!this.test(";")) {
|
||||
cond = this.parseExpr();
|
||||
}
|
||||
if (!this.test(";")) {
|
||||
this.report("expected ';'");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
this.step();
|
||||
let incr: Stmt | undefined;
|
||||
if (!this.test(")")) {
|
||||
incr = this.parseAssign();
|
||||
}
|
||||
if (!this.test(")")) {
|
||||
this.report("expected '}'");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
this.step();
|
||||
|
||||
if (!this.test("{")) {
|
||||
this.report("expected '{'");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
const body = this.parseExpr();
|
||||
return this.expr({ type: "for", decl, cond, incr, body }, pos);
|
||||
}
|
||||
|
||||
private parseIf(): Expr {
|
||||
const pos = this.pos();
|
||||
this.step();
|
||||
@ -677,20 +784,14 @@ export class Parser {
|
||||
}
|
||||
|
||||
private stmt(kind: StmtKind, pos: Pos): Stmt {
|
||||
const id = this.nextNodeId;
|
||||
this.nextNodeId += 1;
|
||||
return { kind, pos, id };
|
||||
return this.astCreator.stmt(kind, pos);
|
||||
}
|
||||
|
||||
private expr(kind: ExprKind, pos: Pos): Expr {
|
||||
const id = this.nextNodeId;
|
||||
this.nextNodeId += 1;
|
||||
return { kind, pos, id };
|
||||
return this.astCreator.expr(kind, pos);
|
||||
}
|
||||
|
||||
private etype(kind: ETypeKind, pos: Pos): EType {
|
||||
const id = this.nextNodeId;
|
||||
this.nextNodeId += 1;
|
||||
return { kind, pos, id };
|
||||
return this.astCreator.etype(kind, pos);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { Expr, Stmt } from "./ast.ts";
|
||||
import { AstVisitor, visitExpr, VisitRes, visitStmts } from "./ast_visitor.ts";
|
||||
import {
|
||||
AstVisitor,
|
||||
visitExpr,
|
||||
VisitRes,
|
||||
visitStmt,
|
||||
visitStmts,
|
||||
} from "./ast_visitor.ts";
|
||||
import { printStackTrace, Reporter } from "./info.ts";
|
||||
import {
|
||||
FnSyms,
|
||||
@ -115,6 +121,19 @@ export class Resolver implements AstVisitor<[Syms]> {
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitForExpr(expr: Expr, syms: Syms): VisitRes {
|
||||
if (expr.kind.type !== "for") {
|
||||
throw new Error();
|
||||
}
|
||||
const childSyms = new LeafSyms(syms);
|
||||
if (expr.kind.decl) visitStmt(expr.kind.decl, this, syms);
|
||||
if (expr.kind.cond) visitExpr(expr.kind.cond, this, syms);
|
||||
if (expr.kind.incr) visitStmt(expr.kind.incr, this, syms);
|
||||
visitExpr(expr.kind.body, this, childSyms);
|
||||
|
||||
return "stop";
|
||||
}
|
||||
|
||||
private reportUseOfUndefined(ident: string, pos: Pos, _syms: Syms) {
|
||||
this.reporter.reportError({
|
||||
reporter: "Resolver",
|
||||
|
@ -7,7 +7,7 @@ if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
syn keyword Keyword break return let fn loop if else struct import or and not
|
||||
syn keyword Keyword break return let fn loop if else struct import or and not while for in
|
||||
syn keyword Special null
|
||||
syn keyword Type int string bool
|
||||
syn keyword Boolean true false
|
||||
@ -17,6 +17,8 @@ syn match Operator '-'
|
||||
syn match Operator '\*'
|
||||
syn match Operator '/'
|
||||
syn match Operator '='
|
||||
syn match Operator '+='
|
||||
syn match Operator '-='
|
||||
syn match Operator '=='
|
||||
syn match Operator '!='
|
||||
syn match Operator '<'
|
||||
|
@ -1,19 +1,4 @@
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||
"name": "Slige",
|
||||
"patterns": [
|
||||
{ "include": "#keywords" },
|
||||
{ "include": "#strings" },
|
||||
{ "include": "#numbers" },
|
||||
{ "include": "#operators" },
|
||||
{ "include": "#punctuation" },
|
||||
{ "include": "#functions" },
|
||||
{ "include": "#idents" }
|
||||
],
|
||||
"repository": {
|
||||
"keywords": {
|
||||
=======
|
||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||
"name": "Slige",
|
||||
"patterns": [
|
||||
@ -45,55 +30,7 @@
|
||||
"patterns": [
|
||||
{
|
||||
"name": "keyword.control.slige",
|
||||
"match": "\\b(break|return|let|fn|loop|if|else|struct|import|or|and|not)\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.language.slige",
|
||||
"match": "\\b(null|false|true)\\b"
|
||||
},
|
||||
{
|
||||
"name": "storage.type.slige",
|
||||
"match": "\\b(int|string|bool)\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"strings": {
|
||||
"name": "string.quoted.double.slige",
|
||||
"begin": "\"",
|
||||
"end": "\"",
|
||||
"patterns": [
|
||||
{
|
||||
"name": "constant.character.escape.slige",
|
||||
"match": "\\\\."
|
||||
}
|
||||
]
|
||||
},
|
||||
"numbers": {
|
||||
"patterns": [
|
||||
{
|
||||
"name": "constant.numeric.slige",
|
||||
"match": "\\b0\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.numeric.slige",
|
||||
"match": "\\b[1-9][0-9]*(\\.[0-9]+)?\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.numeric.slige",
|
||||
"match": "\\b0x[0-9a-fA-F]+?\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.numeric.slige",
|
||||
"match": "\\b0b[01]+?\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"operators": {
|
||||
>>>>>>> 53a965f (everything)
|
||||
"patterns": [
|
||||
{
|
||||
"name": "keyword.control.slige",
|
||||
"match": "\\b(break|return|let|fn|loop|if|else|struct|import|or|and|not)\\b"
|
||||
"match": "\\b(break|return|let|fn|loop|if|else|struct|import|or|and|not|while|for|in)\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.language.slige",
|
||||
@ -139,7 +76,7 @@
|
||||
"operators": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\+|\\-|\\*|\\/|=|(==)|(!=)|<|>|(<=)|(>=)|\\.|:|(\\->)|(::)|(::<)",
|
||||
"match": "\\+|\\-|\\*|\\/|=|(+=)|(-=)|(==)|(!=)|<|>|(<=)|(>=)|\\.|:|(\\->)|(::)|(::<)",
|
||||
"name": "keyword.operator.slige"
|
||||
}
|
||||
]
|
||||
|
8
examples/for.slg
Normal file
8
examples/for.slg
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
fn main() {
|
||||
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
|
||||
}
|
||||
}
|
||||
|
6
examples/increment.slg
Normal file
6
examples/increment.slg
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
fn main() {
|
||||
let i = 0;
|
||||
i += 1;
|
||||
}
|
||||
|
80
examples/special_loops.slg
Normal file
80
examples/special_loops.slg
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
fn string_push_char(str: string, value: int) -> string #[builtin(StringPushChar)] {}
|
||||
fn string_char_at(str: string, index: int) -> int #[builtin(StringCharAt)] {}
|
||||
fn string_length(str: string) -> int #[builtin(StringLength)] {}
|
||||
|
||||
fn string_array_new() -> [string] #[builtin(ArrayNew)] {}
|
||||
fn string_array_push(array: [string], value: string) #[builtin(ArrayPush)] {}
|
||||
fn string_array_length(array: [string]) -> int #[builtin(ArrayLength)] {}
|
||||
fn string_array_at(array: [string], index: int) -> string #[builtin(ArrayAt)] {}
|
||||
|
||||
fn int_array_new() -> [int] #[builtin(ArrayNew)] {}
|
||||
fn int_array_push(array: [int], value: int) #[builtin(ArrayPush)] {}
|
||||
fn int_array_length(array: [int]) -> int #[builtin(ArrayLength)] {}
|
||||
fn int_array_at(array: [int], index: int) -> int #[builtin(ArrayAt)] {}
|
||||
|
||||
fn file_open(filename: string, mode: string) -> int #[builtin(FileOpen)] {}
|
||||
fn file_close(file: int) #[builtin(FileClose)] {}
|
||||
fn file_write_string(file: int, content: string) -> int #[builtin(FileWriteString)] {}
|
||||
fn file_read_char(file: int) -> int #[builtin(FileReadChar)] {}
|
||||
fn file_read_to_string(file: int) -> string #[builtin(FileReadToString)] {}
|
||||
fn file_flush(file: int) #[builtin(FileFlush)] {}
|
||||
fn file_eof(file: int) -> bool #[builtin(FileEof)] {}
|
||||
|
||||
fn stdin() -> int { 0 }
|
||||
fn stdout() -> int { 1 }
|
||||
fn stderr() -> int { 2 }
|
||||
|
||||
fn file_read_line(file: int) -> string {
|
||||
let line = "";
|
||||
loop {
|
||||
if file_eof(file) {
|
||||
break;
|
||||
}
|
||||
let ch = file_read_char(file);
|
||||
if ch == "\n"[0] {
|
||||
break;
|
||||
}
|
||||
line = string_push_char(line, ch);
|
||||
}
|
||||
line
|
||||
}
|
||||
|
||||
fn print(msg: string) #[builtin(Print)] {}
|
||||
fn println(msg: string) { print(msg + "\n") }
|
||||
|
||||
fn input(prompt: string) -> string {
|
||||
print("> ");
|
||||
file_flush(stdout());
|
||||
file_read_line(stdin())
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
fn main() {
|
||||
let i = 0;
|
||||
while i < 3 {
|
||||
println("hello world");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let chars = string_to_array("12435");
|
||||
|
||||
for char in chars {
|
||||
println(string_push_char("", char));
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_array(value: string) -> [int] {
|
||||
let result = int_array_new();
|
||||
let length = string_length(value);
|
||||
|
||||
for (let i = 0; i < length; i += 1) {
|
||||
int_array_push(result, value[i]);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
|
8
examples/while.slg
Normal file
8
examples/while.slg
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
fn main() {
|
||||
let i = 0;
|
||||
while i < 10 {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ auto Array::at(int32_t index) & -> Value&
|
||||
{
|
||||
if (index >= static_cast<int32_t>(this->values.size()) || index < 0) {
|
||||
std::cout << std::format(
|
||||
"index not in range, expected to be in range (0..{}), got: {}",
|
||||
"index not in range, expected to be in range (0..{}), got: {}\n",
|
||||
this->values.size(), index);
|
||||
exit(1);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ auto String::at(int32_t index) -> int32_t
|
||||
{
|
||||
if (index >= static_cast<int32_t>(this->value.length()) || index < 0) {
|
||||
std::cout << std::format(
|
||||
"index not in range, expected to be in range (0..{}), got: {}",
|
||||
"index not in range, expected to be in range (0..{}), got: {}\n",
|
||||
this->value.length() - 1, index);
|
||||
exit(1);
|
||||
}
|
||||
|
51
stdlib.slg
Normal file
51
stdlib.slg
Normal file
@ -0,0 +1,51 @@
|
||||
|
||||
fn string_push_char(str: string, value: int) -> string #[builtin(StringPushChar)] {}
|
||||
fn string_char_at(str: string, index: int) -> int #[builtin(StringCharAt)] {}
|
||||
fn string_length(str: string) -> int #[builtin(StringLength)] {}
|
||||
|
||||
fn string_array_new() -> [string] #[builtin(ArrayNew)] {}
|
||||
fn string_array_push(array: [string], value: string) #[builtin(ArrayPush)] {}
|
||||
fn string_array_length(array: [string]) -> int #[builtin(ArrayLength)] {}
|
||||
fn string_array_at(array: [string], index: int) -> string #[builtin(ArrayAt)] {}
|
||||
|
||||
fn int_array_new() -> [int] #[builtin(ArrayNew)] {}
|
||||
fn int_array_push(array: [int], value: int) #[builtin(ArrayPush)] {}
|
||||
fn int_array_length(array: [int]) -> int #[builtin(ArrayLength)] {}
|
||||
fn int_array_at(array: [int], index: int) -> int #[builtin(ArrayAt)] {}
|
||||
|
||||
fn file_open(filename: string, mode: string) -> int #[builtin(FileOpen)] {}
|
||||
fn file_close(file: int) #[builtin(FileClose)] {}
|
||||
fn file_write_string(file: int, content: string) -> int #[builtin(FileWriteString)] {}
|
||||
fn file_read_char(file: int) -> int #[builtin(FileReadChar)] {}
|
||||
fn file_read_to_string(file: int) -> string #[builtin(FileReadToString)] {}
|
||||
fn file_flush(file: int) #[builtin(FileFlush)] {}
|
||||
fn file_eof(file: int) -> bool #[builtin(FileEof)] {}
|
||||
|
||||
fn stdin() -> int { 0 }
|
||||
fn stdout() -> int { 1 }
|
||||
fn stderr() -> int { 2 }
|
||||
|
||||
fn file_read_line(file: int) -> string {
|
||||
let line = "";
|
||||
loop {
|
||||
if file_eof(file) {
|
||||
break;
|
||||
}
|
||||
let ch = file_read_char(file);
|
||||
if ch == "\n"[0] {
|
||||
break;
|
||||
}
|
||||
line = string_push_char(line, ch);
|
||||
}
|
||||
line
|
||||
}
|
||||
|
||||
fn print(msg: string) #[builtin(Print)] {}
|
||||
fn println(msg: string) { print(msg + "\n") }
|
||||
|
||||
fn input(prompt: string) -> string {
|
||||
print("> ");
|
||||
file_flush(stdout());
|
||||
file_read_line(stdin())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user