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

@ -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";
@ -124,7 +123,7 @@ export class Checker {
if (isBuiltin) {
return;
}
const { returnType } = stmt.kind.vtype!;
this.fnReturnStack.push(returnType);
const body = this.checkExpr(stmt.kind.body);
@ -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 {

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

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

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

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

6
examples/increment.slg Normal file

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

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

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

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