将特殊字符写入方案中的输出端口(编译器设计)
writing special characters to output port in scheme (compiler design)
我有一个家庭作业要在 chez 方案中为 chez 方案(到 x64 程序集)创建一个编译器。
到目前为止,我的技术是创建一个巨大的字符串,然后将其显示在一个文件中。例如,假设我的输出汇编程序存储在变量 'str' 中——我将其写入文件的方法是:
(define out (open-output-port "file-name"))
(display str out)
我的字符串由 \n 分隔,它标志着每行的结尾。例如:最终 'str' 的一部分可能如下所示:
"mov rax, rdx\n
LoopStart:\n
cmp rdx,0\n"
等等...(汇编代码)
这是通过对方案中的每一行代码使用(多次)调用字符串追加来实现的。
它一直工作正常,直到我意识到我无法将 "special" 字符输出到程序集文件 - 例如 \n - 它被解释为最终文件中的新行,即使我希望能够在我的最终装配中产生这种字符组合。 \t、#\newline 和显示函数将 "consume".
的任何其他特殊字符也是如此
我看到还有另一个方案函数没有 "consume" 这些特殊字符 - write.
write 的问题在于,例如:
(write out "blablalba \n\n blabla")
将在文件 "out" 中生成以下文本: "blabla \n\n blabla"
(包括括号!!!)
我想找到一种能够在没有括号但也没有 "consuming" 特殊字符的情况下编写字符串的方法。
任何帮助将不胜感激,
谢谢,埃拉德
~~~~~~~~~~~~~编辑~~~~~~~~~~~~~~~~~~~~~
好的,首先感谢您的回复。
不过,我会尽量更准确地回答我的问题,
我想要的是能够编译这行代码,例如:
(char? "\n")
我的编译器应该模仿 Chez 方案(我没有使用球拍!)当然还有 Chez returns #f。
但是在我的编译器中,我首先扫描代码中的任何常量 - 我当然找到了这个字符串,但是由于显示功能,输出的代码将有一个换行符而不是字符串文字 '\n'.
因此,正如建议的那样,我认为我需要做的就是找到一种方法将字符串文字中的 '\' 字符替换为 '\\' (因为 NASM 语法实际上不应该包含这些特殊字符,除了字符串文字)。
有没有简单的方法来解决这个问题?我可以使用 string->list 然后搜索这些特殊情况(我认为大约有 5 - \n,\r,\t,现在想不起来了)
无论如何,我会尝试写它,然后我会post在这里。谢谢
~~~~~~~~~~~~第二次编辑~~~~~~~~~~~~~~~~~~~~~`
好的,现在我知道我需要做点别的了,
我需要能够转动这个字符串:
"abc\nabc\n\tabc"
对此:
"'abc',10,'abc',10,9,'abc'"
这是因为我确实在它前面有一个 "db" 前缀,这就是你应该如何在汇编中定义这个字符串文字。
如何在方案中执行此操作?
在我看来,您希望能够发出包含例如 \n 的字符串。例如,要创建一个包含字符串
的文本文件
abc\ndef
对吗?
如果是这样,那么答案很简单:只需 "display" 一个包含所需字符的字符串。事实上,我相信你对你的程序有点误解;挑战不在于让显示器做你想做的事,而是在于指定你想要输出的字符串。
让我们从代码开始:
#lang racket
(define str "abc\ndef")
(define out (open-output-file "/tmp/file-name"))
(display str out)
您应该会发现输出文件包含八个字符:一个 'a'、一个 'b'、一个 'c'、一个反斜杠、一个 'n'、一个 'd'、'e' 和 'f'.
这是(也许)令人惊讶的事情:文件中的字符串与 str 定义的字符串完全相同。查看此内容的一种方法是检查 string-length,或将字符串映射到列表:
(string-length str) ;; -> 8
(string->list str) ;; -> '(#\a #\b #\c #\ #\n #\d #\e #\f)
所以实际上,您真正的挑战与 'display' 没有任何关系,它只是将字符转储到端口;你的问题是在第一种情况下构建字符串,这可以通过多种不同的方式来完成。您可以使用引号字符,就像我在这个示例中所做的那样,或者您可以使用 string
函数,它将字符列表转换为字符串,或者您可以使用 "here-strings"... 或任何数字其他技术。
在您使用的 x86 Intel 语法中,您永远不需要文字 \
或 "
字符,这使您可以轻松地将其包装在 double-quoted 字符串中,而无需使用 \
转义在 asm.
中获得文字 \
事实上,YASM 甚至不支持具有 C-style 转义处理的字符文字,因此您只需要 \
作为具有文字 \
的字符常量的一部分。 NASM 确实支持 C-style 在反引号内转义,但无论哪种方式,您始终可以改用数字常量。
例如而不是
push `abc\n` ; NASM syntax for a numeric constant that will have the ASCII codes for a, b, c, \n in that order in memory
mov rsi, rsp ; buf
mov eax, 1 ; sys_write
mov edi, 1 ; stdout
mov edx, 4 ; length
syscall ; x86-64 Linux sys_write(1, "abc\n", 4)
...
您可以发出与
相同的 machine-code 字节
; YASM syntax, or NASM without any C-style escapes
push ('abc' <<8) | 0xa ; 0xa = ASCII code for newline
...
所以在这种情况下,将字符文字与数字文字混合使用是很尴尬的,而不是仅使用 push 0x0a636261
(x86 是 little-endian)。
作为 db
pseudo-instruction:
的参数要简单得多
lea rdi, [rel hello_string]
call printf ; puts would be more efficient, but for example purposes I don't want a function that adds a newline on its own.
...
section .rodata
hello_string: db 'Hello World!', 0xa, 0 ; null-terminated string
; instead of 'Hello World!\n[=12=]'
Multi-byte 带引号的字符串作为 db
的多个参数。我假设您在方案中的 double-quoted 字符串中使用的 Intel 语法风格与那些足够相似。
我上面所说的一切也适用于 GNU as
:您可以根据需要在 .ascii
之后使用 .byte
,或者使用立即数的数字常量。
这里有几件事需要澄清:
"\n"
是一个长度为 1 的字符串,其中包含换行符。 "\n"
是一个长度为 2 的字符串,其中包含一个反斜杠和一个 n。 display
不转换字符串中的任何内容,它只打印字符串中的内容。因此,如果您有一个包含反斜杠和 n 的字符串,display
将打印它。
当您有一个包含反斜杠和 n 的文件并将该文件读入字符串时,您将得到字符串 "\n"
,而不是字符串 "\n"
。如果文件包含一个实际的换行符,则后者是您将得到的。
因此,如果您有一个内容为 "abc\n"
的文件,并且您想将其转换为 db 'abc\n'
,则无需对 \n
执行任何操作。将文件读入字符串后,您将得到字符串 "\"abc\n\""
(其中包含一个引号、字母 abc、一个反斜杠、一个 n 和另一个引号)。然后将双引号替换为单引号,并在前面添加一个 db
,您将得到 "db 'abc\n'"
。你打印它,你会得到 db 'abc\n'
就是这样。
假设您的目标语法是 nasm,就是这样。 db 'abc\n'
是有效的 nasm 语法并且会做你想做的。 db 'abc', 10
也是有效的语法并且做同样的事情,但只要您的输入语法只包含 nasm 支持的转义序列,就不需要将它们转换为字节。
我有一个家庭作业要在 chez 方案中为 chez 方案(到 x64 程序集)创建一个编译器。
到目前为止,我的技术是创建一个巨大的字符串,然后将其显示在一个文件中。例如,假设我的输出汇编程序存储在变量 'str' 中——我将其写入文件的方法是:
(define out (open-output-port "file-name"))
(display str out)
我的字符串由 \n 分隔,它标志着每行的结尾。例如:最终 'str' 的一部分可能如下所示:
"mov rax, rdx\n
LoopStart:\n
cmp rdx,0\n"
等等...(汇编代码)
这是通过对方案中的每一行代码使用(多次)调用字符串追加来实现的。
它一直工作正常,直到我意识到我无法将 "special" 字符输出到程序集文件 - 例如 \n - 它被解释为最终文件中的新行,即使我希望能够在我的最终装配中产生这种字符组合。 \t、#\newline 和显示函数将 "consume".
的任何其他特殊字符也是如此
我看到还有另一个方案函数没有 "consume" 这些特殊字符 - write.
write 的问题在于,例如:
(write out "blablalba \n\n blabla")
将在文件 "out" 中生成以下文本: "blabla \n\n blabla"
(包括括号!!!)
我想找到一种能够在没有括号但也没有 "consuming" 特殊字符的情况下编写字符串的方法。
任何帮助将不胜感激,
谢谢,埃拉德
~~~~~~~~~~~~~编辑~~~~~~~~~~~~~~~~~~~~~
好的,首先感谢您的回复。
不过,我会尽量更准确地回答我的问题,
我想要的是能够编译这行代码,例如:
(char? "\n")
我的编译器应该模仿 Chez 方案(我没有使用球拍!)当然还有 Chez returns #f。
但是在我的编译器中,我首先扫描代码中的任何常量 - 我当然找到了这个字符串,但是由于显示功能,输出的代码将有一个换行符而不是字符串文字 '\n'.
因此,正如建议的那样,我认为我需要做的就是找到一种方法将字符串文字中的 '\' 字符替换为 '\\' (因为 NASM 语法实际上不应该包含这些特殊字符,除了字符串文字)。
有没有简单的方法来解决这个问题?我可以使用 string->list 然后搜索这些特殊情况(我认为大约有 5 - \n,\r,\t,现在想不起来了)
无论如何,我会尝试写它,然后我会post在这里。谢谢
~~~~~~~~~~~~第二次编辑~~~~~~~~~~~~~~~~~~~~~`
好的,现在我知道我需要做点别的了,
我需要能够转动这个字符串:
"abc\nabc\n\tabc"
对此:
"'abc',10,'abc',10,9,'abc'"
这是因为我确实在它前面有一个 "db" 前缀,这就是你应该如何在汇编中定义这个字符串文字。
如何在方案中执行此操作?
在我看来,您希望能够发出包含例如 \n 的字符串。例如,要创建一个包含字符串
的文本文件abc\ndef
对吗?
如果是这样,那么答案很简单:只需 "display" 一个包含所需字符的字符串。事实上,我相信你对你的程序有点误解;挑战不在于让显示器做你想做的事,而是在于指定你想要输出的字符串。
让我们从代码开始:
#lang racket
(define str "abc\ndef")
(define out (open-output-file "/tmp/file-name"))
(display str out)
您应该会发现输出文件包含八个字符:一个 'a'、一个 'b'、一个 'c'、一个反斜杠、一个 'n'、一个 'd'、'e' 和 'f'.
这是(也许)令人惊讶的事情:文件中的字符串与 str 定义的字符串完全相同。查看此内容的一种方法是检查 string-length,或将字符串映射到列表:
(string-length str) ;; -> 8
(string->list str) ;; -> '(#\a #\b #\c #\ #\n #\d #\e #\f)
所以实际上,您真正的挑战与 'display' 没有任何关系,它只是将字符转储到端口;你的问题是在第一种情况下构建字符串,这可以通过多种不同的方式来完成。您可以使用引号字符,就像我在这个示例中所做的那样,或者您可以使用 string
函数,它将字符列表转换为字符串,或者您可以使用 "here-strings"... 或任何数字其他技术。
在您使用的 x86 Intel 语法中,您永远不需要文字 \
或 "
字符,这使您可以轻松地将其包装在 double-quoted 字符串中,而无需使用 \
转义在 asm.
\
事实上,YASM 甚至不支持具有 C-style 转义处理的字符文字,因此您只需要 \
作为具有文字 \
的字符常量的一部分。 NASM 确实支持 C-style 在反引号内转义,但无论哪种方式,您始终可以改用数字常量。
例如而不是
push `abc\n` ; NASM syntax for a numeric constant that will have the ASCII codes for a, b, c, \n in that order in memory
mov rsi, rsp ; buf
mov eax, 1 ; sys_write
mov edi, 1 ; stdout
mov edx, 4 ; length
syscall ; x86-64 Linux sys_write(1, "abc\n", 4)
...
您可以发出与
相同的 machine-code 字节; YASM syntax, or NASM without any C-style escapes
push ('abc' <<8) | 0xa ; 0xa = ASCII code for newline
...
所以在这种情况下,将字符文字与数字文字混合使用是很尴尬的,而不是仅使用 push 0x0a636261
(x86 是 little-endian)。
作为 db
pseudo-instruction:
lea rdi, [rel hello_string]
call printf ; puts would be more efficient, but for example purposes I don't want a function that adds a newline on its own.
...
section .rodata
hello_string: db 'Hello World!', 0xa, 0 ; null-terminated string
; instead of 'Hello World!\n[=12=]'
Multi-byte 带引号的字符串作为 db
的多个参数。我假设您在方案中的 double-quoted 字符串中使用的 Intel 语法风格与那些足够相似。
我上面所说的一切也适用于 GNU as
:您可以根据需要在 .ascii
之后使用 .byte
,或者使用立即数的数字常量。
这里有几件事需要澄清:
"\n"
是一个长度为 1 的字符串,其中包含换行符。 "\n"
是一个长度为 2 的字符串,其中包含一个反斜杠和一个 n。 display
不转换字符串中的任何内容,它只打印字符串中的内容。因此,如果您有一个包含反斜杠和 n 的字符串,display
将打印它。
当您有一个包含反斜杠和 n 的文件并将该文件读入字符串时,您将得到字符串 "\n"
,而不是字符串 "\n"
。如果文件包含一个实际的换行符,则后者是您将得到的。
因此,如果您有一个内容为 "abc\n"
的文件,并且您想将其转换为 db 'abc\n'
,则无需对 \n
执行任何操作。将文件读入字符串后,您将得到字符串 "\"abc\n\""
(其中包含一个引号、字母 abc、一个反斜杠、一个 n 和另一个引号)。然后将双引号替换为单引号,并在前面添加一个 db
,您将得到 "db 'abc\n'"
。你打印它,你会得到 db 'abc\n'
就是这样。
假设您的目标语法是 nasm,就是这样。 db 'abc\n'
是有效的 nasm 语法并且会做你想做的。 db 'abc', 10
也是有效的语法并且做同样的事情,但只要您的输入语法只包含 nasm 支持的转义序列,就不需要将它们转换为字节。