mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 18:16:31 +00:00
parse generics
This commit is contained in:
parent
7944c76a6a
commit
bc82124601
@ -15,6 +15,7 @@ export type StmtKind =
|
|||||||
| {
|
| {
|
||||||
type: "fn";
|
type: "fn";
|
||||||
ident: string;
|
ident: string;
|
||||||
|
etypeParams?: ETypeParam[];
|
||||||
params: Param[];
|
params: Param[];
|
||||||
returnType?: EType;
|
returnType?: EType;
|
||||||
body: Expr;
|
body: Expr;
|
||||||
@ -42,7 +43,9 @@ export type ExprKind =
|
|||||||
| { type: "group"; expr: Expr }
|
| { type: "group"; expr: Expr }
|
||||||
| { type: "field"; subject: Expr; value: string }
|
| { type: "field"; subject: Expr; value: string }
|
||||||
| { type: "index"; subject: Expr; value: Expr }
|
| { type: "index"; subject: Expr; value: Expr }
|
||||||
| { type: "call"; subject: Expr; args: Expr[] }
|
| { type: "call"; subject: Expr; etypeArgs?: EType[]; args: Expr[] }
|
||||||
|
| { type: "path"; subject: Expr; value: string }
|
||||||
|
| { type: "etype_args"; subject: Expr; etypeArgs?: EType[] }
|
||||||
| { type: "unary"; unaryType: UnaryType; subject: Expr }
|
| { type: "unary"; unaryType: UnaryType; subject: Expr }
|
||||||
| { type: "binary"; binaryType: BinaryType; left: Expr; right: Expr }
|
| { type: "binary"; binaryType: BinaryType; left: Expr; right: Expr }
|
||||||
| { type: "if"; cond: Expr; truthy: Expr; falsy?: Expr; elsePos?: Pos }
|
| { type: "if"; cond: Expr; truthy: Expr; falsy?: Expr; elsePos?: Pos }
|
||||||
@ -98,7 +101,7 @@ export type SymKind =
|
|||||||
| { type: "fn"; stmt: Stmt }
|
| { type: "fn"; stmt: Stmt }
|
||||||
| { type: "fn_param"; param: Param }
|
| { type: "fn_param"; param: Param }
|
||||||
| { type: "closure"; inner: Sym }
|
| { type: "closure"; inner: Sym }
|
||||||
| { type: "builtin"; builtinId: number };
|
| { type: "generic"; etypeParam: ETypeParam };
|
||||||
|
|
||||||
export type EType = {
|
export type EType = {
|
||||||
kind: ETypeKind;
|
kind: ETypeKind;
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
BinaryType,
|
BinaryType,
|
||||||
EType,
|
EType,
|
||||||
ETypeKind,
|
ETypeKind,
|
||||||
|
ETypeParam,
|
||||||
Expr,
|
Expr,
|
||||||
ExprKind,
|
ExprKind,
|
||||||
Param,
|
Param,
|
||||||
@ -16,6 +17,8 @@ import { printStackTrace, Reporter } from "./info.ts";
|
|||||||
import { Lexer } from "./lexer.ts";
|
import { Lexer } from "./lexer.ts";
|
||||||
import { Pos, Token } from "./token.ts";
|
import { Pos, Token } from "./token.ts";
|
||||||
|
|
||||||
|
type Res<T> = { ok: true; value: T } | { ok: false };
|
||||||
|
|
||||||
export class Parser {
|
export class Parser {
|
||||||
private currentToken: Token | null;
|
private currentToken: Token | null;
|
||||||
|
|
||||||
@ -172,20 +175,28 @@ export class Parser {
|
|||||||
}
|
}
|
||||||
const ident = this.current().identValue!;
|
const ident = this.current().identValue!;
|
||||||
this.step();
|
this.step();
|
||||||
|
let etypeParams: ETypeParam[] | undefined;
|
||||||
|
if (this.test("<")) {
|
||||||
|
etypeParams = this.parseFnETypeParams();
|
||||||
|
}
|
||||||
if (!this.test("(")) {
|
if (!this.test("(")) {
|
||||||
this.report("expected '('");
|
this.report("expected '('");
|
||||||
return this.stmt({ type: "error" }, pos);
|
return this.stmt({ type: "error" }, pos);
|
||||||
}
|
}
|
||||||
const params = this.parseFnParams();
|
const params = this.parseFnParams();
|
||||||
let returnType: EType | null = null;
|
let returnType: EType | undefined;
|
||||||
if (this.test("->")) {
|
if (this.test("->")) {
|
||||||
this.step();
|
this.step();
|
||||||
returnType = this.parseEType();
|
returnType = this.parseEType();
|
||||||
}
|
}
|
||||||
|
|
||||||
let anno: Anno | null = null;
|
let anno: Anno | undefined;
|
||||||
if (this.test("#")) {
|
if (this.test("#")) {
|
||||||
anno = this.parseAnno();
|
const result = this.parseAnno();
|
||||||
|
if (!result.ok) {
|
||||||
|
return this.stmt({ type: "error" }, pos);
|
||||||
|
}
|
||||||
|
anno = result.value;
|
||||||
}
|
}
|
||||||
if (!this.test("{")) {
|
if (!this.test("{")) {
|
||||||
this.report("expected block");
|
this.report("expected block");
|
||||||
@ -196,10 +207,11 @@ export class Parser {
|
|||||||
{
|
{
|
||||||
type: "fn",
|
type: "fn",
|
||||||
ident,
|
ident,
|
||||||
|
etypeParams,
|
||||||
params,
|
params,
|
||||||
returnType: returnType !== null ? returnType : undefined,
|
returnType,
|
||||||
body,
|
body,
|
||||||
anno: anno != null ? anno : undefined,
|
anno,
|
||||||
},
|
},
|
||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
@ -231,60 +243,83 @@ export class Parser {
|
|||||||
return annoArgs;
|
return annoArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseAnno(): Anno | null {
|
private parseAnno(): Res<Anno> {
|
||||||
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 null;
|
return { ok: false };
|
||||||
}
|
}
|
||||||
this.step();
|
this.step();
|
||||||
if (!this.test("ident")) {
|
if (!this.test("ident")) {
|
||||||
this.report("expected identifier");
|
this.report("expected identifier");
|
||||||
return null;
|
return { ok: false };
|
||||||
}
|
}
|
||||||
const ident = this.current().identValue!;
|
const ident = this.current().identValue!;
|
||||||
const values = this.parseAnnoArgs();
|
const values = this.parseAnnoArgs();
|
||||||
if (!this.test("]")) {
|
if (!this.test("]")) {
|
||||||
this.report("expected ']'");
|
this.report("expected ']'");
|
||||||
return null;
|
return { ok: false };
|
||||||
}
|
}
|
||||||
this.step();
|
this.step();
|
||||||
return { ident, pos, values };
|
return { ok: true, value: { ident, pos, values } };
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseFnETypeParams(): ETypeParam[] {
|
||||||
|
return this.parseDelimitedList(this.parseETypeParam, ">", ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseETypeParam(): Res<ETypeParam> {
|
||||||
|
const pos = this.pos();
|
||||||
|
if (this.test("ident")) {
|
||||||
|
const ident = this.current().identValue!;
|
||||||
|
this.step();
|
||||||
|
return { ok: true, value: { ident, pos } };
|
||||||
|
}
|
||||||
|
this.report("expected generic parameter");
|
||||||
|
return { ok: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseFnParams(): Param[] {
|
private parseFnParams(): Param[] {
|
||||||
this.step();
|
return this.parseDelimitedList(this.parseParam, ")", ",");
|
||||||
if (this.test(")")) {
|
|
||||||
this.step();
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const params: Param[] = [];
|
|
||||||
const paramResult = this.parseParam();
|
|
||||||
if (!paramResult.ok) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
params.push(paramResult.value);
|
|
||||||
while (this.test(",")) {
|
|
||||||
this.step();
|
|
||||||
if (this.test(")")) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const paramResult = this.parseParam();
|
|
||||||
if (!paramResult.ok) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
params.push(paramResult.value);
|
|
||||||
}
|
|
||||||
if (!this.test(")")) {
|
|
||||||
this.report("expected ')'");
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
this.step();
|
|
||||||
return params;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseParam(): { ok: true; value: Param } | { ok: false } {
|
private parseDelimitedList<T>(
|
||||||
|
parseElem: (this: Parser) => Res<T>,
|
||||||
|
endToken: string,
|
||||||
|
delimiter: string,
|
||||||
|
): T[] {
|
||||||
|
this.step();
|
||||||
|
if (this.test(endToken)) {
|
||||||
|
this.step();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const elems: T[] = [];
|
||||||
|
const elemRes = parseElem.call(this);
|
||||||
|
if (!elemRes.ok) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
elems.push(elemRes.value);
|
||||||
|
while (this.test(delimiter)) {
|
||||||
|
this.step();
|
||||||
|
if (this.test(endToken)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const elemRes = parseElem.call(this);
|
||||||
|
if (!elemRes.ok) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
elems.push(elemRes.value);
|
||||||
|
}
|
||||||
|
if (!this.test(endToken)) {
|
||||||
|
this.report(`expected '${endToken}'`);
|
||||||
|
return elems;
|
||||||
|
}
|
||||||
|
this.step();
|
||||||
|
return elems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseParam(): Res<Param> {
|
||||||
const pos = this.pos();
|
const pos = this.pos();
|
||||||
if (this.test("ident")) {
|
if (this.test("ident")) {
|
||||||
const ident = this.current().identValue!;
|
const ident = this.current().identValue!;
|
||||||
@ -616,24 +651,47 @@ export class Parser {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (this.test("(")) {
|
if (this.test("(")) {
|
||||||
|
const args = this.parseDelimitedList(
|
||||||
|
this.parseExprArg,
|
||||||
|
")",
|
||||||
|
",",
|
||||||
|
);
|
||||||
|
subject = this.expr({ type: "call", subject, args }, pos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (this.test("::")) {
|
||||||
this.step();
|
this.step();
|
||||||
let args: Expr[] = [];
|
if (!this.test("ident")) {
|
||||||
if (!this.test(")")) {
|
this.report("expected ident");
|
||||||
args.push(this.parseExpr());
|
|
||||||
while (this.test(",")) {
|
|
||||||
this.step();
|
|
||||||
if (this.test(")")) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
args.push(this.parseExpr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this.test(")")) {
|
|
||||||
this.report("expected ')'");
|
|
||||||
return this.expr({ type: "error" }, pos);
|
return this.expr({ type: "error" }, pos);
|
||||||
}
|
}
|
||||||
|
const value = this.current().identValue!;
|
||||||
this.step();
|
this.step();
|
||||||
subject = this.expr({ type: "call", subject, args }, pos);
|
subject = this.expr({ type: "path", subject, value }, pos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (this.test("::<")) {
|
||||||
|
const etypeArgs = this.parseDelimitedList(
|
||||||
|
this.parseETypeArg,
|
||||||
|
">",
|
||||||
|
",",
|
||||||
|
);
|
||||||
|
if (this.test("(")) {
|
||||||
|
subject = this.expr(
|
||||||
|
{ type: "etype_args", subject, etypeArgs },
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const args = this.parseDelimitedList(
|
||||||
|
this.parseExprArg,
|
||||||
|
")",
|
||||||
|
",",
|
||||||
|
);
|
||||||
|
subject = this.expr(
|
||||||
|
{ type: "call", subject, etypeArgs, args },
|
||||||
|
pos,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -641,6 +699,14 @@ export class Parser {
|
|||||||
return subject;
|
return subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private parseExprArg(): Res<Expr> {
|
||||||
|
return { ok: true, value: this.parseExpr() };
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseETypeArg(): Res<EType> {
|
||||||
|
return { ok: true, value: this.parseEType() };
|
||||||
|
}
|
||||||
|
|
||||||
private parseOperand(): Expr {
|
private parseOperand(): Expr {
|
||||||
const pos = this.pos();
|
const pos = this.pos();
|
||||||
if (this.test("ident")) {
|
if (this.test("ident")) {
|
||||||
@ -690,7 +756,7 @@ export class Parser {
|
|||||||
return this.parseLoop();
|
return this.parseLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.report("expected expr", pos);
|
this.report(`expected expr, got '${this.current().type}'`, pos);
|
||||||
this.step();
|
this.step();
|
||||||
return this.expr({ type: "error" }, pos);
|
return this.expr({ type: "error" }, pos);
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,18 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
throw new Error("expected fn statement");
|
throw new Error("expected fn statement");
|
||||||
}
|
}
|
||||||
const fnScopeSyms = new FnSyms(syms);
|
const fnScopeSyms = new FnSyms(syms);
|
||||||
|
for (const param of stmt.kind.etypeParams ?? []) {
|
||||||
|
if (fnScopeSyms.definedLocally(param.ident)) {
|
||||||
|
this.reportAlreadyDefined(param.ident, param.pos, syms);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fnScopeSyms.define(param.ident, {
|
||||||
|
ident: param.ident,
|
||||||
|
type: "generic",
|
||||||
|
pos: param.pos,
|
||||||
|
etypeParam: param,
|
||||||
|
});
|
||||||
|
}
|
||||||
for (const param of stmt.kind.params) {
|
for (const param of stmt.kind.params) {
|
||||||
if (fnScopeSyms.definedLocally(param.ident)) {
|
if (fnScopeSyms.definedLocally(param.ident)) {
|
||||||
this.reportAlreadyDefined(param.ident, param.pos, syms);
|
this.reportAlreadyDefined(param.ident, param.pos, syms);
|
||||||
|
@ -56,12 +56,50 @@ fn input(prompt: string) -> string {
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
fn is_prime(n: int) -> bool {
|
fn min(a: int, b: int) -> int {
|
||||||
if n == 1 or n == 0{
|
if b < a { b } else { a }
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 2; i < n; i += 1) {
|
fn max(a: int, b: int) -> int {
|
||||||
|
if a < b { b } else { b }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqrt(n: int) -> int {
|
||||||
|
let low = min(1, n);
|
||||||
|
let high = max(1, n);
|
||||||
|
let mid = 0;
|
||||||
|
|
||||||
|
while 100 * low * low < n {
|
||||||
|
low = low * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (high * high) / 100 > n {
|
||||||
|
high = high / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 100; i += 1) {
|
||||||
|
mid = (low + high) / 2;
|
||||||
|
if mid * mid == n {
|
||||||
|
return mid;
|
||||||
|
}
|
||||||
|
if mid * mid > n {
|
||||||
|
high = mid;
|
||||||
|
} else {
|
||||||
|
low = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mid
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_prime(n: int) -> bool {
|
||||||
|
if n == 0{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if n == 1 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let n_root = sqrt(n);
|
||||||
|
for (let i = 2; i < n_root; i += 1) {
|
||||||
if remainder(n, i) == 0 {
|
if remainder(n, i) == 0 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -70,7 +108,7 @@ fn is_prime(n: int) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
for (let i = 1; i < 10000; i += 1) {
|
for (let i = 1; i <= 10000; i += 1) {
|
||||||
if is_prime(i) {
|
if is_prime(i) {
|
||||||
print(int_to_string(i) + " ");
|
print(int_to_string(i) + " ");
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,13 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo Text:
|
echo Text:
|
||||||
|
|
||||||
|
if command -v pygmentize 2>&1 >/dev/null
|
||||||
|
then
|
||||||
|
pygmentize -l rust -Ostyle="gruvbox-dark",linenos=1 $1
|
||||||
|
else
|
||||||
cat $1
|
cat $1
|
||||||
|
fi
|
||||||
|
|
||||||
echo Compiling $1...
|
echo Compiling $1...
|
||||||
|
|
||||||
|
17
tests/generics.slg
Normal file
17
tests/generics.slg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
fn exit(status_code: int) #[builtin(Exit)] {}
|
||||||
|
|
||||||
|
fn id<T>(v: T) -> T {
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if id::<int>(123) != 123 {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if id::<bool>(true) != true {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user