Skip to content

Refactor bad code, build a testable specification and write a compiler guide #478

@ringabout

Description

@ringabout

The Neon Genesis Evolution Plan

北海虽赊,扶摇可接;东隅已逝,桑榆非晚。-- 滕王阁序
It is better late than never.

Introduction

Lacking a compiler documentation and a language specification with elaborate testable examples are the long-standing obstacles which hinder the Nim language from gaining the popularity it deserves. A compiler documentation gives developers the overview of the compiler internals and helps them step into the development of the Nim compiler. A language specification clarifies how the language works. Armed with detailed testable examples, it can prevent regressions from happening and act as a delicate guide to developers proving that the feature works and the language works.

The RFC is intended to be progressive and grows gradually. The task should start from commenting the compiler code. A testable specification can roll out at the same time. Finally the comments and the specification should help form a guide summarizing how the Nim compiler works.

The tasks are divided into three steps: comment the compiler code, build a testable specification and write a compiler guide.

Comment the compiler code

Compiler code without comments tends to be hard to understand. Comments help developers gain the insights of why the code is written like this and how it probably works. Comments could be obsolete and meaningless as time goes by. However, It is worthwhile updating the comments periodically since the code and comments will be read thousands of times.

The Nim CI should have a measure to calculate the comment ratio of the code. Ideally it should measure the comment ratio per pull request. The comment ratio is defined as comments lines / total lines (excluding empty lines).

  • add the comment ratio metric to the Nim CI

The contribution of the compiler documentation should be in the form of a pull request. The pull request should comment a certain module or functionality in the Nim compiler.

Build a testable specification

A specification should describe what should work or fail, with abundant testable examples. It should cover as many cases as possible.

As a convention, a describe block can be used. The implementation starts from

template describe*(x: string, body: typed) =
  block:
    body

It may be extended for better documentation generation in the future. The testable specification for the Nim manual should be put under the tests/spec/manual directory. A simple example is given below:

# manual_bool_type.nim
import stdtest/specutils

describe "The internal integer value of a Boolean type":
  describe "0 for false":
    doAssert ord(false) == 0
  describe "1 for true":
    doAssert ord(true) == 1

describe "A Boolean type is an Ordinal type":
  doAssert bool is Ordinal
  doAssert true is Ordinal
  doAssert false is Ordinal

describe "The size of a Boolean type is one byte":
  doAssert sizeof(bool) == 1
  doAssert sizeof(true) == 1
  doAssert sizeof(false) == 1

These examples should generate a pretty HTML documentation. They can be linked to the Nim manual or included into the Nim manual in the foldable form (defaults to folded).

Write a compiler guide

A compiler guide helps developers learn the internals of the Nim compiler without wasting time in reading the compiler code. What is a magic function? How the Nim method is implemented? Is it efficient to use & to concatenate multiple strings? These are questions that a compile guide should attempt to answer. The guide should focus on the explanation of the specific terms and the gist of the implementation.

For instance:

`$` function is a magic proc used to concatenate strings and chars. The Nim compiler does some optimizations to make it perform as good as the in-place version.

There are four overloads for `$` function in the system module.

```nim
proc `&`*(x: string, y: char): string {.magic: "ConStrStr", noSideEffect.}
proc `&`*(x, y: char): string {.magic: "ConStrStr", noSideEffect.}
proc `&`*(x, y: string): string {.magic: "ConStrStr", noSideEffect.}
proc `&`*(x: char, y: string): string {.magic: "ConStrStr", noSideEffect.}
```

All of them have the same magic `mConStrStr`, which is needed in the optimization phases. 

```nim
s = "Hello " & name & ", how do you feel" & 'z'
```

Here is the ast of the right-side expression:

```nim
StmtList
  Infix
    Ident "&"
    Infix
      Ident "&"
      Infix
        Ident "&"
        StrLit "Hello "
        Ident "name"
      StrLit ", how do you feel"
    CharLit 122
```

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions