mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-02-24 08:29:00 +00:00
Add symbolic constants.
This commit is contained in:
parent
330e61f327
commit
3f0e36a67c
@ -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
|
||||
----
|
||||
|
||||
|
@ -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.)
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
else:
|
||||
self.scanner.check_type('integer literal')
|
||||
initial = int(self.scanner.token)
|
||||
self.scanner.scan()
|
||||
else:
|
||||
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)
|
||||
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()
|
||||
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
|
||||
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",):
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user