{
:skip_whitespace
:token
:validate_bytes
}
Where
Define (token text i)
Cond {
| [i = (STRING.length text)] {i 'eof}
| True
Let c (STRING.fetch text i)
In
Cond {
| [c = `"`] (scan_string text [i + 1])
| [c = ```] (scan_character text [i + 1])
| (ASCII.is_digit c) (scan_number text i)
| (ASCII.is_letter c) (scan_word text i)
| (is_op_symbol c) (scan_operator text i)
| True (scan_basic_symbol text i)
}
}
Define (skip_whitespace text i)
Let m (STRING.length text)
In
Iterate i
Cond {
| [i = m] i
| True
Let i (skip_spaces text i)
In
Let c (STRING.fetch text i)
In
Cond {
| [c = `\n`] (Continue [i + 1])
| [c = `;`] (Continue (skip_line text [i + 1]))
| True i
}
}
Define (validate_bytes text)
Let m (STRING.length text)
In
If [m = 0]
'succeed
Iterate i From 0
Cond {
| [i = m]
If [(STRING.fetch text [m - 1]) = `\n`]
'succeed
'fail."Source text encoding: no terminating newline character."
| True
Let c (STRING.fetch text i)
In
If (Or (ASCII.is_visible c) [c = ` `] [c = `\n`])
(Continue [i + 1])
'fail.[
STDIO.sprintf
<- "Source text encoding: forbidden byte (%d)." <- c
]
}
Where
Define (scan_string text i)
Let begin i
In
Iterate {i mode} From {i 'default}
Let c (STRING.fetch text i)
In
If [c = `\n`]
{i 'error."Incomplete string literal."}
Match mode {
| 'default
Cond {
| [c = `\\`]
(Continue [i + 1] 'escaped)
| [c = `"`]
Let end i
In
{[end + 1] 'str.(STRING.clip text [begin - 1] [end + 1])}
| True
(Continue [i + 1] 'default)
}
| 'escaped
Cond {
| (Or [c = `\\`] [c = `"`] [c = `n`] [c = `r`] [c = `t`])
(Continue [i + 1] 'default)
| True
{i 'error."Invalid escape sequence in string literal."}
}
}
Define (scan_character text i)
Let begin i
In
Iterate {i mode} From {i 'start}
Let c (STRING.fetch text i)
In
If [c = `\n`]
{i 'error."Incomplete character literal."}
Match mode {
| 'start
If [c = `\\`]
(Continue [i + 1] 'escaped)
(Continue [i + 1] 'stop.c)
| 'escaped
Let num
Cond {
| [c = `\\`] `\\`
| [c = `n`] `\n`
| [c = `r`] `\r`
| [c = `t`] `\t`
| True `?`
}
In
If [num = `?`]
{i 'error."Invalid escape sequence in character literal."}
(Continue [i + 1] 'stop.num)
| 'stop.num
If [c = ```]
{[i + 1] 'num.num}
{i 'error."Invalid character literal."}
}
Define (scan_number text i)
Iterate {i num} From {i 0}
Let c (STRING.fetch text i)
In
If (ASCII.is_digit c)
(Continue [i + 1] [[num * 10] + [c - `0`]])
{i 'num.num}
Define (scan_word text i)
Let begin i
Let {end has_upper has_lower}
Iterate {i has_upper has_lower} From {i False False}
Let c (STRING.fetch text i)
In
Cond {
| (ASCII.is_upper c) (Continue [i + 1] True has_lower)
| (ASCII.is_lower c) (Continue [i + 1] has_upper True)
| (ASCII.is_digit c) (Continue [i + 1] has_upper has_lower)
| [c = `_`] (Continue [i + 1] has_upper has_lower)
| True {i has_upper has_lower}
}
In
Let word (STRING.clip text begin end)
In
If (And has_upper has_lower)
{end 'sym.word}
{end 'id.word}
Define (scan_operator text i)
Let begin i
Let end
Iterate i From [i + 1]
If (is_op_symbol (STRING.fetch text i))
(Continue [i + 1])
i
In
{end 'op.(STRING.clip text begin end)}
Define (scan_basic_symbol text i)
{[i + 1] 'sym.(STRING.clip text i [i + 1])}
Where
Define (skip_line text i)
Iterate i
If [(STRING.fetch text i) = `\n`]
[i + 1]
(Continue [i + 1])
Define (skip_spaces text i)
Iterate i
If [(STRING.fetch text i) = ` `]
(Continue [i + 1])
i
Define (is_op_symbol c)
(Or [c = `+`] [c = `-`] [c = `*`] [c = `/`] [c = `\\`] [c = `%`]
[c = `=`] [c = `<`] [c = `>`] [c = `!`] [c = `?`] [c = `^`] [c = `&`])
Where
Open Z
{
:Infix =
:Infix +
:Infix -
:Infix *
}
Open FUNC {:Infix <-}
Where
Let ASCII Package "ascii"
Let FUNC Package "func"
Let STDIO Package "stdio"
Let STRING Package "string"
Let Z Package "z"