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