From d20cc677bb623e9b20e95131c2de511ecdaac43f Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Thu, 18 Feb 2021 00:38:30 +0100 Subject: [PATCH] The typeof builtin function --- .../compiler/AbstractExpressionCompiler.scala | 1 + .../AbstractStatementPreprocessor.scala | 2 +- .../m6809/M6809ExpressionCompiler.scala | 6 +++ .../compiler/z80/Z80ExpressionCompiler.scala | 3 ++ src/main/scala/millfork/env/Environment.scala | 49 +++++++++++++++++-- 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala index e4c8b0fd..38b3e7f8 100644 --- a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala @@ -495,6 +495,7 @@ object AbstractExpressionCompiler { case 1 => b case 2 => w } + case FunctionCallExpression("typeof", params) => w case FunctionCallExpression("%%", params) => params.map { e => getExpressionTypeImpl(env, log, e, loosely).size } match { case List(1, 1) | List(2, 1) => b case List(1, 2) | List(2, 2) => w diff --git a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala index 137cf51f..c9619e44 100644 --- a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala +++ b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala @@ -738,7 +738,7 @@ object AbstractStatementPreprocessor { "<<", "<<'", ">>", ">>'", ">>>>", "&", "&&", "||", "|", "^", "==", "!=", "<", ">", ">=", "<=", - "not", "hi", "lo", "nonet", "sizeof" + "not", "hi", "lo", "nonet", "sizeof", "typeof" ) def mightBeMemset(ctx: CompilationContext, f: ForStatement): Boolean = { diff --git a/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala b/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala index 314a70c7..35059b03 100644 --- a/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala @@ -187,6 +187,12 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { } else h ++ l) ++ targetifyD(ctx, target) case fce@FunctionCallExpression(functionName, params) => functionName match { + case "sizeof" => + ctx.log.fatal("Unreachable branch: 6809 sizeof") + Nil + case "typeof" => + ctx.log.fatal("Unreachable branch: 6809 typeof") + Nil case "not" => assertBool(ctx, "not", params, 1) compile(ctx, params.head, target, branches.flip) diff --git a/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala b/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala index 1b4831c3..fcf81759 100644 --- a/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala @@ -826,6 +826,9 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { case "sizeof" => ctx.log.fatal("Unreachable branch: 8080 sizeof") Nil + case "typeof" => + ctx.log.fatal("Unreachable branch: 8080 typeof") + Nil case "nonet" => if (params.length != 1) { ctx.log.error("Invalid number of parameters", f.position) diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index 0f77acbc..e576d92c 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -671,6 +671,30 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa NumericConstant(size, Constant.minimumSize(size)) } + def evalTypeof(expr: Expression): Constant = { + val size: Int = expr match { + case VariableExpression(name) => + maybeGet[Thing](name) match { + case None => + log.error(s"`$name` is not defined", expr.position) + hintTypo(name) + 1 + case Some(thing) => thing match { + case t: Type => t.name.hashCode & 0xffff + case v: Variable => v.typ.name.hashCode & 0xffff + case a: MfArray => a.elementType.name.hashCode & 0xffff + case ConstantThing(_, MemoryAddressConstant(a: MfArray), _) => a.elementType.name.hashCode & 0xffff + case x => + log.error("Invalid parameter for expr: " + name) + 1 + } + } + case _ => + AbstractExpressionCompiler.getExpressionType(this, log, expr).alignedSize + } + NumericConstant(size, Constant.minimumSize(size)) + } + def eval(e: Expression, vars: Map[String, Constant]): Option[Constant] = evalImpl(e, Some(vars)) def eval(e: Expression): Option[Constant] = { @@ -739,6 +763,13 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa log.error("Invalid number of parameters for `sizeof`", e.position) Some(Constant.One) } + case "typeof" => + if (params.size == 1) { + Some(evalTypeof(params.head)) + } else { + log.error("Invalid number of parameters for `typeof`", e.position) + Some(Constant.Zero) + } case "hi" => if (params.size == 1) { evalImpl(params.head, vv).map(_.hiByte.quickSimplify) @@ -1021,7 +1052,19 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa if (!silent) log.error("Function not supported in inline assembly", e.position) None case "sizeof" => - Some(evalSizeof(params.head)) + if (params.size == 1) { + Some(evalSizeof(params.head)) + } else { + log.error("Invalid number of parameters for `sizeof`", e.position) + Some(Constant.One) + } + case "typeof" => + if (params.size == 1) { + Some(evalTypeof(params.head)) + } else { + log.error("Invalid number of parameters for `typeof`", e.position) + Some(Constant.Zero) + } case _ => None } @@ -2650,7 +2693,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa nameCheck(l) case SumExpression(params, _) => nameCheck(params.map(_._2)) - case FunctionCallExpression("sizeof", List(ve@VariableExpression(e))) => + case FunctionCallExpression("sizeof" | "typeof", List(ve@VariableExpression(e))) => checkName[Thing]("Type, variable or constant", e, ve.position) case FunctionCallExpression(name, params) => if (name.exists(_.isLetter) && !Environment.predefinedFunctions(name)) { @@ -2753,7 +2796,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa object Environment { // built-in special-cased functions; can be considered keywords by some: - val predefinedFunctions: Set[String] = Set("not", "hi", "lo", "nonet", "sizeof") + val predefinedFunctions: Set[String] = Set("not", "hi", "lo", "nonet", "sizeof", "typeof") // built-in special-cased functions, not keywords, but assumed to work almost as such: val specialFunctions: Set[String] = Set("call") // functions that exist only in constants: