Upload project files

This commit is contained in:
wangdequan
2026-06-28 08:20:33 +08:00
parent f0e96308d2
commit 84a5b54195
162 changed files with 14070 additions and 31381 deletions

View File

@@ -10,6 +10,9 @@ import type {
WaitInstruction
} from "../ir/index.js";
import { lexGrl, type GrlToken } from "../lexer/index.js";
import { parseGrlExpression } from "../parser/index.js";
import { evaluateNumberExpression } from "./constantExpression.js";
import type { GrlNumberLiteral } from "../ast/index.js";
export interface IoMap {
aliases?: Record<string, IoReference>;
@@ -185,7 +188,7 @@ class IoStatementParser {
return token.raw === "true";
}
if (token.kind === "number") {
return normalizedNumber(token);
return evaluateNumberExpression(numberLiteralFromToken(token));
}
if (token.kind === "string") {
return token.value;
@@ -197,8 +200,40 @@ class IoStatementParser {
}
private parseDuration(): number {
const token = this.consumeNumber("Expected duration");
return normalizedNumber(token);
const tokens = this.collectDurationExpressionTokens();
return evaluateNumberExpression(parseGrlExpression(tokens), { expectedKind: "time", defaultUnit: "s" });
}
private collectDurationExpressionTokens(): GrlToken[] {
const tokens: GrlToken[] = [];
let parenDepth = 0;
let bracketDepth = 0;
while (!this.isAtEnd()) {
const token = this.peek();
if (
tokens.length > 0 &&
parenDepth === 0 &&
bracketDepth === 0 &&
(this.isKeywordLike(token, "on_timeout") || this.isCurrentStatementStartAfter(tokens))
) {
break;
}
const consumed = this.advance();
tokens.push(consumed);
if (consumed.kind === "punctuation" && consumed.raw === "(") {
parenDepth += 1;
} else if (consumed.kind === "punctuation" && consumed.raw === ")") {
parenDepth = Math.max(0, parenDepth - 1);
} else if (consumed.kind === "punctuation" && consumed.raw === "[") {
bracketDepth += 1;
} else if (consumed.kind === "punctuation" && consumed.raw === "]") {
bracketDepth = Math.max(0, bracketDepth - 1);
}
}
if (tokens.length === 0) {
throw ioError("GRL_TOKEN_EXPECTED", "Expected duration");
}
return tokens;
}
private collectUntilKeyword(keywords: string[]): GrlToken[] {
@@ -242,20 +277,63 @@ class IoStatementParser {
private isCurrentStatementStartAfter(tokens: GrlToken[]): boolean {
const token = this.peek();
if (this.isKeywordLike(token, "wait") || this.isKeywordLike(token, "pulse")) {
const previous = tokens.at(-1);
if (!previous || token.range.start.line <= previous.range.end.line) {
return false;
}
if (this.isStatementStart(token)) {
return true;
}
if (!this.isIoStartAtCurrent()) {
return false;
}
const previous = tokens.at(-1);
return previous ? token.range.start.line > previous.range.end.line : true;
return true;
}
private isIoStartAtCurrent(): boolean {
return this.peek().raw === "io" && this.maybePeek(1)?.raw === ".";
}
private isStatementStart(token: GrlToken): boolean {
return (
(token.kind === "keyword" || token.kind === "identifier") &&
[
"io",
"wait",
"pulse",
"movej",
"movel",
"movec",
"set_tool",
"set_frame",
"set_speed",
"set_zone",
"run_path",
"run_operation",
"if",
"elseif",
"else",
"while",
"for",
"switch",
"case",
"default",
"break",
"continue",
"label",
"jump",
"call",
"return",
"alarm",
"raise",
"try",
"catch",
"finally",
"end"
].includes(token.raw)
);
}
private isKeywordLike(token: GrlToken, keyword: string): boolean {
return (token.kind === "keyword" || token.kind === "identifier") && token.raw === keyword;
}
@@ -361,13 +439,6 @@ function compileStatementString(
return parseIoFlowStatements(tokens, ioMap);
}
function normalizedNumber(token: GrlToken): number {
if (token.kind !== "number") {
throw ioError("GRL_NUMBER_EXPECTED", "Expected number");
}
return token.unit?.normalizedValue ?? token.value;
}
function tokenSourceMap(token: GrlToken): MotionSourceMap {
return {
line: token.range.start.line,
@@ -375,6 +446,25 @@ function tokenSourceMap(token: GrlToken): MotionSourceMap {
};
}
function numberLiteralFromToken(token: Extract<GrlToken, { kind: "number" }>): GrlNumberLiteral {
return {
kind: "NumberLiteral",
value: token.value,
raw: token.raw,
...(token.unit
? {
unit: {
raw: token.unit.raw,
kind: token.unit.kind,
siUnit: token.unit.siUnit,
normalizedValue: token.unit.normalizedValue
}
}
: {}),
range: token.range
};
}
function ioError(code: string, message: string): KdlStructuredError {
return new KdlStructuredError(code, message);
}