1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-05-31 21:41:29 +00:00

Add symbolic constants.

This commit is contained in:
Chris Pressey 2018-03-27 12:36:33 +01:00
parent 330e61f327
commit 3f0e36a67c
5 changed files with 81 additions and 35 deletions

View File

@ -1,6 +1,12 @@
History of SixtyPical
=====================
0.15
----
* Symbolic constants can be defined with the `const` keyword, and can
be used in most places where literal values can be used.
0.14
----

View File

@ -81,7 +81,6 @@ is probably NP-complete. But doing it adequately is probably not that hard.
### And at some point...
* `low` and `high` address operators - to turn `word` type into `byte`.
* `const`s that can be used in defining the size of tables, etc.
* Tests, and implementation, ensuring a routine can be assigned to a vector of "wider" type
* Related: can we simply view a (small) part of a buffer as a byte table? If not, why not?
* Related: add constant to buffer to get new buffer. (Or to table, but... well, maybe.)

View File

@ -1,7 +1,7 @@
SixtyPical
==========
This document describes the SixtyPical programming language version 0.14,
This document describes the SixtyPical programming language version 0.15,
both its static semantics (the capabilities and limits of the static
analyses it defines) and its runtime semantics (with reference to the
semantics of 6502 machine code.)
@ -555,9 +555,10 @@ The block is always executed as least once.
Grammar
-------
Program ::= {TypeDefn} {Defn} {Routine}.
Program ::= {ConstDefn | TypeDefn} {Defn} {Routine}.
ConstDefn::= "const" Ident<new> Const.
TypeDefn::= "typedef" Type Ident<new>.
Defn ::= Type Ident<new> [Constraints] (":" Literal | "@" LitWord).
Defn ::= Type Ident<new> [Constraints] (":" Const | "@" LitWord).
Type ::= TypeTerm ["table" TypeSize].
TypeExpr::= "byte"
| "word"
@ -573,12 +574,13 @@ Grammar
| "routine" Ident<new> Constraints (Block | "@" LitWord)
.
LocExprs::= LocExpr {"," LocExpr}.
LocExpr ::= Register | Flag | Literal | Ident.
LocExpr ::= Register | Flag | Const | Ident.
Register::= "a" | "x" | "y".
Flag ::= "c" | "z" | "n" | "v".
Const ::= Literal | Ident<const>.
Literal ::= LitByte | LitWord | LitBit.
LitByte ::= "0" ... "255".
LitWord ::= "0" ... "65535".
LitWord ::= ["word"] "0" ... "65535".
LitBit ::= "on" | "off".
Block ::= "{" {Instr} "}".
Instr ::= "ld" LocExpr "," LocExpr ["+" LocExpr]
@ -598,6 +600,6 @@ Grammar
| "copy" LocExpr "," LocExpr ["+" LocExpr]
| "if" ["not"] LocExpr Block ["else" Block]
| "repeat" Block ("until" ["not"] LocExpr | "forever")
| "for" LocExpr ("up"|"down") "to" Literal Block
| "for" LocExpr ("up"|"down") "to" Const Block
| "with" "interrupts" LitBit Block
.

View File

@ -24,6 +24,7 @@ class Parser(object):
self.symbols = {} # token -> SymEntry
self.current_statics = {} # token -> SymEntry
self.typedefs = {} # token -> Type AST
self.consts = {} # token -> Loc
for token in ('a', 'x', 'y'):
self.symbols[token] = SymEntry(None, LocationRef(TYPE_BYTE, token))
for token in ('c', 'z', 'n', 'v'):
@ -51,8 +52,11 @@ class Parser(object):
def program(self):
defns = []
routines = []
while self.scanner.on('typedef'):
typedef = self.typedef()
while self.scanner.on('typedef', 'const'):
if self.scanner.on('typedef'):
self.typedef()
if self.scanner.on('const'):
self.defn_const()
typenames = ['byte', 'word', 'table', 'vector', 'buffer', 'pointer'] # 'routine',
typenames.extend(self.typedefs.keys())
while self.scanner.on(*typenames):
@ -110,6 +114,15 @@ class Parser(object):
self.typedefs[name] = type_
return type_
def defn_const(self):
self.scanner.expect('const')
name = self.defn_name()
if name in self.consts:
self.syntax_error('Const "%s" already declared' % name)
loc = self.const()
self.consts[name] = loc
return loc
def defn(self):
type_ = self.defn_type()
name = self.defn_name()
@ -118,10 +131,9 @@ class Parser(object):
if self.scanner.consume(':'):
if isinstance(type_, TableType) and self.scanner.on_type('string literal'):
initial = self.scanner.token
self.scanner.scan()
else:
self.scanner.check_type('integer literal')
initial = int(self.scanner.token)
self.scanner.scan()
initial = self.const().value
addr = None
if self.scanner.consume('@'):
@ -136,21 +148,31 @@ class Parser(object):
return Defn(self.scanner.line_number, name=name, addr=addr, initial=initial, location=location)
def literal_int(self):
self.scanner.check_type('integer literal')
c = int(self.scanner.token)
self.scanner.scan()
return c
def literal_int_const(self):
value = self.literal_int()
type_ = TYPE_WORD if value > 255 else TYPE_BYTE
loc = ConstantRef(type_, value)
return loc
def const(self):
if self.scanner.token in ('on', 'off'):
loc = ConstantRef(TYPE_BIT, 1 if self.scanner.token == 'on' else 0)
self.scanner.scan()
return loc
elif self.scanner.on_type('integer literal'):
value = int(self.scanner.token)
self.scanner.scan()
type_ = TYPE_WORD if value > 255 else TYPE_BYTE
loc = ConstantRef(type_, value)
return loc
elif self.scanner.consume('word'):
loc = ConstantRef(TYPE_WORD, int(self.scanner.token))
self.scanner.scan()
return loc
elif self.scanner.token in self.consts:
loc = self.consts[self.scanner.token]
self.scanner.scan()
return loc
else:
self.syntax_error('bad constant "%s"' % self.scanner.token)
def defn_size(self):
self.scanner.expect('[')
size = self.literal_int()
size = self.const().value
self.scanner.expect(']')
return size
@ -294,16 +316,8 @@ class Parser(object):
return accum
def locexpr(self, forward=False):
if self.scanner.token in ('on', 'off'):
loc = ConstantRef(TYPE_BIT, 1 if self.scanner.token == 'on' else 0)
self.scanner.scan()
return loc
elif self.scanner.on_type('integer literal'):
return self.literal_int_const()
elif self.scanner.consume('word'):
loc = ConstantRef(TYPE_WORD, int(self.scanner.token))
self.scanner.scan()
return loc
if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.consts or self.scanner.on_type('integer literal'):
return self.const()
elif forward:
name = self.scanner.token
self.scanner.scan()
@ -387,7 +401,7 @@ class Parser(object):
else:
self.syntax_error('expected "up" or "down", found "%s"' % self.scanner.token)
self.scanner.expect('to')
final = self.literal_int_const()
final = self.const()
block = self.block()
return For(self.scanner.line_number, dest=dest, direction=direction, final=final, block=block)
elif self.scanner.token in ("ld",):

View File

@ -228,6 +228,31 @@ Can't have two typedefs with the same name.
| }
? SyntaxError
Constants.
| const lives 3
| const days lives
| const w1 1000
| const w2 word 0
|
| typedef byte table[days] them
|
| byte lark: lives
|
| routine main {
| ld a, lives
| }
= ok
Can't have two constants with the same name.
| const w1 1000
| const w1 word 0
|
| routine main {
| }
? SyntaxError
Explicit memory address.
| byte screen @ 1024