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

View File

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

View File

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

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

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

View File

@ -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(); this.step();
const value = this.parseExpr(); 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 { 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 };
} }
} }

View File

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

View File

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

View File

@ -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", "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "Slige", "name": "Slige",
"patterns": [ "patterns": [
@ -45,55 +30,7 @@
"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",
"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"
}, },
{ {
"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
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) { 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);
} }

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