Upload project files
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user