Skip to content

Commit

Permalink
Support INPUTting expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
BalaM314 committed Nov 1, 2024
1 parent 0a8d985 commit e36dc09
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 7 deletions.
6 changes: 3 additions & 3 deletions core/src/statements/statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ export class Statement implements TextRanged, IFormattable {
/** Returns the node at `ind` and asserts that it is a type. */
expr(ind:number, allowed:"type", error?:string):ExpressionASTTypeNode;
/** Returns the node at `ind` and asserts that it is one of the given types. */
expr<Type extends new (...args:any[]) => {}>(ind:number, allowed:Type[], error?:string):InstanceType<Type>;
expr(ind:number, allowed:"expr" | "type" | readonly (new (...args:any[]) => {})[] = "expr", error?:string):ExpressionASTNodeExt {
expr<Type extends new (...args:any[]) => {}>(ind:number, allowed:Type[], error?:string, extraValidator?:(node:ExpressionASTNodeExt) => boolean):InstanceType<Type>;
expr(ind:number, allowed:"expr" | "type" | readonly (new (...args:any[]) => {})[] = "expr", error?:string, extraValidator:(node:ExpressionASTNodeExt) => boolean = () => true):ExpressionASTNodeExt {
if(allowed === "type") allowed = ExpressionASTTypeNodes;
if(allowed === "expr") allowed = ExpressionASTNodes;

if(allowed.some(c => this.nodes.at(ind) instanceof c))
if(allowed.find(c => this.nodes.at(ind) instanceof c) && extraValidator(this.nodes.at(ind)!))
return this.nodes.at(ind) as ExpressionAST;

if(error != undefined) fail(error, this.nodes.at(ind));
Expand Down
16 changes: 12 additions & 4 deletions core/src/statements/statements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This file contains the definitions for every statement type supported by Soodoco

import { configs } from "../config/index.js";
import { Token, TokenType } from "../lexer/index.js";
import { ExpressionAST, ExpressionASTFunctionCallNode, ExpressionASTNodeExt, ExpressionASTTypeNode, getUniqueNamesFromCommaSeparatedTokenList, isLiteral, literalTypes, parseExpression, parseFunctionArguments, processTypeData, ProgramASTBranchNode, ProgramASTBranchNodeType, ProgramASTNodeGroup, splitTokensOnComma } from "../parser/index.js";
import { ExpressionAST, ExpressionASTArrayAccessNode, ExpressionASTBranchNode, ExpressionASTFunctionCallNode, ExpressionASTNodeExt, ExpressionASTTypeNode, getUniqueNamesFromCommaSeparatedTokenList, isLiteral, literalTypes, parseExpression, parseFunctionArguments, processTypeData, ProgramASTBranchNode, ProgramASTBranchNodeType, ProgramASTNodeGroup, splitTokensOnComma } from "../parser/index.js";
import { ClassMethodData, ClassVariableType, ConstantData, EnumeratedVariableType, FileMode, FunctionData, PointerVariableType, PrimitiveVariableType, RecordVariableType, SetVariableType, TypedNodeValue, UnresolvedVariableType, UntypedNodeValue, VariableScope, VariableType, VariableValue } from "../runtime/runtime-types.js";
import { Runtime } from "../runtime/runtime.js";
import { combineClasses, crash, enableConfig, f, fail, getTotalRange, RangeArray } from "../utils/funcs.js";
Expand Down Expand Up @@ -198,11 +198,19 @@ export class OutputStatement extends Statement {
runtime._output(this.outMessage.map(expr => runtime.evaluateUntyped(expr)));
}
}
@statement("input", "INPUT y", "keyword.input", "name")
@statement("input", "INPUT y", "keyword.input", "expr+")
export class InputStatement extends Statement {
name = this.token(1).text;
name = this.expr(1,
[Token, ExpressionASTArrayAccessNode, ExpressionASTBranchNode],
`Invalid INPUT target: must be a single variable name, an array access expression, or a property access expression`,
node => {
if(node instanceof Token) return node.type == "name";
if(node instanceof ExpressionASTBranchNode) return node.operator.id == "operator.access";
return true;
}
);
run(runtime:Runtime){
const variable = runtime.getVariable(this.name) ?? runtime.handleNonexistentVariable(this.name, this.nodes[1].range);
const variable = runtime.evaluateExpr(this.name, "variable");
if(!variable.mutable) fail(f.quote`Cannot INPUT ${this.name} because it is immutable`, this.nodes[1], this);
const input = runtime._input(f.text`Enter the value for variable "${this.name}" (type: ${variable.type})`, variable.type);
switch(variable.type){
Expand Down
34 changes: 34 additions & 0 deletions spec/src/full.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,40 @@ OUTPUT a >= b`,
],
//#endregion
//#region statements
input_variable: [
`DECLARE hello: STRING
INPUT hello`,
[],
["input"]
],
input_array_slot: [
`FUNCTION foo() RETURNS INTEGER
RETURN 3 + 4;
ENDFUNCTION
DECLARE hello: ARRAY[1:10] OF STRING
INPUT hello[foo() - 2]
OUTPUT hello[5]`,
["input"],
["input"]
],
input_record_slot: [
`TYPE struct
DECLARE field: STRING
ENDTYPE
DECLARE hello: ARRAY[1:10] OF struct
INPUT hello[4].field
OUTPUT hello[4].field`,
["input"],
["input"]
],
input_invalid_1: [
`INPUT 2 + 2`,
``
],
input_invalid_2: [
`INPUT TIME(0)`,
``
],
if_normal: [
`IF FALSE THEN
ENDIF`,
Expand Down

0 comments on commit e36dc09

Please sign in to comment.