mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 12:46:31 +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;
|
vtype?: VType;
|
||||||
}
|
}
|
||||||
| { type: "let"; param: Param; value: Expr }
|
| { type: "let"; param: Param; value: Expr }
|
||||||
| { type: "assign"; subject: Expr; value: Expr }
|
| { type: "assign"; assignType: AssignType; subject: Expr; value: Expr }
|
||||||
| { type: "expr"; expr: Expr };
|
| { type: "expr"; expr: Expr };
|
||||||
|
|
||||||
|
export type AssignType = "=" | "+=" | "-=";
|
||||||
|
|
||||||
export type Expr = {
|
export type Expr = {
|
||||||
kind: ExprKind;
|
kind: ExprKind;
|
||||||
pos: Pos;
|
pos: Pos;
|
||||||
@ -59,6 +61,15 @@ export type ExprKind =
|
|||||||
type: "sym";
|
type: "sym";
|
||||||
ident: string;
|
ident: string;
|
||||||
sym: Sym;
|
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";
|
export type UnaryType = "not";
|
||||||
@ -119,3 +130,25 @@ export type Anno = {
|
|||||||
values: Expr[];
|
values: Expr[];
|
||||||
pos: Pos;
|
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;
|
visitBoolExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||||
visitNullExpr?(expr: Expr, ...args: Args): VisitRes;
|
visitNullExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||||
visitLoopExpr?(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;
|
visitBlockExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||||
visitSymExpr?(expr: Expr, ...args: Args): VisitRes;
|
visitSymExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||||
visitParam?(param: Param, ...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;
|
if (v.visitLoopExpr?.(expr, ...args) == "stop") return;
|
||||||
visitExpr(expr.kind.body, v, ...args);
|
visitExpr(expr.kind.body, v, ...args);
|
||||||
break;
|
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":
|
case "block":
|
||||||
if (v.visitBlockExpr?.(expr, ...args) == "stop") return;
|
if (v.visitBlockExpr?.(expr, ...args) == "stop") return;
|
||||||
expr.kind.stmts.map((stmt) => visitStmt(stmt, v, ...args));
|
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 { EType, Expr, Stmt } from "./ast.ts";
|
||||||
import { printStackTrace, Reporter } from "./info.ts";
|
import { printStackTrace, Reporter } from "./info.ts";
|
||||||
import { Pos } from "./token.ts";
|
import { Pos } from "./token.ts";
|
||||||
@ -166,6 +165,9 @@ export class Checker {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const pos = stmt.pos;
|
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);
|
const value = this.checkExpr(stmt.kind.value);
|
||||||
switch (stmt.kind.subject.kind.type) {
|
switch (stmt.kind.subject.kind.type) {
|
||||||
case "field": {
|
case "field": {
|
||||||
@ -199,7 +201,10 @@ export class Checker {
|
|||||||
case "index": {
|
case "index": {
|
||||||
const subject = this.checkExpr(stmt.kind.subject.kind.subject);
|
const subject = this.checkExpr(stmt.kind.subject.kind.subject);
|
||||||
if (subject.type !== "array" && subject.type !== "string") {
|
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" };
|
return { type: "error" };
|
||||||
}
|
}
|
||||||
const indexValue = this.checkExpr(stmt.kind.subject.kind.value);
|
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);
|
this.report("cannot index on array with non-int", pos);
|
||||||
return { type: "error" };
|
return { type: "error" };
|
||||||
}
|
}
|
||||||
if (subject.type == "array" && !vtypesEqual(subject.inner, value)) {
|
if (
|
||||||
|
subject.type == "array" &&
|
||||||
|
!vtypesEqual(subject.inner, value)
|
||||||
|
) {
|
||||||
this.report(
|
this.report(
|
||||||
`cannot assign incompatible type to array ` +
|
`cannot assign incompatible type to array ` +
|
||||||
`'${vtypeToString(subject)}'` +
|
`'${vtypeToString(subject)}'` +
|
||||||
@ -357,7 +365,7 @@ export class Checker {
|
|||||||
if (subject.type === "array") {
|
if (subject.type === "array") {
|
||||||
return subject.inner;
|
return subject.inner;
|
||||||
}
|
}
|
||||||
return { type: "int" }
|
return { type: "int" };
|
||||||
}
|
}
|
||||||
|
|
||||||
public checkCallExpr(expr: Expr): VType {
|
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",
|
"or",
|
||||||
"and",
|
"and",
|
||||||
"not",
|
"not",
|
||||||
|
"while",
|
||||||
|
"for",
|
||||||
|
"in",
|
||||||
];
|
];
|
||||||
if (keywords.includes(value)) {
|
if (keywords.includes(value)) {
|
||||||
return this.token(value, pos);
|
return this.token(value, pos);
|
||||||
@ -126,10 +129,6 @@ export class Lexer {
|
|||||||
this.step();
|
this.step();
|
||||||
return this.token("+=", pos);
|
return this.token("+=", pos);
|
||||||
}
|
}
|
||||||
if (first === "-" && !this.done() && this.test(">")) {
|
|
||||||
this.step();
|
|
||||||
return this.token("->", pos);
|
|
||||||
}
|
|
||||||
if (first === ":") {
|
if (first === ":") {
|
||||||
if (!this.done() && this.test(":")) {
|
if (!this.done() && this.test(":")) {
|
||||||
this.step();
|
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 { Checker } from "./checker.ts";
|
||||||
|
import { CompoundAssignDesugarer } from "./desugar/compound_assign.ts";
|
||||||
|
import { SpecialLoopDesugarer } from "./desugar/special_loop.ts";
|
||||||
import { Reporter } from "./info.ts";
|
import { Reporter } from "./info.ts";
|
||||||
import { Lexer } from "./lexer.ts";
|
import { Lexer } from "./lexer.ts";
|
||||||
import { Lowerer } from "./lowerer.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 text = await Deno.readTextFile(Deno.args[0]);
|
||||||
|
|
||||||
|
const astCreator = new AstCreator();
|
||||||
const reporter = new Reporter();
|
const reporter = new Reporter();
|
||||||
|
|
||||||
const lexer = new Lexer(text, reporter);
|
const lexer = new Lexer(text, reporter);
|
||||||
|
|
||||||
const parser = new Parser(lexer, reporter);
|
const parser = new Parser(lexer, astCreator, reporter);
|
||||||
const ast = parser.parse();
|
const ast = parser.parse();
|
||||||
|
|
||||||
// console.log(JSON.stringify(ast, null, 4));
|
new SpecialLoopDesugarer(astCreator).desugar(ast);
|
||||||
|
|
||||||
new Resolver(reporter).resolve(ast);
|
new Resolver(reporter).resolve(ast);
|
||||||
|
|
||||||
|
new CompoundAssignDesugarer(astCreator).desugar(ast);
|
||||||
|
|
||||||
new Checker(reporter).check(ast);
|
new Checker(reporter).check(ast);
|
||||||
|
|
||||||
if (reporter.errorOccured()) {
|
if (reporter.errorOccured()) {
|
||||||
@ -40,7 +46,5 @@ const lowerer = new Lowerer(lexer.currentPos());
|
|||||||
lowerer.lower(ast);
|
lowerer.lower(ast);
|
||||||
lowerer.printProgram();
|
lowerer.printProgram();
|
||||||
const program = lowerer.finish();
|
const program = lowerer.finish();
|
||||||
//console.log(JSON.stringify(program, null, 4));
|
|
||||||
// console.log(JSON.stringify(program));
|
|
||||||
|
|
||||||
await Deno.writeTextFile("out.slgbc", JSON.stringify(program));
|
await Deno.writeTextFile("out.slgbc", JSON.stringify(program));
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Anno,
|
Anno,
|
||||||
|
AssignType,
|
||||||
|
AstCreator,
|
||||||
BinaryType,
|
BinaryType,
|
||||||
EType,
|
EType,
|
||||||
ETypeKind,
|
ETypeKind,
|
||||||
@ -15,9 +17,12 @@ import { Pos, Token } from "./token.ts";
|
|||||||
|
|
||||||
export class Parser {
|
export class Parser {
|
||||||
private currentToken: Token | null;
|
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();
|
this.currentToken = lexer.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +40,9 @@ export class Parser {
|
|||||||
) {
|
) {
|
||||||
stmts.push(this.parseSingleLineBlockStmt());
|
stmts.push(this.parseSingleLineBlockStmt());
|
||||||
this.eatSemicolon();
|
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();
|
const expr = this.parseMultiLineBlockExpr();
|
||||||
stmts.push(this.stmt({ type: "expr", expr }, expr.pos));
|
stmts.push(this.stmt({ type: "expr", expr }, expr.pos));
|
||||||
} else {
|
} else {
|
||||||
@ -57,6 +64,12 @@ export class Parser {
|
|||||||
if (this.test("loop")) {
|
if (this.test("loop")) {
|
||||||
return this.parseLoop();
|
return this.parseLoop();
|
||||||
}
|
}
|
||||||
|
if (this.test("while")) {
|
||||||
|
return this.parseWhile();
|
||||||
|
}
|
||||||
|
if (this.test("for")) {
|
||||||
|
return this.parseFor();
|
||||||
|
}
|
||||||
this.report("expected expr");
|
this.report("expected expr");
|
||||||
return this.expr({ type: "error" }, pos);
|
return this.expr({ type: "error" }, pos);
|
||||||
}
|
}
|
||||||
@ -106,7 +119,9 @@ export class Parser {
|
|||||||
} else if (this.test("fn")) {
|
} else if (this.test("fn")) {
|
||||||
stmts.push(this.parseSingleLineBlockStmt());
|
stmts.push(this.parseSingleLineBlockStmt());
|
||||||
stmts.push(this.parseFn());
|
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();
|
const expr = this.parseMultiLineBlockExpr();
|
||||||
if (this.test("}")) {
|
if (this.test("}")) {
|
||||||
this.step();
|
this.step();
|
||||||
@ -115,13 +130,19 @@ export class Parser {
|
|||||||
stmts.push(this.stmt({ type: "expr", expr }, expr.pos));
|
stmts.push(this.stmt({ type: "expr", expr }, expr.pos));
|
||||||
} else {
|
} else {
|
||||||
const expr = this.parseExpr();
|
const expr = this.parseExpr();
|
||||||
if (this.test("=")) {
|
if (this.test("=") || this.test("+=") || this.test("-=")) {
|
||||||
|
const assignType = this.current().type as AssignType;
|
||||||
this.step();
|
this.step();
|
||||||
const value = this.parseExpr();
|
const value = this.parseExpr();
|
||||||
this.eatSemicolon();
|
this.eatSemicolon();
|
||||||
stmts.push(
|
stmts.push(
|
||||||
this.stmt(
|
this.stmt(
|
||||||
{ type: "assign", subject: expr, value },
|
{
|
||||||
|
type: "assign",
|
||||||
|
assignType,
|
||||||
|
subject: expr,
|
||||||
|
value,
|
||||||
|
},
|
||||||
expr.pos,
|
expr.pos,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -294,15 +315,22 @@ export class Parser {
|
|||||||
const value = this.parseExpr();
|
const value = this.parseExpr();
|
||||||
return this.stmt({ type: "let", param, value }, pos);
|
return this.stmt({ type: "let", param, value }, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseAssign(): Stmt {
|
private parseAssign(): Stmt {
|
||||||
const pos = this.pos();
|
const pos = this.pos();
|
||||||
const subject = this.parseExpr();
|
const subject = this.parseExpr();
|
||||||
if (!this.test("=")) {
|
if (this.test("=") || this.test("+=") || this.test("-=")) {
|
||||||
return this.stmt({ type: "expr", expr: subject }, pos);
|
const assignType = this.current().type as AssignType;
|
||||||
|
this.step();
|
||||||
|
const value = this.parseExpr();
|
||||||
|
return this.stmt({
|
||||||
|
type: "assign",
|
||||||
|
assignType,
|
||||||
|
subject,
|
||||||
|
value,
|
||||||
|
}, pos);
|
||||||
}
|
}
|
||||||
this.step();
|
return this.stmt({ type: "expr", expr: subject }, pos);
|
||||||
const value = this.parseExpr();
|
|
||||||
return this.stmt({ type: "assign", subject, value }, pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseReturn(): Stmt {
|
private parseReturn(): Stmt {
|
||||||
@ -329,13 +357,92 @@ export class Parser {
|
|||||||
const pos = this.pos();
|
const pos = this.pos();
|
||||||
this.step();
|
this.step();
|
||||||
if (!this.test("{")) {
|
if (!this.test("{")) {
|
||||||
this.report("expected '}'");
|
this.report("expected '{'");
|
||||||
return this.expr({ type: "error" }, pos);
|
return this.expr({ type: "error" }, pos);
|
||||||
}
|
}
|
||||||
const body = this.parseExpr();
|
const body = this.parseExpr();
|
||||||
return this.expr({ type: "loop", body }, pos);
|
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 {
|
private parseIf(): Expr {
|
||||||
const pos = this.pos();
|
const pos = this.pos();
|
||||||
this.step();
|
this.step();
|
||||||
@ -677,20 +784,14 @@ export class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private stmt(kind: StmtKind, pos: Pos): Stmt {
|
private stmt(kind: StmtKind, pos: Pos): Stmt {
|
||||||
const id = this.nextNodeId;
|
return this.astCreator.stmt(kind, pos);
|
||||||
this.nextNodeId += 1;
|
|
||||||
return { kind, pos, id };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private expr(kind: ExprKind, pos: Pos): Expr {
|
private expr(kind: ExprKind, pos: Pos): Expr {
|
||||||
const id = this.nextNodeId;
|
return this.astCreator.expr(kind, pos);
|
||||||
this.nextNodeId += 1;
|
|
||||||
return { kind, pos, id };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private etype(kind: ETypeKind, pos: Pos): EType {
|
private etype(kind: ETypeKind, pos: Pos): EType {
|
||||||
const id = this.nextNodeId;
|
return this.astCreator.etype(kind, pos);
|
||||||
this.nextNodeId += 1;
|
|
||||||
return { kind, pos, id };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { Expr, Stmt } from "./ast.ts";
|
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 { printStackTrace, Reporter } from "./info.ts";
|
||||||
import {
|
import {
|
||||||
FnSyms,
|
FnSyms,
|
||||||
@ -115,6 +121,19 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
return "stop";
|
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) {
|
private reportUseOfUndefined(ident: string, pos: Pos, _syms: Syms) {
|
||||||
this.reporter.reportError({
|
this.reporter.reportError({
|
||||||
reporter: "Resolver",
|
reporter: "Resolver",
|
||||||
|
@ -7,7 +7,7 @@ if exists("b:current_syntax")
|
|||||||
finish
|
finish
|
||||||
endif
|
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 Special null
|
||||||
syn keyword Type int string bool
|
syn keyword Type int string bool
|
||||||
syn keyword Boolean true false
|
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 '-='
|
||||||
syn match Operator '=='
|
syn match Operator '=='
|
||||||
syn match Operator '!='
|
syn match Operator '!='
|
||||||
syn match Operator '<'
|
syn match Operator '<'
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||||
"name": "Slige",
|
"name": "Slige",
|
||||||
"patterns": [
|
"patterns": [
|
||||||
|
{ "include": "#comments" },
|
||||||
{ "include": "#keywords" },
|
{ "include": "#keywords" },
|
||||||
{ "include": "#strings" },
|
{ "include": "#strings" },
|
||||||
{ "include": "#numbers" },
|
{ "include": "#numbers" },
|
||||||
@ -12,88 +12,25 @@
|
|||||||
{ "include": "#idents" }
|
{ "include": "#idents" }
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
|
"comments": {
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "comment.line.slige",
|
||||||
|
"begin": "//",
|
||||||
|
"end": "\\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "comment.block.slige",
|
||||||
|
"begin": "/\\*",
|
||||||
|
"end": "\\*/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"keywords": {
|
"keywords": {
|
||||||
=======
|
|
||||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
|
||||||
"name": "Slige",
|
|
||||||
"patterns": [
|
|
||||||
{ "include": "#comments" },
|
|
||||||
{ "include": "#keywords" },
|
|
||||||
{ "include": "#strings" },
|
|
||||||
{ "include": "#numbers" },
|
|
||||||
{ "include": "#operators" },
|
|
||||||
{ "include": "#punctuation" },
|
|
||||||
{ "include": "#functions" },
|
|
||||||
{ "include": "#idents" }
|
|
||||||
],
|
|
||||||
"repository": {
|
|
||||||
"comments": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "comment.line.slige",
|
|
||||||
"begin": "//",
|
|
||||||
"end": "\\n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "comment.block.slige",
|
|
||||||
"begin": "/\\*",
|
|
||||||
"end": "\\*/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"keywords": {
|
|
||||||
"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": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"name": "keyword.control.slige",
|
"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",
|
"name": "constant.language.slige",
|
||||||
@ -139,7 +76,7 @@
|
|||||||
"operators": {
|
"operators": {
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"match": "\\+|\\-|\\*|\\/|=|(==)|(!=)|<|>|(<=)|(>=)|\\.|:|(\\->)|(::)|(::<)",
|
"match": "\\+|\\-|\\*|\\/|=|(+=)|(-=)|(==)|(!=)|<|>|(<=)|(>=)|\\.|:|(\\->)|(::)|(::<)",
|
||||||
"name": "keyword.operator.slige"
|
"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) {
|
if (index >= static_cast<int32_t>(this->values.size()) || index < 0) {
|
||||||
std::cout << std::format(
|
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);
|
this->values.size(), index);
|
||||||
exit(1);
|
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) {
|
if (index >= static_cast<int32_t>(this->value.length()) || index < 0) {
|
||||||
std::cout << std::format(
|
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);
|
this->value.length() - 1, index);
|
||||||
exit(1);
|
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