add sugar; special loop, compound assign

This commit is contained in:
sfja 2024-12-14 02:53:58 +01:00
parent b6c7f09f8b
commit 344214f1a4
18 changed files with 690 additions and 121 deletions

View File

@ -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 };
}
}

View File

@ -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));

View File

@ -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 {

View 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";
}
}
}
}

View 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";
}
}

View File

@ -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();

View File

@ -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));

View File

@ -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",
assignType,
subject,
value,
}, pos);
}
this.step();
const value = this.parseExpr();
return this.stmt({ type: "assign", 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);
}
}

View File

@ -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",

View File

@ -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 '<'

View File

@ -1,8 +1,8 @@
{
<<<<<<< HEAD
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "Slige",
"patterns": [
{ "include": "#comments" },
{ "include": "#keywords" },
{ "include": "#strings" },
{ "include": "#numbers" },
@ -12,88 +12,25 @@
{ "include": "#idents" }
],
"repository": {
"comments": {
"patterns": [
{
"name": "comment.line.slige",
"begin": "//",
"end": "\\n"
},
{
"name": "comment.block.slige",
"begin": "/\\*",
"end": "\\*/"
}
]
},
"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": [
{
"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
View File

@ -0,0 +1,8 @@
fn main() {
for (let i = 0; i < 10; i += 1) {
}
}

6
examples/increment.slg Normal file
View File

@ -0,0 +1,6 @@
fn main() {
let i = 0;
i += 1;
}

View 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
View File

@ -0,0 +1,8 @@
fn main() {
let i = 0;
while i < 10 {
i += 1;
}
}

View File

@ -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);
}

View File

@ -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
View 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())
}