diff --git a/README.md b/README.md index 89cd659..e65730b 100644 --- a/README.md +++ b/README.md @@ -145,14 +145,17 @@ include an external assembly file as-is at this position. lips will look for the included file in the directory of the file using the directive. +* `.ascii "some\ntext\0"` +writes a string using its characters' ASCII values. +a few escapes are currently supported: `\ " a b f n r t v 0` + +* `.asciiz "some\ntext"` +same as ascii, but with a null byte added to the end. + ### Unimplemented * FLOAT: writes a list of 32-bit floating point numbers until end-of-line. this may not get implemented due to a lack of aliasing in vanilla Lua, and thus accuracy issues. -* ASCII: writes a string using its characters' ASCII values. - -* ASCIIZ: same as ASCII, but with a null byte added to the end. - * INCBIN: write an external binary file as-is at this position. diff --git a/lips/Dumper.lua b/lips/Dumper.lua index faacf02..adb497c 100644 --- a/lips/Dumper.lua +++ b/lips/Dumper.lua @@ -1,5 +1,6 @@ -local insert = table.insert local floor = math.floor +local format = string.format +local insert = table.insert local data = require "lips.data" diff --git a/lips/Lexer.lua b/lips/Lexer.lua index 922f454..c5843c2 100644 --- a/lips/Lexer.lua +++ b/lips/Lexer.lua @@ -1,10 +1,25 @@ local byte = string.byte local char = string.char local find = string.find +local format = string.format +local insert = table.insert local open = io.open local data = require "lips.data" +local simple_escapes = { + ['0'] = 0x00, + ['\\'] = 0x5C, + ['"'] = 0x22, + ['a'] = 0x07, + ['b'] = 0x08, + ['f'] = 0x0C, + ['n'] = 0x0A, + ['r'] = 0x0D, + ['t'] = 0x09, + ['v'] = 0x0B, +} + local function readfile(fn) local f = open(fn, 'r') if not f then @@ -208,12 +223,48 @@ end function Lexer:lex_string(yield) -- TODO: support escaping if self.chr ~= '"' then - self:error("expected opening double quote") + self:error('expected opening double quote') + end + self:nextc() + + local bytes = {} + while true do + if self.chr == '\n' then + self:error('unimplemented') + self:nextc() + yield('EOL', '\n') + elseif self.ord == self.EOF then + self:nextc() + self:error('unexpected EOF; incomplete string') + elseif self.chr == '"' then + self:nextc() + break + elseif self.chr == '\\' then + self:nextc() + local simple = simple_escapes[self.chr] + if simple then + insert(bytes, simple) + else + self:error('unknown escape sequence') + end + self:nextc() + else + insert(bytes, byte(self.chr)) + self:nextc() + end + end + + yield('STRING', bytes) +end + +function Lexer:lex_string_naive(yield) -- no escape sequences + if self.chr ~= '"' then + self:error('expected opening double quote') end self:nextc() local buff = self:read_chars('[^"\n]') if self.chr ~= '"' then - self:error("expected closing double quote") + self:error('expected closing double quote') end self:nextc() yield('STRING', buff) @@ -222,7 +273,7 @@ end function Lexer:lex_include(_yield) self:read_chars('%s') local fn - self:lex_string(function(tt, tok) + self:lex_string_naive(function(tt, tok) fn = tok end) if self.options.path then @@ -293,6 +344,8 @@ function Lexer:lex(_yield) else yield('DIR', up) end + elseif self.chr == '"' then + self:lex_string(yield) elseif self.chr == '@' then self:nextc() local buff = self:read_chars('[%w_]') diff --git a/lips/Parser.lua b/lips/Parser.lua index f3e9bc3..528abc8 100644 --- a/lips/Parser.lua +++ b/lips/Parser.lua @@ -1,5 +1,5 @@ -local insert = table.insert local format = string.format +local insert = table.insert local data = require "lips.data" local overrides = require "lips.overrides" @@ -57,6 +57,15 @@ function Parser:number() return value end +function Parser:string() + if self.tt ~= 'STRING' then + self:error('expected string') + end + local value = self.tok + self:advance() + return value +end + function Parser:directive() local name = self.tok self:advance() @@ -84,13 +93,20 @@ function Parser:directive() self.dumper:add_directive(line, name, self:number()) end self:expect_EOL() - elseif name == 'HEX' then - self:error('unimplemented') elseif name == 'INC' then - -- noop + -- noop, handled by lexer + elseif name == 'ASCII' or name == 'ASCIIZ' then + local bytes = self:string() + for i, number in ipairs(bytes) do + self.dumper:add_directive(line, 'BYTE', number) + end + if name == 'ASCIIZ' then + self.dumper:add_directive(line, 'BYTE', 0) + end + self:expect_EOL() elseif name == 'INCBIN' then self:error('unimplemented') - elseif name == 'FLOAT' or name == 'ASCII' or name == 'ASCIIZ' then + elseif name == 'FLOAT' then self:error('unimplemented') else self:error('unknown directive')