From c61d0442267614503da898758369138818168dc9 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Tue, 8 Oct 2019 01:33:55 +0200 Subject: [PATCH] Document keywords and handle them better --- docs/README.md | 2 + docs/doc_index.md | 2 + docs/lang/keywords.md | 113 ++++++++++++++++++ mkdocs.yml | 1 + src/main/scala/millfork/env/Environment.scala | 37 ++++-- src/main/scala/millfork/parser/MfParser.scala | 3 +- 6 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 docs/lang/keywords.md diff --git a/docs/README.md b/docs/README.md index 504ddf94..b3ff47dc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -36,6 +36,8 @@ * [Important guidelines regarding reentrancy](lang/reentrancy.md) +* [List of keywords](lang/keywords.md) + ## Library reference * [`stdlib` module](stdlib/stdlib.md) diff --git a/docs/doc_index.md b/docs/doc_index.md index a526e899..fa02154a 100644 --- a/docs/doc_index.md +++ b/docs/doc_index.md @@ -36,6 +36,8 @@ * [Important guidelines regarding reentrancy](lang/reentrancy.md) +* [List of keywords](lang/keywords.md) + ## Library reference * [`stdlib` module](stdlib/stdlib.md) diff --git a/docs/lang/keywords.md b/docs/lang/keywords.md new file mode 100644 index 00000000..7899e94d --- /dev/null +++ b/docs/lang/keywords.md @@ -0,0 +1,113 @@ +[< back to index](../doc_index.md) + +# List of keywords + +## Normal keywords + +### Top level keywords + +These keywords can occur at the top level of the file: + + import segment + array struct union enum alias + +### Declaration modifiers + +These keywords occur within variable, array and function declarations: + +* Variable declaration modifiers: `const volatile register static stack` + +* Function declaration modifiers: `asm inline noinline interrupt kernal_interrupt macro reentrant extern` + +* Array declaration modifiers: `const` + +* Common modifiers: `segment align` + +### Statement keywords + +These keywords occur within function bodies: + + if else for while do + goto label + asm + return + +### Sub-statement keywords + +These keywords occur within bodies of certain other statements: + +* for-loop directions: `to until downto parallelto parallel until` + +* special return-dispatch branch: `default` + +* loop control flow: `break continue` + +## Contextual keywords + +These are not considered keywords, but have special meaning in certain syntactical locations: + +* special alignment constant: `fast` + +* special array function: `file` + +* text encoding names: `z defaultz scr scrz`; for the rest, see [the list of text encodings](./text.md) +`default` is also a name of a text encoding, but it's also a keyword + +## Reserved identifiers + +Most of reserved identifiers are considered keywords (i.e. you cannot override them, even locally). +Some are not, but overriding them is strongly discouraged and for highest code clarity should also be considered keywords. + +The keyword status of those identifiers may change in the future. + +### Built-in types + +The following identifiers are considered keywords: + + byte ubyte sbyte word long + pointer void bool + set_carry set_zero set_overflow set_negative + clear_carry clear_zero clear_overflow clear_negative + int16 int24 int32 int40 int 48 int56 int64 int72 int80 int88 int96 int104 int112 int120 int128 + unsigned8 signed8 + +`farword` is a deprecated alias for `int24` and it is not a keyword. It will be removed in the future. + +### Built-in constants + +The following identifiers are considered keywords: + + true false + +The following identifiers are not considered keywords: + + nullptr nullchar + +### Built-in functions and operators + +The following identifiers are considered keywords: + + not hi lo nonet sizeof + +The following identifiers are not considered keywords: + + sin cos tan call + +## Reserved field names + +It is not allowed to define a struct or a union with a field with any of the following names: + + pointer addr rawaddr return + +## Preprocesor keywords + +### Directives + + #if #elseif #else #endif + #define #use + #pragma + #fatal #error #warn #info #infoeval + +### Built-in functions + + not hi lo defined if \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index e68b377b..1b402ba9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,6 +30,7 @@ nav: - Inline 6502 assembly: lang/assembly.md - Inline 8080/LR35902/Z80 assembly: lang/assemblyz80.md - Reentrancy guidelines: lang/reentrancy.md + - List of keywords: lang/keywords.md - Library reference: - stdlib module: stdlib/stdlib.md - string module: stdlib/string.md diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index b6038990..19f98d98 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -505,8 +505,14 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } def assertNotDefined(name: String, position: Option[Position]): Boolean = { - if (builtinsAdded && Environment.keywords(name)) { - log.error(s"Cannot redefine a builtin keyword `$name`", position) + if (builtinsAdded && Environment.invalidNewIdentifiers(name)) { + if (Environment.predefinedFunctions(name)) { + log.error(s"Cannot redefine a builtin predefined function `$name`", position) + } else if (Environment.neverIdentifiers(name)) { + log.error(s"Cannot the keyword `$name` as a name", position) + } else if (Environment.invalidNewIdentifiers(name)) { + log.error(s"Cannot redefine a builtin predefined identifier `$name`", position) + } false } else if (things.contains(name) || parent.exists(_.things.contains(name)) || things.contains(name.stripPrefix(prefix))) { if (!name.contains('.')){ @@ -2120,13 +2126,30 @@ 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 keywords: Set[String] = Set( - "true", "false", - "byte", "sbyte", "word", "pointer", "void", "long", - "array", "const", "alias", "import", "static", "register", "stack", "volatile", "asm", "extern", "kernal_interrupt", "interrupt", - "for", "if", "do", "while", "else", "return", "default", "to", "until", "paralleluntil", "parallelto", "downto", + // built-in special-cased functions, not keywords, but assumed to work almost as such: + val specialFunctions: Set[String] = Set("sin", "cos", "tan", "call") + // keywords: + val neverIdentifiers: Set[String] = Set( + "array", "const", "alias", "import", "static", "register", "stack", "volatile", "asm", "extern", "kernal_interrupt", "interrupt", "reentrant", "segment", + "struct", "union", "enum", + "for", "if", "do", "while", "else", "return", "default", + "to", "until", "paralleluntil", "parallelto", "downto", + "break", "continue", "inline", "noinline" ) ++ predefinedFunctions + // predefined identifiers that cannot be overridden and do not name a type: + val neverValidTypeIdentifiers: Set[String] = Set( + "true", "false", + ) ++ neverIdentifiers + // predefined type identifiers: + val invalidNewIdentifiers: Set[String] = Set( + "byte", "sbyte", "word", "pointer", "void", "long", "bool", + "set_carry", "set_zero", "set_overflow", "set_negative", + "clear_carry", "clear_zero", "clear_overflow", "clear_negative", + "int8", "int16", "int24", "int32", "int40", "int48", "int56", "int64", "int72", "int80", "int88", "int96", "int104", "int112", "int120", "int128", + "signed8", "unsigned16") ++ neverValidTypeIdentifiers + // built-in special-cased field names; can be considered keywords by some: val invalidFieldNames: Set[String] = Set("addr", "rawaddr", "pointer", "return") } diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index 26bd8465..72594843 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -486,7 +486,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri bank <- bankDeclaration flags <- functionFlags ~ HWS returnType <- identifier ~ SWS - if !InvalidReturnTypes(returnType) + if !Environment.neverValidTypeIdentifiers(returnType) name <- identifier ~ HWS params <- "(" ~/ AWS ~/ (if (flags("asm")) asmParamDefinition else paramDefinition).rep(sep = AWS ~ "," ~/ AWS) ~ AWS ~ ")" ~/ AWS alignment <- alignmentDeclaration(fastAlignmentForFunctions).? ~/ AWS @@ -735,5 +735,4 @@ object MfParser { val functionFlags: P[Set[String]] = flags_("asm", "inline", "interrupt", "macro", "noinline", "reentrant", "kernal_interrupt") - val InvalidReturnTypes: Set[String] = Set("enum", "alias", "array", "const", "stack", "register", "static", "volatile", "import", "struct", "union") }